[NUI] Support sub node's Animation
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Common / PropertyHelper.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.Diagnostics;
21 using System.Text;
22 using Tizen.NUI.BaseComponents;
23
24 namespace Tizen.NUI
25 {
26     using OOConverter = Converter<object, object>;
27     using PPConverter = Converter<PropertyValue, PropertyValue>;
28
29     internal static class PropertyHelper
30     {
31         private static readonly Dictionary<string, VisualPropertyData> visualPropertyTable = new Dictionary<string, VisualPropertyData>()
32         {
33             { "backgroundColor",        new VisualPropertyData(View.Property.BACKGROUND, ColorVisualProperty.MixColor, ObjectColorToVector3, PropertyValueColorToVector3,
34                                         new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.Opacity, ObjectColorToAlpha, PropertyValueColorToAlpha)) },
35             { "backgroundOpacity",      new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.Opacity, ObjectIntToFloat) },
36             { "boxShadow.BlurRadius",   new VisualPropertyData(View.Property.SHADOW, ColorVisualProperty.BlurRadius) },
37             { "boxShadow.Color",        new VisualPropertyData(View.Property.SHADOW, ColorVisualProperty.MixColor, ObjectColorToVector3, PropertyValueColorToVector3,
38                                         new VisualPropertyData(View.Property.SHADOW, Visual.Property.Opacity, ObjectColorToAlpha, PropertyValueColorToAlpha)) },
39             { "boxShadow.CornerRadius", new VisualPropertyData(View.Property.SHADOW, Visual.Property.CornerRadius, ObjectIntToFloat) },
40             { "boxShadow.Offset",       new VisualPropertyData(View.Property.SHADOW, (int)VisualTransformPropertyType.Offset) },
41             { "boxShadow.Opacity",      new VisualPropertyData(View.Property.SHADOW, Visual.Property.Opacity, ObjectIntToFloat) },
42             { "cornerRadius",           new VisualPropertyData(ImageView.Property.IMAGE, Visual.Property.CornerRadius, null, null,
43                                         new VisualPropertyData(View.Property.SHADOW, Visual.Property.CornerRadius, null, null,
44                                         new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.CornerRadius, null, null))) },
45             { "borderlineWidth",        new VisualPropertyData(ImageView.Property.IMAGE, Visual.Property.BorderlineWidth, ObjectIntToFloat, null,
46                                         new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.BorderlineWidth, ObjectIntToFloat, null)) },
47             { "borderlineColor",        new VisualPropertyData(ImageView.Property.IMAGE, Visual.Property.BorderlineColor, null, null,
48                                         new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.BorderlineColor, null, null)) },
49             { "borderlineOffset",       new VisualPropertyData(ImageView.Property.IMAGE, Visual.Property.BorderlineOffset, null, null,
50                                         new VisualPropertyData(View.Property.BACKGROUND, Visual.Property.BorderlineOffset, null, null)) },
51             { "imageShadow.Offset",     new VisualPropertyData(View.Property.SHADOW, (int)VisualTransformPropertyType.Offset) },
52             { "shadow.CornerRadius",    new VisualPropertyData(View.Property.SHADOW, Visual.Property.CornerRadius, ObjectIntToFloat) },
53         };
54
55         static PropertyHelper() { }
56
57         ///<summary>
58         /// Returns a Property if stringProperty is a valid index
59         ///</summary>
60         internal static Property GetPropertyFromString(Animatable handle, string stringProperty)
61         {
62             Property property = new Property(handle, LowerFirstLetter(stringProperty));
63             if (property.propertyIndex == Property.InvalidIndex)
64             {
65                 throw new System.ArgumentException("string property is invalid");
66             }
67
68             return property;
69         }
70
71         ///<summary>
72         /// Returns a Property if stringProperty is a valid index
73         ///</summary>
74         internal static SearchResult Search(Animatable animatable, string stringProperty)
75         {
76             var propertyName = LowerFirstLetter(stringProperty);
77
78             if(animatable is View)
79             {
80                 View view = animatable as View;
81                 return SearchProperty(view, propertyName) ?? SearchVisualProperty(view, propertyName);
82             }
83             return SearchProperty(animatable, propertyName);
84         }
85
86         private static SearchResult SearchProperty(Animatable animatable, string lowercasePropertyString)
87         {
88             Property property = new Property(animatable, lowercasePropertyString);
89
90             if (property.propertyIndex == Property.InvalidIndex)
91             {
92                 property.Dispose();
93                 return null;
94             }
95
96             OOConverter converter = null;
97             if (animatable.GetPropertyType(property.propertyIndex).Equals(PropertyType.Float))
98             {
99                 converter = ObjectIntToFloat;
100             }
101
102             return new SearchResult(property, converter);
103         }
104
105         private static SearchResult SearchVisualProperty(View view, string lowercasePropertyString)
106         {
107             if (visualPropertyTable.TryGetValue(lowercasePropertyString, out var found))
108             {
109                 return GenerateVisualPropertySearchResult(view, found);
110             }
111
112             return null;
113         }
114
115         private static SearchResult GenerateVisualPropertySearchResult(View view, VisualPropertyData data)
116         {
117             var propertyIntPtr = Interop.View.GetVisualProperty(view.SwigCPtr, data.ViewPropertyIndex, data.VisualPropertyIndex);
118             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
119
120             var property = new Property(propertyIntPtr, true);
121             if (property.propertyIndex == Property.InvalidIndex)
122             {
123                 property.Dispose();
124                 return data.RelatedData == null ? null : GenerateVisualPropertySearchResult(view, data.RelatedData);
125             }
126
127             SearchResult result = new SearchResult(property, data.ObjectConverter, data.PropertyValueConverter);
128
129             if (data.RelatedData != null)
130             {
131                 result.NextResult = GenerateVisualPropertySearchResult(view, data.RelatedData);
132             }
133
134             return result;
135         }
136
137         private static string LowerFirstLetter(string original)
138         {
139             StringBuilder sb = new StringBuilder(original);
140             sb[0] = (char)(sb[0] | 0x20);
141             return sb.ToString();
142         }
143
144         private static object ObjectColorToVector3(object value)
145         {
146             if (value is Vector4)
147             {
148                 var colorValue = value as Vector4;
149                 return new Vector3(colorValue.R, colorValue.G, colorValue.B);
150             }
151
152             if (value is Color)
153             {
154                 var colorValue = value as Color;
155                 return new Vector3(colorValue.R, colorValue.G, colorValue.B);
156             }
157
158             return null;
159         }
160
161         private static PropertyValue PropertyValueColorToVector3(PropertyValue value)
162         {
163             var valueType = value.GetType();
164
165             if (valueType != PropertyType.Vector4)
166             {
167                 return null;
168             }
169
170             var colorValue = new Vector4();
171             value.Get(colorValue);
172             using (var v3 = new Vector3(colorValue.R, colorValue.G, colorValue.B))
173             {
174                 colorValue.Dispose();
175                 return new PropertyValue(v3);
176             }
177         }
178
179         private static object ObjectColorToAlpha(object value)
180         {
181             if (value is Vector4)
182             {
183                 var colorValue = value as Vector4;
184                 return colorValue.A;
185             }
186
187             if (value is Color)
188             {
189                 var colorValue = value as Color;
190                 return colorValue.A;
191             }
192
193             return null;
194         }
195
196         private static PropertyValue PropertyValueColorToAlpha(PropertyValue value)
197         {
198             var valueType = value.GetType();
199
200             if (valueType != PropertyType.Vector4)
201             {
202                 return null;
203             }
204
205             using (var colorValue = new Vector4())
206             {
207                 value.Get(colorValue);
208                 return new PropertyValue(colorValue.A);
209             }
210         }
211
212         private static object ObjectIntToFloat(object value)
213         {
214             Type type = value.GetType();
215             if (type.Equals(typeof(System.Int32)) || type.Equals(typeof(int)))
216             {
217                 return (float)((int)value);
218             }
219
220             return value;
221         }
222
223         private static object ObjectVector4ToFloat(object value)
224         {
225             if (value is Vector4 vector)
226             {
227                 return vector.X;
228             }
229
230             return value;
231         }
232
233         internal class SearchResult : Disposable
234         {
235             private readonly OOConverter objectConverter;
236             private readonly PPConverter propertyValueConverter;
237
238             internal SearchResult(Property property, OOConverter objectConverter = null, PPConverter propertyValueConverter = null)
239             {
240                 this.objectConverter = objectConverter;
241                 this.propertyValueConverter = propertyValueConverter;
242                 Property = property;
243             }
244
245             internal Property Property { get; }
246
247             internal SearchResult NextResult { get; set; }
248
249             internal PropertyValue RefineValue(object value)
250             {
251                 Debug.Assert(Property != null && value != null);
252
253                 var refined = value;
254
255                 if (objectConverter != null)
256                 {
257                     refined = objectConverter(value);
258                 }
259
260                 if (refined == null)
261                 {
262                     return null;
263                 }
264
265                 return PropertyValue.CreateFromObject(refined);
266             }
267
268
269             // Refine object as IntPtr of PropertyValue to optimize.
270             // Warning : This API don't automatically release memory.
271             internal global::System.IntPtr RefineValueIntPtr(object value)
272             {
273                 Debug.Assert(Property != null && value != null);
274
275                 var refined = value;
276
277                 if (objectConverter != null)
278                 {
279                     refined = objectConverter(value);
280                 }
281
282                 if (refined == null)
283                 {
284                     return global::System.IntPtr.Zero;
285                 }
286
287                 return PropertyValue.CreateFromObjectIntPtr(refined);
288             }
289
290             internal KeyFrames RefineKeyFrames(KeyFrames keyFrames)
291             {
292                 Debug.Assert(keyFrames != null);
293
294                 var refined = keyFrames;
295                 if (propertyValueConverter != null)
296                 {
297                     // TODO Enable this code when csharp-binder is ready
298                     // refined = new KeyFrames();
299                     // for (uint i = 0; i < keyFrames.Count; i++)
300                     // {
301                     //     var keyFrame = keyFrames.GetKeyFrame(i);
302                     //     var newKeyFrame = propertyValueConverter(keyFrame);
303                     //     if (newKeyFrame == null)
304                     //     {
305                     //         return null;
306                     //     }
307                     //     refined.Add(newKeyFrame);
308                     // }
309                 }
310
311                 return refined;
312             }
313
314             /// <summary>
315             /// Dispose
316             /// </summary>
317             protected override void Dispose(DisposeTypes type)
318             {
319                 if (disposed)
320                 {
321                     return;
322                 }
323
324                 if (type == DisposeTypes.Explicit)
325                 {
326                     Property.Dispose();
327                     NextResult?.Dispose();
328                 }
329
330                 base.Dispose(type);
331             }
332         }
333
334         private class VisualPropertyData
335         {
336             internal VisualPropertyData(int viewPropertyIndex, int visualPropertyIndex, OOConverter objectConverter = null, PPConverter propertyValueConverter = null, VisualPropertyData relatedData = null)
337             {
338                 ViewPropertyIndex = viewPropertyIndex;
339                 VisualPropertyIndex = visualPropertyIndex;
340                 ObjectConverter = objectConverter;
341                 PropertyValueConverter = propertyValueConverter;
342                 RelatedData = relatedData;
343             }
344
345             internal int ViewPropertyIndex { get; }
346
347             internal int VisualPropertyIndex { get; }
348
349             internal OOConverter ObjectConverter { get; }
350
351             internal PPConverter PropertyValueConverter { get; }
352
353             internal VisualPropertyData RelatedData { get; }
354         }
355     }
356 }