From: Nick Holland Date: Wed, 7 Dec 2016 15:41:53 +0000 (+0000) Subject: C# Control factory / Registry X-Git-Tag: dali_1.2.22~2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=9b82688bddfb0f7799bdf73190032751360bd197 C# Control factory / Registry Allows controls to register themselves with DALi type registry along with their properties. Once registered this allows DALi to load a JSON file which can create C# controls and fill in properties for that control. See json-loader and data-picker-using-json Change-Id: I7f57722a5a7e30b7c85a732dc2077da7cf5809c7 --- diff --git a/plugins/dali-swig/.gitignore b/plugins/dali-swig/.gitignore index 43660d7..2263391 100644 --- a/plugins/dali-swig/.gitignore +++ b/plugins/dali-swig/.gitignore @@ -4,6 +4,5 @@ autom4te.cache/ aclocal.m4 config/ config.* -cpp/dali_wrap.cpp -cpp/dali_wrap.h +automatic/* configure diff --git a/plugins/dali-swig/Makefile.am b/plugins/dali-swig/Makefile.am index 991c0ee..e1520bf 100755 --- a/plugins/dali-swig/Makefile.am +++ b/plugins/dali-swig/Makefile.am @@ -39,14 +39,16 @@ manual/cpp/callbackbase_wrap.o: $(BUILT_SOURCES) g++ -c -fpic $(CXXFLAGS) $(DALICORE_CFLAGS) $(DALIADAPTOR_CFLAGS) $(DALITOOLKIT_CFLAGS) manual/cpp/callbackbase_wrap.cpp -o manual/cpp/callbackbase_wrap.o NDali.dll: $(BUILT_SOURCES) - $(MCS) -nologo -target:library -out:NDali.dll automatic/csharp/*.cs manual/csharp/*.cs + $(MCS) -nologo -target:library -out:NDali.dll automatic/csharp/*.cs manual/csharp/*.cs views/*.cs check-local: examples/dali-test.exe \ examples/hello-world.exe \ examples/scroll-view.exe \ examples/custom-control.exe \ - examples/spin-control.exe \ + examples/date-picker.exe \ examples/control-dashboard.exe \ + examples/date-picker-using-json.exe \ + examples/json-loader.exe \ examples/user-alphafunction.exe \ examples/image-view.exe \ examples/libNDalic.so examples/NDali.dll diff --git a/plugins/dali-swig/SWIG/dali-core.i b/plugins/dali-swig/SWIG/dali-core.i index 5535ee9..73ce8cc 100755 --- a/plugins/dali-swig/SWIG/dali-core.i +++ b/plugins/dali-swig/SWIG/dali-core.i @@ -84,6 +84,7 @@ %ignore *::AnchorPoint::DEFAULT; %ignore *::SetPositionInheritanceMode(PositionInheritanceMode); %ignore *::GetKeyValue(SizeType) const; +%ignore *::TypeInfo::GetCreator() const; %rename(ParentOriginTop) Dali::ParentOrigin::TOP; %rename(ParentOriginBottom) Dali::ParentOrigin::BOTTOM; @@ -209,6 +210,8 @@ typedef std::pair< Dali::Radian, Dali::Radian > AngleThresholdPair; %include %include %include +%include +%include %include %include diff --git a/plugins/dali-swig/SWIG/dali.i b/plugins/dali-swig/SWIG/dali.i index 1f5149b..e4da86b 100755 --- a/plugins/dali-swig/SWIG/dali.i +++ b/plugins/dali-swig/SWIG/dali.i @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include @@ -165,8 +167,67 @@ return $null; %} + + + +// Type registry type maps +%typemap(cstype) Dali::TypeInfo::CreateFunction "System.Delegate" +%typemap(csin, pre ="System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput); ") + Dali::TypeInfo::CreateFunction "new System.Runtime.InteropServices.HandleRef(null, ip)" // null was this + +%typemap(cstype) Dali::CSharpTypeInfo::CreateFunction "System.Delegate" +%typemap(csin, pre ="System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput); ") + Dali::CSharpTypeInfo::CreateFunction "new System.Runtime.InteropServices.HandleRef(null, ip)" // null was this + + + +%typemap(cstype) Dali::CSharpTypeInfo::SetPropertyFunction "System.Delegate" +%typemap(csin, pre ="System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput); ") + Dali::CSharpTypeInfo::SetPropertyFunction "new System.Runtime.InteropServices.HandleRef(null, ip)" // null was this + + + +%typemap(cstype) Dali::CSharpTypeInfo::GetPropertyFunction "System.Delegate" +%typemap(csin, pre ="System.IntPtr ip2 = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput); ") + Dali::CSharpTypeInfo::GetPropertyFunction "new System.Runtime.InteropServices.HandleRef(null, ip2)" // null was this + + + + + + + #endif + + +/** + * Extend the type registry to allow for registering of C# controls and custom properties + */ +%extend Dali::TypeRegistration { + + + static void RegisterControl( const std::string& controlName, Dali::CSharpTypeInfo::CreateFunction createFunc ) + { + Dali::CSharpTypeRegistry::RegisterType( controlName, typeid( Dali::Toolkit::Control), createFunc ); + }; + + + static void RegisterProperty( const std::string& controlName, + const std::string& propertyName, + int index, + Property::Type type, + Dali::CSharpTypeInfo::SetPropertyFunction setFunc, + Dali::CSharpTypeInfo::GetPropertyFunction getFunc ) + { + Dali::CSharpTypeRegistry::RegisterProperty( controlName, propertyName, index, type, setFunc, getFunc ); + }; + +}; + + + + %ignore operator<<; %ignore *::GetImplementation(); %ignore *::GetImplementation(Dali::BaseHandle&); @@ -219,7 +280,7 @@ using namespace Dali::Toolkit; %include alphafunction.i %include name-changed.i - +%include property-value.i %include dali-operator.i %include dali-core.i %include dali-adaptor.i diff --git a/plugins/dali-swig/SWIG/events/application-event.i b/plugins/dali-swig/SWIG/events/application-event.i index bc81dec..3db23f4 100644 --- a/plugins/dali-swig/SWIG/events/application-event.i +++ b/plugins/dali-swig/SWIG/events/application-event.i @@ -1018,6 +1018,9 @@ public static Application NewApplication(string stylesheet, Application.WINDOW_MODE windowMode) { + // register all Views with the type registry, so that can be created / styled via JSON + ViewRegistryHelper.Initialize(); + Application ret = New(1, stylesheet, windowMode); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); diff --git a/plugins/dali-swig/SWIG/events/builder-event.i b/plugins/dali-swig/SWIG/events/builder-event.i index db58063..268b7d7 100644 --- a/plugins/dali-swig/SWIG/events/builder-event.i +++ b/plugins/dali-swig/SWIG/events/builder-event.i @@ -80,6 +80,29 @@ } } + /// + public void LoadFromFile( string fileName ) + { + try + { + string json = System.IO.File.ReadAllText( fileName ); + if( json.Length > 0 ) + { + LoadFromString( json ); + } + else + { + throw new global::System.InvalidOperationException("Failed to load file " +fileName); + + } + } + catch ( System.Exception e) + { + throw new global::System.InvalidOperationException("Failed to parse " +fileName); + } + } + + %} %enddef diff --git a/plugins/dali-swig/SWIG/property-value.i b/plugins/dali-swig/SWIG/property-value.i new file mode 100644 index 0000000..290b4a1 --- /dev/null +++ b/plugins/dali-swig/SWIG/property-value.i @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#if defined(SWIGCSHARP) + + + %typemap(cscode) Dali::Property::Value %{ + + // Extension to property value class that allows us to create a + // PropertyValue from a C# object, e.g. int, float, string + static public Property.Value CreateFromObject( System.Object obj) + { + System.Type type = obj.GetType (); + + Property.Value value; + + if ( type.Equals (typeof(int)) ) + { + System.Console.WriteLine (" got an int property value "); + value = new Property.Value ((int) obj ); + } + if ( type.Equals (typeof(System.Int32)) ) + { + System.Console.WriteLine (" got an int property value "); + value = new Property.Value ((int) obj ); + } + else if ( type.Equals (typeof(bool)) ) + { + System.Console.WriteLine (" got an bool property value "); + value = new Property.Value ((bool) obj ); + } + else if ( type.Equals (typeof(float)) ) + { + System.Console.WriteLine (" got an float property value "); + value = new Property.Value ((float) obj ); + } + else if ( type.Equals (typeof(string)) ) + { + System.Console.WriteLine (" got a string property value "); + value = new Property.Value ((string) obj ); + } + else if ( type.Equals (typeof(Vector2)) ) + { + System.Console.WriteLine (" got an Vector2 property value "); + value = new Property.Value ((Vector2) obj ); + } + else if ( type.Equals (typeof(Vector3)) ) + { + System.Console.WriteLine (" got an Vector3 property value "); + value = new Property.Value ((Vector3) obj ); + } + else if ( type.Equals (typeof(Vector4)) ) + { + System.Console.WriteLine (" got an Vector4 property value "); + + value = new Property.Value ((Vector4) obj ); + } + else if ( type.Equals (typeof(Position)) ) + { + System.Console.WriteLine (" got an Position property value "); + value = new Property.Value ((Position) obj ); + } + else if ( type.Equals (typeof(Size)) ) + { + System.Console.WriteLine (" got an Size property value "); + value = new Property.Value ((Size) obj ); + } + else if ( type.Equals (typeof(Color)) ) + { + System.Console.WriteLine (" got an Color property value "); + value = new Property.Value ((Color) obj ); + } + else + { + throw new global::System.InvalidOperationException("Unimplemented type for Property Value"); + } + return value; + } + + + %} + + + + +#endif \ No newline at end of file diff --git a/plugins/dali-swig/examples/date-picker-using-json.cs b/plugins/dali-swig/examples/date-picker-using-json.cs new file mode 100644 index 0000000..dbc388c --- /dev/null +++ b/plugins/dali-swig/examples/date-picker-using-json.cs @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using System; +using System.Runtime.InteropServices; +using Dali; + +namespace MyCSharpExample +{ + // A spin control (for continously changing values when users can easily predict a set of values) + + class Example + { + private Dali.Application _application; + private Spin _spinYear; // spin control for year + private Spin _spinMonth; // spin control for month + private Spin _spinDay; // spin control for day + private Builder _builder; // DALi Builder + + public Example(Dali.Application application) + { + _application = application; + _application.Initialized += Initialize; + } + + public void Initialize(object source, NUIApplicationInitEventArgs e) + { + + Stage stage = Stage.GetCurrent(); + stage.BackgroundColor = Color.White; + + // load date JSON template... + + _builder = new Builder (); + + // Optional constant to see logging information coming out + // of DALi JSON parser (builder) + Property.Map constants = new Property.Map(); + constants.Insert( "CONFIG_SCRIPT_LOG_LEVEL", new Property.Value( "Verbose") ); + _builder.AddConstants( constants ); + + _builder.LoadFromFile( "./json/date-picker.json" ); + + // create the date-picker from the template in the json file + BaseHandle handle = _builder.Create( "date-picker"); + + Actor actorTree = Actor.DownCast( handle ); + + stage.Add( actorTree ); + + Actor year = actorTree.FindChildByName("Year"); + Actor month = actorTree.FindChildByName("Month" ); + Actor day = actorTree.FindChildByName("Day"); + + // need to get the actual C# View associated with the actor, + _spinYear = (Spin ) ViewRegistry.GetCustomViewFromActor( year ); + _spinMonth = (Spin ) ViewRegistry.GetCustomViewFromActor( month ); + _spinDay = (Spin ) ViewRegistry.GetCustomViewFromActor( day ); + + _spinYear.Value = 2099; + _spinMonth.Value = 5; + _spinDay.Value = 23; + + + _spinYear.SetKeyboardFocusable(true); + _spinMonth.SetKeyboardFocusable(true); + _spinDay.SetKeyboardFocusable(true); + + + FocusManager keyboardFocusManager = FocusManager.Instance; + keyboardFocusManager.PreFocusChange += OnKeyboardPreFocusChange; + keyboardFocusManager.FocusedActorEnterKeyPressed += OnFocusedActorEnterKeyPressed; + + } + + private Actor OnKeyboardPreFocusChange(object source, FocusManager.PreFocusChangeEventArgs e) + { + Actor nextFocusActor = e.Proposed; + + // When nothing has been focused initially, focus the text field in the first spin + if (!e.Current && !e.Proposed) + { + nextFocusActor = _spinYear.SpinText; + } + else if(e.Direction == View.KeyboardFocus.Direction.LEFT) + { + // Move the focus to the spin in the left of the current focused spin + if(e.Current == _spinMonth.SpinText) + { + nextFocusActor = _spinYear.SpinText; + } + else if(e.Current == _spinDay.SpinText) + { + nextFocusActor = _spinMonth.SpinText; + } + } + else if(e.Direction == View.KeyboardFocus.Direction.RIGHT) + { + // Move the focus to the spin in the right of the current focused spin + if(e.Current == _spinYear.SpinText) + { + nextFocusActor = _spinMonth.SpinText; + } + else if(e.Current == _spinMonth.SpinText) + { + nextFocusActor = _spinDay.SpinText; + } + } + + return nextFocusActor; + } + + private void OnFocusedActorEnterKeyPressed(object source, FocusManager.FocusedActorEnterKeyEventArgs e) + { + // Make the text field in the current focused spin to take the key input + KeyInputFocusManager manager = KeyInputFocusManager.Get(); + + if (e.Actor == _spinYear.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinYear.SpinText) + { + manager.SetFocus(_spinYear.SpinText); + } + } + else if (e.Actor == _spinMonth.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinMonth.SpinText) + { + manager.SetFocus(_spinMonth.SpinText); + } + } + else if (e.Actor == _spinDay.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinDay.SpinText) + { + manager.SetFocus(_spinDay.SpinText); + } + } + } + + public void MainLoop() + { + _application.MainLoop (); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + Example example = new Example(Application.NewApplication()); + example.MainLoop (); + } + } +} diff --git a/plugins/dali-swig/examples/date-picker.cs b/plugins/dali-swig/examples/date-picker.cs new file mode 100644 index 0000000..c9701cf --- /dev/null +++ b/plugins/dali-swig/examples/date-picker.cs @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using System; +using System.Runtime.InteropServices; +using Dali; + +namespace MyCSharpExample +{ + // A spin control (for continously changing values when users can easily predict a set of values) + + class Example + { + private Dali.Application _application; + private FlexContainer _container; // Flex container to hold spin controls + private Spin _spinYear; // spin control for year + private Spin _spinMonth; // spin control for month + private Spin _spinDay; // spin control for day + + public Example(Dali.Application application) + { + _application = application; + _application.Initialized += Initialize; + } + + public void Initialize(object source, NUIApplicationInitEventArgs e) + { + + Stage stage = Stage.GetCurrent(); + stage.BackgroundColor = Color.White; + + // Create a container for the spins + _container = new FlexContainer(); + + _container.ParentOrigin = NDalic.ParentOriginCenter; + _container.AnchorPoint = NDalic.AnchorPointCenter; + _container.FlexDirection = (int)FlexContainer.FlexDirectionType.ROW; + _container.Size = new Vector3(480.0f, 150.0f, 0.0f); + + stage.Add(_container); + + // Create a Spin control for year + _spinYear = new Spin(); + _spinYear.ParentOrigin = NDalic.ParentOriginCenter; + _spinYear.AnchorPoint = NDalic.AnchorPointCenter; + _spinYear.Flex = 0.3f; + _spinYear.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + _container.Add(_spinYear); + + _spinYear.MinValue = 1900; + _spinYear.MaxValue = 2100; + _spinYear.Value = 2016; + _spinYear.Step = 1; + _spinYear.MaxTextLength = 4; + _spinYear.TextPointSize = 26; + _spinYear.TextColor = Color.White; + _spinYear.SetKeyboardFocusable(true); + _spinYear.Name = "_spinYear"; + + // Create a Spin control for month + _spinMonth = new Spin(); + _spinMonth.ParentOrigin = NDalic.ParentOriginCenter; + _spinMonth.AnchorPoint = NDalic.AnchorPointCenter; + _spinMonth.Flex = 0.3f; + _spinMonth.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + _container.Add(_spinMonth); + + _spinMonth.MinValue = 1; + _spinMonth.MaxValue = 12; + _spinMonth.Value = 10; + _spinMonth.Step = 1; + _spinMonth.MaxTextLength = 2; + _spinMonth.TextPointSize = 26; + _spinMonth.TextColor = Color.White; + _spinMonth.SetKeyboardFocusable(true); + _spinMonth.Name = "_spinMonth"; + + // Create a Spin control for day + _spinDay = new Spin(); + _spinDay.ParentOrigin = NDalic.ParentOriginCenter; + _spinDay.AnchorPoint = NDalic.AnchorPointCenter; + _spinDay.Flex = 0.3f; + _spinDay.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + _container.Add(_spinDay); + + _spinDay.MinValue = 1; + _spinDay.MaxValue = 31; + _spinDay.Value = 26; + _spinDay.Step = 1; + _spinDay.MaxTextLength = 2; + _spinDay.TextPointSize = 26; + _spinDay.TextColor = Color.White; + _spinDay.SetKeyboardFocusable(true); + _spinDay.Name = "_spinDay"; + + FocusManager keyboardFocusManager = FocusManager.Instance; + keyboardFocusManager.PreFocusChange += OnKeyboardPreFocusChange; + keyboardFocusManager.FocusedActorEnterKeyPressed += OnFocusedActorEnterKeyPressed; + + } + + private Actor OnKeyboardPreFocusChange(object source, FocusManager.PreFocusChangeEventArgs e) + { + Actor nextFocusActor = e.Proposed; + + // When nothing has been focused initially, focus the text field in the first spin + if (!e.Current && !e.Proposed) + { + nextFocusActor = _spinYear.SpinText; + } + else if(e.Direction == View.KeyboardFocus.Direction.LEFT) + { + // Move the focus to the spin in the left of the current focused spin + if(e.Current == _spinMonth.SpinText) + { + nextFocusActor = _spinYear.SpinText; + } + else if(e.Current == _spinDay.SpinText) + { + nextFocusActor = _spinMonth.SpinText; + } + } + else if(e.Direction == View.KeyboardFocus.Direction.RIGHT) + { + // Move the focus to the spin in the right of the current focused spin + if(e.Current == _spinYear.SpinText) + { + nextFocusActor = _spinMonth.SpinText; + } + else if(e.Current == _spinMonth.SpinText) + { + nextFocusActor = _spinDay.SpinText; + } + } + + return nextFocusActor; + } + + private void OnFocusedActorEnterKeyPressed(object source, FocusManager.FocusedActorEnterKeyEventArgs e) + { + // Make the text field in the current focused spin to take the key input + KeyInputFocusManager manager = KeyInputFocusManager.Get(); + + if (e.Actor == _spinYear.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinYear.SpinText) + { + manager.SetFocus(_spinYear.SpinText); + } + } + else if (e.Actor == _spinMonth.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinMonth.SpinText) + { + manager.SetFocus(_spinMonth.SpinText); + } + } + else if (e.Actor == _spinDay.SpinText) + { + if (manager.GetCurrentFocusControl() != _spinDay.SpinText) + { + manager.SetFocus(_spinDay.SpinText); + } + } + } + + public void MainLoop() + { + _application.MainLoop (); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + Example example = new Example(Application.NewApplication()); + example.MainLoop (); + } + } +} diff --git a/plugins/dali-swig/examples/json-loader.cs b/plugins/dali-swig/examples/json-loader.cs new file mode 100644 index 0000000..388b077 --- /dev/null +++ b/plugins/dali-swig/examples/json-loader.cs @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using System; +using System.Runtime.InteropServices; +using Dali; + +namespace MyExampleApp +{ + class Example + { + private Dali.Application _application; + private Builder _builder; + private string _jsonFileName; + + public Example(Dali.Application application, string fileName) + { + _application = application; + _jsonFileName = fileName; + _application.Initialized += Initialize; + } + + public void Initialize(object source, NUIApplicationInitEventArgs e) + { + if( _jsonFileName.Length == 0) + { + Console.WriteLine("Please specify JSON file to load"); + return; + } + + _builder = new Builder (); + + Property.Map constants = new Property.Map(); + + // In dali-demo we have some JSON files that can be loaded, but they need 3 different macros defining. + // The JSON folder is typically installed into dali-env/opt/share/com.samsung.dali-demo/res: + // + //string demoDirectory = ".../dali-env/opt/share/com.samsung.dali-demo/res"; + //constants.Insert( "DEMO_IMAGE_DIR" , new Property.Value( demoDirectory+"/images") ); + //constants.Insert( "DEMO_MODEL_DIR" , new Property.Value( demoDirectory+"/models") ); + //constants.Insert( "DEMO_SCRIPT_DIR", new Property.Value( demoDirectory+"/scripts") ); + constants.Insert( "CONFIG_SCRIPT_LOG_LEVEL", new Property.Value( "Verbose") ); + + _builder.AddConstants( constants ); + + + Stage stage = Stage.GetCurrent(); + stage.BackgroundColor = Color.White; + + _builder.LoadFromFile( _jsonFileName ); + + _builder.AddActors( stage.GetRootLayer() ); + + } + + + public void MainLoop() + { + _application.MainLoop (); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + string fileName= ""; + + if( args.Length > 0) + { + fileName = args[0]; + } + + Console.WriteLine("arguments = " + args.Length); + Example example = new Example(Application.NewApplication(), fileName); + example.MainLoop (); + } + } +} diff --git a/plugins/dali-swig/examples/json/date-picker-template.json b/plugins/dali-swig/examples/json/date-picker-template.json new file mode 100644 index 0000000..dd01d8f --- /dev/null +++ b/plugins/dali-swig/examples/json/date-picker-template.json @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +{ + // Data picker loaded directly on to the stage + // + "templates": { + "date-picker": + { + "type":"FlexContainer", + "name":"exampleDatePicker", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "flexDirection":"ROW", + "size":[480.0, 150, 0 ], + "actors": [ + { + + "type": "Spin", + "name": "Year", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "Value":2017, + "MinValue":1900, + "MaxValue":2100, + "Step":1, + "TextColor":[0.0,0.0,1.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + }, + { + + "type": "Spin", + "name": "Month", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "parentOrigin": "CENTER", + "Value":10, + "Step":1, + "MinValue":1, + "MaxValue":12, + "TextColor":[1.0,1.0,1.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + + }, + { + + "type": "Spin", + "name": "Day", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "Value":1, + "MinValue":1, + "MaxValue":31, + "TextColor":[1.0,0.0,0.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + }] + + } +} + +} + diff --git a/plugins/dali-swig/examples/json/date-picker.json b/plugins/dali-swig/examples/json/date-picker.json new file mode 100644 index 0000000..3126aa3 --- /dev/null +++ b/plugins/dali-swig/examples/json/date-picker.json @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +{ + // Data picker loaded directly on to the stage + // + "stage": [{ + + "type":"FlexContainer", + "name":"exampleDatePicker", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "flexDirection":"ROW", + "size":[480.0, 150, 0 ], + "actors": [ + { + + "type": "Spin", + "name": "Year", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "MinValue":1900, + "MaxValue":2100, + "Value":2017, + "Step":1, + "TextColor":[0.0,0.0,1.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + }, + { + + "type": "Spin", + "name": "Month", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "parentOrigin": "CENTER", + "Step":1, + "MinValue":1, + "MaxValue":12, + "Value":10, + "TextColor":[1.0,1.0,1.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + + }, + { + + "type": "Spin", + "name": "Day", + "parentOrigin": "CENTER", + "anchorPoint": "CENTER", + "MinValue":1, + "MaxValue":31, + "Value":1, + "TextColor":[1.0,0.0,0.0,1.0], + "properties": { // properties registered dynamically + "flex":0.3, + "flexMargin": [5.0,0.0,5.0,0.0] + } + }] + +}] + +} + diff --git a/plugins/dali-swig/examples/json/spin.json b/plugins/dali-swig/examples/json/spin.json new file mode 100644 index 0000000..df9b10c --- /dev/null +++ b/plugins/dali-swig/examples/json/spin.json @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +{ + // a tree of actors + "stage": [ + // You can add an array of Actors / Views here + // Lets add a spin to the stage + { + "type":"Spin", + "parentOrigin":"CENTER", + "size":[120,120,0] + // now lets use the C# app json-loader.exe to load it + } + + ] +} diff --git a/plugins/dali-swig/examples/spin-control.cs b/plugins/dali-swig/examples/spin-control.cs deleted file mode 100755 index f385852..0000000 --- a/plugins/dali-swig/examples/spin-control.cs +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System; -using System.Runtime.InteropServices; -using Dali; - -namespace MyCSharpExample -{ - // A spin control (for continously changing values when users can easily predict a set of values) - class Spin : CustomView - { - private VisualBase _arrowVisual; - private TextField _textField; - private int _arrowVisualPropertyIndex; - private string _arrowImage; - private int _currentValue; - private int _minValue; - private int _maxValue; - private int _singleStep; - private bool _wrappingEnabled; - private string _fontFamily; - private string _fontStyle; - private int _pointSize; - private Color _textColor; - private Color _textBackgroundColor; - private int _maxTextLength; - - public Spin() : base(ViewWrapperImpl.CustomViewBehaviour.REQUIRES_KEYBOARD_NAVIGATION_SUPPORT | ViewWrapperImpl.CustomViewBehaviour.DISABLE_STYLE_CHANGE_SIGNALS) - { - } - - public override void OnInitialize() - { - // Initialize the properties - _arrowImage = "./images/arrow.png"; - _textBackgroundColor = new Color(0.6f, 0.6f, 0.6f, 1.0f); - _currentValue = 0; - _minValue = 0; - _maxValue = 0; - _singleStep = 1; - _maxTextLength = 0; - - // Create image visual for the arrow keys - _arrowVisualPropertyIndex = RegisterProperty("ArrowImage", new Dali.Property.Value(_arrowImage), Dali.Property.AccessMode.READ_WRITE); - _arrowVisual = VisualFactory.Get().CreateVisual( _arrowImage, new Uint16Pair(150, 150) ); - RegisterVisual( _arrowVisualPropertyIndex, _arrowVisual ); - - // Create a text field - _textField = new TextField(); - _textField.ParentOrigin = NDalic.ParentOriginCenter; - _textField.AnchorPoint = NDalic.AnchorPointCenter; - _textField.WidthResizePolicy = "SIZE_RELATIVE_TO_PARENT"; - _textField.HeightResizePolicy = "SIZE_RELATIVE_TO_PARENT"; - _textField.SizeModeFactor = new Vector3( 1.0f, 0.45f, 1.0f ); - _textField.PlaceholderText = "----"; - _textField.BackgroundColor = _textBackgroundColor; - _textField.HorizontalAlignment = "Center"; - _textField.VerticalAlignment = "Center"; - _textField.SetKeyboardFocusable(true); - _textField.Name = "_textField"; - - this.Add(_textField); - - _textField.KeyInputFocusGained += TextFieldKeyInputFocusGained; - _textField.KeyInputFocusLost += TextFieldKeyInputFocusLost; - } - - public override Vector3 GetNaturalSize() - { - return new Vector3(150.0f, 150.0f, 0.0f); - } - - public void TextFieldKeyInputFocusGained(object source, KeyInputFocusGainedEventArgs e) - { - // Make sure when the current spin that takes input focus also takes the keyboard focus - // For example, when you tap the spin directly - FocusManager.Instance.SetCurrentFocusActor(_textField); - } - - public void TextFieldKeyInputFocusLost(object source, KeyInputFocusLostEventArgs e) - { - int previousValue = _currentValue; - - // If the input value is invalid, change it back to the previous valid value - if(int.TryParse(_textField.Text, out _currentValue)) - { - if (_currentValue < _minValue || _currentValue > _maxValue) - { - _currentValue = previousValue; - } - } - else - { - _currentValue = previousValue; - } - - // Otherwise take the new value - this.Value = _currentValue; - } - - public override Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, View.KeyboardFocus.Direction direction, bool loopEnabled) - { - // Respond to Up/Down keys to change the value while keeping the current spin focused - Actor nextFocusedActor = currentFocusedActor; - if (direction == View.KeyboardFocus.Direction.UP) - { - this.Value += this.Step; - nextFocusedActor = _textField; - } - else if (direction == View.KeyboardFocus.Direction.DOWN) - { - this.Value -= this.Step; - nextFocusedActor = _textField; - } - else - { - // Return a native empty handle as nothing can be focused in the left or right - nextFocusedActor = new Actor(); - nextFocusedActor.Reset(); - } - - return nextFocusedActor; - } - - // Value property of type int: - public int Value - { - get - { - return _currentValue; - } - set - { - _currentValue = value; - - // Make sure no invalid value is accepted - if (_currentValue < _minValue) - { - _currentValue = _minValue; - } - - if (_currentValue > _maxValue) - { - _currentValue = _maxValue; - } - - _textField.Text = _currentValue.ToString(); - } - } - - // MinValue property of type int: - public int MinValue - { - get - { - return _minValue; - } - set - { - _minValue = value; - } - } - - // MaxValue property of type int: - public int MaxValue - { - get - { - return _maxValue; - } - set - { - _maxValue = value; - } - } - - // Step property of type int: - public int Step - { - get - { - return _singleStep; - } - set - { - _singleStep = value; - } - } - - // WrappingEnabled property of type bool: - public bool WrappingEnabled - { - get - { - return _wrappingEnabled; - } - set - { - _wrappingEnabled = value; - } - } - - // TextPointSize property of type int: - public int TextPointSize - { - get - { - return _pointSize; - } - set - { - _pointSize = value; - _textField.PointSize = _pointSize; - } - } - - // TextColor property of type Color: - public Color TextColor - { - get - { - return _textColor; - } - set - { - _textColor = value; - _textField.TextColor = _textColor; - } - } - - // MaxTextLength property of type int: - public int MaxTextLength - { - get - { - return _maxTextLength; - } - set - { - _maxTextLength = value; - _textField.MaxLength = _maxTextLength; - } - } - - public TextField SpinText - { - get - { - return _textField; - } - set - { - _textField = value; - } - } - - // Indicator property of type string: - public string IndicatorImage - { - get - { - return _arrowImage; - } - set - { - _arrowImage = value; - _arrowVisual = VisualFactory.Get().CreateVisual( _arrowImage, new Uint16Pair(150, 150) ); - RegisterVisual( _arrowVisualPropertyIndex, _arrowVisual ); - } - } - } - - class Example - { - private Dali.Application _application; - private FlexContainer _container; - private Spin _spinYear; - private Spin _spinMonth; - private Spin _spinDay; - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - delegate void CallbackDelegate(); - - public Example(Dali.Application application) - { - _application = application; - _application.Initialized += Initialize; - } - - public void Initialize(object source, NUIApplicationInitEventArgs e) - { - Stage stage = Stage.GetCurrent(); - stage.BackgroundColor = Color.White; - - // Create a container for the spins - _container = new FlexContainer(); - - _container.ParentOrigin = NDalic.ParentOriginCenter; - _container.AnchorPoint = NDalic.AnchorPointCenter; - _container.FlexDirection = (int)FlexContainer.FlexDirectionType.ROW; - _container.Size = new Vector3(480.0f, 150.0f, 0.0f); - - stage.Add(_container); - - // Create a Spin control for year - _spinYear = new Spin(); - _spinYear.ParentOrigin = NDalic.ParentOriginCenter; - _spinYear.AnchorPoint = NDalic.AnchorPointCenter; - _spinYear.Flex = 0.3f; - _spinYear.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); - _container.Add(_spinYear); - - _spinYear.MinValue = 1900; - _spinYear.MaxValue = 2100; - _spinYear.Value = 2016; - _spinYear.Step = 1; - _spinYear.MaxTextLength = 4; - _spinYear.TextPointSize = 26; - _spinYear.TextColor = Color.White; - _spinYear.SetKeyboardFocusable(true); - _spinYear.Name = "_spinYear"; - - // Create a Spin control for month - _spinMonth = new Spin(); - _spinMonth.ParentOrigin = NDalic.ParentOriginCenter; - _spinMonth.AnchorPoint = NDalic.AnchorPointCenter; - _spinMonth.Flex = 0.3f; - _spinMonth.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); - _container.Add(_spinMonth); - - _spinMonth.MinValue = 1; - _spinMonth.MaxValue = 12; - _spinMonth.Value = 10; - _spinMonth.Step = 1; - _spinMonth.MaxTextLength = 2; - _spinMonth.TextPointSize = 26; - _spinMonth.TextColor = Color.White; - _spinMonth.SetKeyboardFocusable(true); - _spinMonth.Name = "_spinMonth"; - - // Create a Spin control for day - _spinDay = new Spin(); - _spinDay.ParentOrigin = NDalic.ParentOriginCenter; - _spinDay.AnchorPoint = NDalic.AnchorPointCenter; - _spinDay.Flex = 0.3f; - _spinDay.FlexMargin = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); - _container.Add(_spinDay); - - _spinDay.MinValue = 1; - _spinDay.MaxValue = 31; - _spinDay.Value = 26; - _spinDay.Step = 1; - _spinDay.MaxTextLength = 2; - _spinDay.TextPointSize = 26; - _spinDay.TextColor = Color.White; - _spinDay.SetKeyboardFocusable(true); - _spinDay.Name = "_spinDay"; - - FocusManager keyboardFocusManager = FocusManager.Instance; - keyboardFocusManager.PreFocusChange += OnKeyboardPreFocusChange; - keyboardFocusManager.FocusedActorEnterKeyPressed += OnFocusedActorEnterKeyPressed; - - } - - private Actor OnKeyboardPreFocusChange(object source, FocusManager.PreFocusChangeEventArgs e) - { - Actor nextFocusActor = e.Proposed; - - // When nothing has been focused initially, focus the text field in the first spin - if (!e.Current && !e.Proposed) - { - nextFocusActor = _spinYear.SpinText; - } - else if(e.Direction == View.KeyboardFocus.Direction.LEFT) - { - // Move the focus to the spin in the left of the current focused spin - if(e.Current == _spinMonth.SpinText) - { - nextFocusActor = _spinYear.SpinText; - } - else if(e.Current == _spinDay.SpinText) - { - nextFocusActor = _spinMonth.SpinText; - } - } - else if(e.Direction == View.KeyboardFocus.Direction.RIGHT) - { - // Move the focus to the spin in the right of the current focused spin - if(e.Current == _spinYear.SpinText) - { - nextFocusActor = _spinMonth.SpinText; - } - else if(e.Current == _spinMonth.SpinText) - { - nextFocusActor = _spinDay.SpinText; - } - } - - return nextFocusActor; - } - - private void OnFocusedActorEnterKeyPressed(object source, FocusManager.FocusedActorEnterKeyEventArgs e) - { - // Make the text field in the current focused spin to take the key input - KeyInputFocusManager manager = KeyInputFocusManager.Get(); - - if (e.Actor == _spinYear.SpinText) - { - if (manager.GetCurrentFocusControl() != _spinYear.SpinText) - { - manager.SetFocus(_spinYear.SpinText); - } - } - else if (e.Actor == _spinMonth.SpinText) - { - if (manager.GetCurrentFocusControl() != _spinMonth.SpinText) - { - manager.SetFocus(_spinMonth.SpinText); - } - } - else if (e.Actor == _spinDay.SpinText) - { - if (manager.GetCurrentFocusControl() != _spinDay.SpinText) - { - manager.SetFocus(_spinDay.SpinText); - } - } - } - - public void MainLoop() - { - _application.MainLoop (); - } - - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main(string[] args) - { - Example example = new Example(Application.NewApplication()); - example.MainLoop (); - } - } -} diff --git a/plugins/dali-swig/manual/csharp/PropertyRangeManager.cs b/plugins/dali-swig/manual/csharp/PropertyRangeManager.cs new file mode 100644 index 0000000..7e6c4d4 --- /dev/null +++ b/plugins/dali-swig/manual/csharp/PropertyRangeManager.cs @@ -0,0 +1,138 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace Dali +{ + /// + /// Helper class for calculating what property indexes should be assigned to C# View (view) classes. + /// + public class PropertyRangeManager + { + private Dictionary _propertyRange; + + /// + /// Initializes a new instance of the class. + /// + public PropertyRangeManager () + { + _propertyRange = new Dictionary (); + } + + /// + /// Only called if a View has scriptable properties + /// + private PropertyRange RegisterView( string viewName, System.Type viewType ) + { + PropertyRange range; + + if ( _propertyRange.TryGetValue (viewName, out range) ) + { + // already registered + return range; + } + + // Find out the event and animatable start indexes for the type + range = new PropertyRange(); + + GetPropertyStartRange( viewType, ref range); + + // add it to our dictionary + _propertyRange.Add( viewName, range ); + + return range; + + } + + /// + /// Gets the index of the property. + /// Each property has to have unique index for this view type + /// + /// The property index. + /// View name + /// View type + /// Type. + public int GetPropertyIndex( string viewName, System.Type viewType, ScriptableProperty.ScriptableType type ) + { + + PropertyRange range; + + if (! _propertyRange.TryGetValue (viewName, out range) ) + { + // view not found, register it now + range = RegisterView( viewName, viewType); + } + + int index = range.GetNextFreePropertyIndex ( type ); + + // update the dictionary + _propertyRange[viewName]=range; + + return index; + + } + + /// + /// We calculate the start property indices, based on the type and it's class heirachy, e.g. DateView (70,000)- > Spin (60,000) -> View (50,000) + /// + private void GetPropertyStartRange( System.Type viewType, ref PropertyRange range ) + { + const int maxCountPerDerivation = 1000; // For child and animtable properties we use a gap of 1000 between each + // views property range in the heirachy + + // custom views start there property index, at view_PROPERTY_END_INDEX + // we add 1000, just incase View class (our C# custom view base) starts using scriptable properties + int startEventPropertyIndex = (int)View.PropertyRange.CONTROL_PROPERTY_END_INDEX+maxCountPerDerivation; + + // for animatable properties current range starts at ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX, + // we add 1000, just incase View class starts using animatable properties + int startAnimatablePropertyIndex = (int)Dali.PropertyRanges.ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX +maxCountPerDerivation; + + while ( viewType.BaseType.Name != "CustomView" ) // custom view is our C# view base class. we don't go any deeper. + { + // for every base class increase property start index + startEventPropertyIndex += (int)Dali.PropertyRanges.DEFAULT_PROPERTY_MAX_COUNT_PER_DERIVATION; // DALi uses 10,000 + startAnimatablePropertyIndex += maxCountPerDerivation; + + //Console.WriteLine ("getStartPropertyIndex = " + viewType.Name +"current index " + startEventPropertyIndex); + viewType = viewType.BaseType; + } + + range.startEventIndex = startEventPropertyIndex; + range.lastUsedEventIndex = startEventPropertyIndex; + + range.startAnimationIndex = startAnimatablePropertyIndex; + range.lastUsedAnimationIndex = startAnimatablePropertyIndex; + + } + + + public struct PropertyRange + { + + public int GetNextFreePropertyIndex( ScriptableProperty.ScriptableType type) + { + if ( type == ScriptableProperty.ScriptableType.Default ) + { + lastUsedEventIndex++; + return lastUsedEventIndex; + } + else + { + lastUsedAnimationIndex++; + return lastUsedAnimationIndex ; + } + } + + + public int startEventIndex; /// start of the property range + public int lastUsedEventIndex; /// last used of the property index + + public int startAnimationIndex; /// start of the property range + public int lastUsedAnimationIndex; /// last used of the property index + }; + + + +} +} diff --git a/plugins/dali-swig/manual/csharp/ViewRegistry.cs b/plugins/dali-swig/manual/csharp/ViewRegistry.cs new file mode 100644 index 0000000..29ece3a --- /dev/null +++ b/plugins/dali-swig/manual/csharp/ViewRegistry.cs @@ -0,0 +1,507 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace Dali +{ + /// + /// Add this attribute to any property belonging to a View (control) you want to be scriptable from JSON + /// + /// + /// Example: + /// + /// class MyView : public CustomView + /// { + /// [ScriptableProperty()] + /// public int MyProperty + /// { + /// get + /// { + /// return _myProperty; + /// } + /// set + /// { + /// _myProperty = value; + /// } + /// } + /// } + /// + /// Internally the following occurs for property registration ( this only occurs once per Type, not per Instance): + /// + /// - The controls static constructor should call ViewRegistry.Register() (only called once for the lifecycle of the app) + /// - Within Register() the code will introspect the Controls properties, looking for the ScriptableProperty() attribute + /// - For every property with the ScriptableProperty() attribute, TypeRegistration.RegisterProperty is called. + /// - TypeRegistration.RegisterProperty calls in to DALi C++ Code Dali::CSharpTypeRegistry::RegisterProperty() + /// - 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). + /// + /// The DALi C# example + /// + /// class MyView : public CustomView + /// { + /// + /// [ScriptableProperty()] + /// public double Hours + /// { + /// get { return seconds / 3600; } + /// set { seconds = value * 3600; } + /// } + /// } + /// + /// Equivalent code in DALi C++: + /// in MyControl.h + /// class MyControl : public Control + /// { + /// struct Property + /// { + /// enum + /// { + /// HOURS = Control::CONTROL_PROPERTY_END_INDEX + 1 + /// } + /// } + /// + /// + /// in MyControl-impl.cpp + /// + /// DALI_TYPE_REGISTRATION_BEGIN( Toolkit::MyControl, Toolkit::Control, Create ); + /// DALI_PROPERTY_REGISTRATION( Toolkit, MyControl, "Hours", INTEGER, DISABLED ) + /// DALI_TYPE_REGISTRATION_END() + /// + /// + /// + public class ScriptableProperty : System.Attribute + { + public enum ScriptableType + { + Default, // Read Writable, non-animatable property, event thread only + // Animatable // Animatable property, Currently disabled, UK + } + public readonly ScriptableType type; + + public ScriptableProperty(ScriptableType type = ScriptableType.Default ) + { + this.type = type; + } + } + + /// + /// View Registry singleton. + /// Used for registering controls and any scriptable properties they have ( see ScriptableProperty ) + /// + /// Internal Design from C# to C++ + /// + /// - Each custom C# view should have it's static constructor called before any JSON file is loaded. + /// Static constructors for a class will only run once ( they are run per control type, not per instance). + /// Example of running a static constructor: + /// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Spin).TypeHandle); + /// Inside the static constructor the control should register it's type with the ViewRegistry + /// e.g. + /// + /// static Spin() + /// { + /// ViewRegistry.Instance.RegisterControl("Spin", CreateInstance, typeof(Spin) ); + /// } + /// + /// The control should also provide a CreateInstance function, which gets passed to the ViewRegistry + /// // Eventually it will be called if DALi Builderfinds a Spin control in a JSON file + /// static CustomView CreateInstance() + /// { + /// return new Spin(); + /// } + /// + /// + /// + /// The DALi C++ equivalent of this is + /// + /// TypeRegistration mType( typeid(Toolkit::Spin), typeid(Toolkit::Control), CreateInstance ); + /// + /// + /// + /// + public sealed class ViewRegistry + { + /// + /// ViewRegistry is a singleton + /// + private static ViewRegistry instance = null; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + delegate IntPtr CreateControlDelegate( IntPtr cPtrControlName ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + delegate IntPtr GetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + delegate void SetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue ); + + private CreateControlDelegate _createCallback; + private SetPropertyDelegate _setPropertyCallback; + private GetPropertyDelegate _getPropertyCallback; + private PropertyRangeManager _propertyRangeManager; + + /// + /// Given a C++ custom control the dictionary allows us to find what CustomView it belongs to + /// + private Dictionary _controlMap; + + /// + // Maps the name of a custom view to a create instance function + /// E.g. given a string "Spin", we can get a function used to create the Spin View. + /// + private Dictionary > _constructorMap; + + /// + /// Lookup table to match C# types to DALi types, used for the automatic property registration + /// + private static readonly Dictionary _daliPropertyTypeLookup + = new Dictionary< string, Dali.Property.Type > + { + { "float", Property.Type.FLOAT }, + { "int", Property.Type.INTEGER }, + { "Int32", Property.Type.INTEGER }, + { "Boolean", Property.Type.BOOLEAN }, + { "string", Property.Type.STRING }, + { "Vector2", Property.Type.VECTOR2 }, + { "Vector3", Property.Type.VECTOR3 }, + { "Vector4", Property.Type.VECTOR4 }, + { "Size", Property.Type.VECTOR2 }, + { "Position",Property.Type.VECTOR3 }, + { "Color", Property.Type.VECTOR4 }, + // { "Matrix3", Property.Type.MATRIX3 }, commented out until we need to use Matrices from JSON + // { "Matrix", Property.Type.MATRIX }, + }; + + + public ViewRegistry() + { + _createCallback = new CreateControlDelegate( CreateControl ); + _getPropertyCallback = new GetPropertyDelegate (GetProperty); + _setPropertyCallback = new SetPropertyDelegate (SetProperty); + + _controlMap = new Dictionary(); + _constructorMap = new Dictionary>(); + _propertyRangeManager = new PropertyRangeManager(); + + } + + private Dali.Property.Type GetDaliPropertyType( string cSharpTypeName ) + { + Dali.Property.Type daliType; + if ( _daliPropertyTypeLookup.TryGetValue (cSharpTypeName, out daliType) ) + { + //Console.WriteLine("mapped "+ cSharpTypeName + " to dAli type " +daliType ); + return daliType; + } + else + { + // Console.WriteLine("Failed to find a mapping between C# property" + cSharpTypeName +" and DALi type"); + return Property.Type.NONE; + } + } + + /// + /// Called directly from DALi C++ type registry to create a control (View) uses no marshalling. + /// + /// Pointer to the Control (Views) handle + /// C pointer to the Control (View) name + private static IntPtr CreateControl( IntPtr cPtrControlName ) + { + string controlName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (cPtrControlName); + // Console.WriteLine ("Create controlled called from C++ create a " + controlName); + + Func< CustomView > controlConstructor; + + // find the control constructor + if ( Instance._constructorMap.TryGetValue (controlName, out controlConstructor) ) + { + // Create the control + CustomView newControl = controlConstructor (); + + // Store the mapping between this instance of the custom control and native part + // We store a pointer to the RefObject for the control + IntPtr cPtr = newControl.GetPtrfromActor(); + RefObject refObj = newControl.GetObjectPtr (); + IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj); + + //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr); + Instance._controlMap.Add (refCptr , newControl ); + + return cPtr; // return pointer to handle + } + else + { + throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName ); + return IntPtr.Zero; + } + } + + private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName ) + { + string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName); + return Instance.GetPropertyValue ( controlPtr, name); + } + + private static void SetProperty( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue ) + { + string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName); + //Console.WriteLine ( SetControlProperty called for:" + name ); + Instance.SetPropertyValue ( controlPtr, name, propertyValue); + + } + + public static ViewRegistry Instance + { + get + { + if (instance==null) + { + instance = new ViewRegistry(); + } + return instance; + } + } + + public static CustomView GetCustomViewFromActor( Actor actor ) + { + // we store a dictionary of ref-obects (C++ land) to custom views (C# land) + Dali.CustomView view; + + RefObject refObj = actor.GetObjectPtr (); + IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj); + + if ( Instance._controlMap.TryGetValue ( refObjectPtr, out view) ) + { + + // call the get property function + + return view; + } + else + { + return null; + } + } + + + /// + /// Function which registers a view and all it's scriptable properties with DALi's type registry. + /// Means the View can be created / configured from a JSON script. + /// + /// The function uses introspection to scan a Views C# properties, then selects the ones with + ///[ScriptableProperty] attribute to be registered. + /// Example of a Spin view registering itself + /// static Spin() + /// { + /// ViewRegistry registers control type with DALi type registery + /// also uses introspection to find any properties that need to be registered with type registry + /// ViewRegistry.Instance.Register("Spin", CreateInstance, typeof(Spin) ); + /// } + /// + /// + public void Register(string viewName, Func< CustomView > createFunction, System.Type viewType ) + { + // add the mapping between the view name and it's create function + _constructorMap.Add (viewName, createFunction); + + // Call into DALi C++ to register the control with the type registry + TypeRegistration.RegisterControl( viewName, _createCallback ); + + // Cycle through each property in the class + foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties()) + { + + if ( propertyInfo.CanRead ) + { + + System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo); + foreach (System.Attribute attr in attrs) + { + // If the Scriptable attribute exists, then register it with the type registry. + if (attr is ScriptableProperty) + { + //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name); + + // first get the attribute type, ( default, or animatable) + ScriptableProperty scriptableProp = attr as ScriptableProperty; + + // 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) + int propertyIndex = _propertyRangeManager.GetPropertyIndex( viewName, viewType, scriptableProp.type ); + + // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String + Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name ); + + // Example RegisterProperty("spin","maxValue", 50001, FLOAT, set, get ); + // Native call to register the property + TypeRegistration.RegisterProperty (viewName, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback); + } + } + // Console.WriteLine ("property name = " + propertyInfo.Name); + } + } + } + + /// + /// Get a property value from a View + /// + /// + private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName) + { + // Get the C# control that maps to the C++ control + Dali.CustomView view; + + BaseHandle baseHandle = new BaseHandle (controlPtr, false); + + RefObject refObj = baseHandle.GetObjectPtr (); + + IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj); + + if ( _controlMap.TryGetValue ( refObjectPtr, out view) ) + { + + // call the get property function + System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null); + + Property.Value value = Property.Value.CreateFromObject (val); + + return (IntPtr)Property.Value.getCPtr (value); + } + else + { + return IntPtr.Zero; + } + } + + /// + /// Set a property value on a View + /// + /// + private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr) + { + // Get the C# control that maps to the C++ control + Dali.CustomView view; + + //Console.WriteLine ("SetPropertyValue refObjectPtr = {0:X}", controlPtr); + + Property.Value propValue = new Property.Value (propertyValuePtr, false); + + if ( _controlMap.TryGetValue ( controlPtr, out view) ) + { + + System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName); + + // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type + System.Type type = propertyInfo.PropertyType; + bool ok = false; + + if ( type.Equals (typeof(Int32)) ) + { + int value = 0; + ok = propValue.Get( ref value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(bool)) ) + { + bool value = false; + ok = propValue.Get( ref value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(float)) ) + { + float value = 0; + ok = propValue.Get( ref value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(string)) ) + { + string value = ""; + ok = propValue.Get( out value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(Vector2)) ) + { + Vector2 value = new Vector2 (); + ok = propValue.Get( value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(Vector3)) ) + { + Vector3 value = new Vector3 (); + ok = propValue.Get( value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(Vector4)) ) + { + Vector4 value = new Vector4 (); + ok = propValue.Get( value ); + + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(Position)) ) + { + Position value = new Position (); + ok = propValue.Get( value ); + if ( ok ) + { + propertyInfo.SetValue (view, value); + } + } + else if ( type.Equals (typeof(Size)) ) + { + // DALi sizes are Vector3 + Vector3 value = new Vector3(); + ok = propValue.Get( value ); + if ( ok ) + { + propertyInfo.SetValue(view, new Size(value.X,value.Y)); + }; + } + else if ( type.Equals (typeof(Color)) ) + { + // Colors are stored as Vector4's in DALi + Vector4 value = new Vector4(); + ok = propValue.Get( value ); + if ( ok ) + { + propertyInfo.SetValue (view, (Color)value); + }; + } + else + { + throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value"); + } + if ( !ok ) + { + throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed"); + } + } + else + { + throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr); + } + + } + + } + + +} \ No newline at end of file diff --git a/plugins/dali-swig/manual/csharp/ViewRegistryHelper.cs b/plugins/dali-swig/manual/csharp/ViewRegistryHelper.cs new file mode 100644 index 0000000..8e9e19a --- /dev/null +++ b/plugins/dali-swig/manual/csharp/ViewRegistryHelper.cs @@ -0,0 +1,15 @@ +using System; + +// include all custom views here which will be +namespace Dali +{ + public class ViewRegistryHelper + { + static public void Initialize() + { + // Register all views with the type registry + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Dali.Spin).TypeHandle); + } + } +} + diff --git a/plugins/dali-swig/views/spin.cs b/plugins/dali-swig/views/spin.cs new file mode 100644 index 0000000..06a0210 --- /dev/null +++ b/plugins/dali-swig/views/spin.cs @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using System; +using System.Runtime.InteropServices; +using Dali; + +// A spin control (for continously changing values when users can easily predict a set of values) + +namespace Dali +{ +public class Spin : CustomView + { + private VisualBase _arrowVisual; + private TextField _textField; + private int _arrowVisualPropertyIndex; + private string _arrowImage; + private int _currentValue; + private int _minValue; + private int _maxValue; + private int _singleStep; + private bool _wrappingEnabled; + private string _fontFamily; + private string _fontStyle; + private int _pointSize; + private Color _textColor; + private Color _textBackgroundColor; + private int _maxTextLength; + + // Called by DALi Builder if it finds a Spin control in a JSON file + static CustomView CreateInstance() + { + return new Spin(); + } + + // static constructor registers the control type (only runs once) + static Spin() + { + // ViewRegistry registers control type with DALi type registery + // also uses introspection to find any properties that need to be registered with type registry + ViewRegistry.Instance.Register("Spin", CreateInstance, typeof(Spin) ); + } + public Spin() : base(ViewWrapperImpl.CustomViewBehaviour.REQUIRES_KEYBOARD_NAVIGATION_SUPPORT | ViewWrapperImpl.CustomViewBehaviour.DISABLE_STYLE_CHANGE_SIGNALS) + { + + } + + public override void OnInitialize() + { + // Initialize the propertiesControl + _arrowImage = "./images/arrow.png"; + _textBackgroundColor = new Color(0.6f, 0.6f, 0.6f, 1.0f); + _currentValue = 0; + _minValue = 0; + _maxValue = 0; + _singleStep = 1; + _maxTextLength = 0; + + // Create image visual for the arrow keys + _arrowVisualPropertyIndex = RegisterProperty("ArrowImage", new Dali.Property.Value(_arrowImage), Dali.Property.AccessMode.READ_WRITE); + _arrowVisual = VisualFactory.Get().CreateVisual( _arrowImage, new Uint16Pair(150, 150) ); + RegisterVisual( _arrowVisualPropertyIndex, _arrowVisual ); + + // Create a text field + _textField = new TextField(); + _textField.ParentOrigin = NDalic.ParentOriginCenter; + _textField.AnchorPoint = NDalic.AnchorPointCenter; + _textField.WidthResizePolicy = "SIZE_RELATIVE_TO_PARENT"; + _textField.HeightResizePolicy = "SIZE_RELATIVE_TO_PARENT"; + _textField.SizeModeFactor = new Vector3( 1.0f, 0.45f, 1.0f ); + _textField.PlaceholderText = "----"; + _textField.BackgroundColor = _textBackgroundColor; + _textField.HorizontalAlignment = "Center"; + _textField.VerticalAlignment = "Center"; + _textField.SetKeyboardFocusable(true); + _textField.Name = "_textField"; + + this.Add(_textField); + + _textField.KeyInputFocusGained += TextFieldKeyInputFocusGained; + _textField.KeyInputFocusLost += TextFieldKeyInputFocusLost; + } + + public override Vector3 GetNaturalSize() + { + return new Vector3(150.0f, 150.0f, 0.0f); + } + + public void TextFieldKeyInputFocusGained(object source, KeyInputFocusGainedEventArgs e) + { + // Make sure when the current spin that takes input focus also takes the keyboard focus + // For example, when you tap the spin directly + FocusManager.Instance.SetCurrentFocusActor(_textField); + } + + public void TextFieldKeyInputFocusLost(object source, KeyInputFocusLostEventArgs e) + { + int previousValue = _currentValue; + + // If the input value is invalid, change it back to the previous valid value + if(int.TryParse(_textField.Text, out _currentValue)) + { + if (_currentValue < _minValue || _currentValue > _maxValue) + { + _currentValue = previousValue; + } + } + else + { + _currentValue = previousValue; + } + + // Otherwise take the new value + this.Value = _currentValue; + } + + public override Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, View.KeyboardFocus.Direction direction, bool loopEnabled) + { + // Respond to Up/Down keys to change the value while keeping the current spin focused + Actor nextFocusedActor = currentFocusedActor; + if (direction == View.KeyboardFocus.Direction.UP) + { + this.Value += this.Step; + nextFocusedActor = _textField; + } + else if (direction == View.KeyboardFocus.Direction.DOWN) + { + this.Value -= this.Step; + nextFocusedActor = _textField; + } + else + { + // Return a native empty handle as nothing can be focused in the left or right + nextFocusedActor = new Actor(); + nextFocusedActor.Reset(); + } + + return nextFocusedActor; + } + + + [ScriptableProperty()] + public int Value + { + get + { + return _currentValue; + } + set + { + + Console.WriteLine ("Value set to " + value ); + _currentValue = value; + + // Make sure no invalid value is accepted + if (_currentValue < _minValue) + { + _currentValue = _minValue; + } + + if (_currentValue > _maxValue) + { + _currentValue = _maxValue; + } + + _textField.Text = _currentValue.ToString(); + } + } + // MinValue property of type int: + [ScriptableProperty()] + public int MinValue + { + get + { + return _minValue; + } + set + { + _minValue = value; + } + } + + // MaxValue property of type int: + [ScriptableProperty()] + public int MaxValue + { + get + { + return _maxValue; + } + set + { + _maxValue = value; + } + } + + // Step property of type int: + [ScriptableProperty()] + public int Step + { + get + { + return _singleStep; + } + set + { + _singleStep = value; + } + } + + // WrappingEnabled property of type bool: + [ScriptableProperty()] + public bool WrappingEnabled + { + get + { + return _wrappingEnabled; + } + set + { + _wrappingEnabled = value; + } + } + + // TextPointSize property of type int: + [ScriptableProperty()] + public int TextPointSize + { + get + { + return _pointSize; + } + set + { + _pointSize = value; + _textField.PointSize = _pointSize; + } + } + + // TextColor property of type Color: + [ScriptableProperty()] + public Color TextColor + { + get + { + return _textColor; + } + set + { + Console.WriteLine ("TextColor set to " + value.R + "," + value.G + ","+ value.B); + + _textColor = value; + _textField.TextColor = _textColor; + } + } + + // MaxTextLength property of type int: + [ScriptableProperty()] + public int MaxTextLength + { + get + { + return _maxTextLength; + } + set + { + _maxTextLength = value; + _textField.MaxLength = _maxTextLength; + } + } + + public TextField SpinText + { + get + { + return _textField; + } + set + { + _textField = value; + } + } + + // Indicator property of type string: + public string IndicatorImage + { + get + { + return _arrowImage; + } + set + { + _arrowImage = value; + _arrowVisual = VisualFactory.Get().CreateVisual( _arrowImage, new Uint16Pair(150, 150) ); + RegisterVisual( _arrowVisualPropertyIndex, _arrowVisual ); + } + } +} +} \ No newline at end of file