[NUI] Remove unused property of Transition
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Theme / ExternalThemeKeyList.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 using System;
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.Reflection;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Xaml;
23
24 namespace Tizen.NUI
25 {
26     /// <summary>
27     /// This class holds a list of string key and corresponding setter function.
28     /// <para>
29     /// Key
30     /// Represents a specific property in a style, for example,
31     /// the key "/Text/PixelSize" of styleType "Button" means "ButtonStyle.Text.PixelSize".
32     /// </para>
33     /// <para>
34     /// Action
35     /// One action takes two values,
36     /// * viewStyle : A style instance in a theme.
37     /// * value: A string value that will overwrite a property value in a given viewStyle.
38     /// The action parses a given string value into a typed object and set it to the viewStyle.
39     /// </para>
40     /// </summary>
41     internal class ExternalThemeKeyList
42     {
43         private Type componentType;
44         private Type styleType;
45         private object selectorData;
46         private List<KeyActionPair> list = new List<KeyActionPair>();
47
48         /// <summary>
49         /// Create a new key list
50         /// For example,
51         /// <code>
52         /// new ExternalThemeKeyList(typeof(Button), typeof(ButtonStyle));
53         /// </code>
54         /// This means that the keys are related with ButtonStyle and they are defined to decorate Button component.
55         /// </summary>
56         internal ExternalThemeKeyList(Type componentType, Type styleType)
57         {
58             this.componentType = componentType;
59             this.styleType = styleType;
60         }
61
62         /// <summary>
63         /// Add a key with a corresponding action.
64         /// For example,
65         /// <code>
66         /// var keyList = new ExternalThemeKeyList(typeof(Button), typeof(ButtonStyle));
67         /// keyList.Add&lt;Extents&gt;("/Margin", (ViewStyle style, Extents value) => viewStyle.Margin = value);
68         /// </code>
69         /// </summary>
70         internal ExternalThemeKeyList Add<T>(string key, Action<ViewStyle, T> setter)
71         {
72             list.Add(new KeyActionPair(componentType.Name + key, (ViewStyle viewStyle, string stringInput) =>
73             {
74                 if (ParseXamlStringToObject<T>(stringInput) is T tValue)
75                 {
76                     if (tValue != null)
77                     {
78                         setter(viewStyle, tValue);
79                     }
80                 }
81             }));
82             return this;
83         }
84
85         /// <summary>
86         /// <para>
87         /// The series of actions for a selector.
88         /// T is a content type of a selector.
89         /// For example, assume that the component type is "Progress",
90         /// <code>
91         /// AddSelector&lt;Color&gt;("/TrackColor", setter);
92         /// </code>
93         /// This will produce additional keys and actions for 5 basic ControlStates,
94         /// "Progress/TrackColor"
95         /// "Progress/TrackColorFocused"
96         /// "Progress/TrackColorPressed"
97         /// "Progress/TrackColorDisabled"
98         /// "Progress/TrackColorOther"
99         /// </para>
100         /// <para>
101         /// Note that if you want to add actions for additional control states, please specify them as parameter.
102         /// </para>
103         /// </summary>
104         /// <code>
105         /// AddSelector&lt;Color&gt;("/TrackColor", setter, ControlState.DisabledSelected);
106         /// </code>
107         internal ExternalThemeKeyList AddSelector<T>(string key, Action<ViewStyle, Selector<T>> setter, params ControlState[] additionalStates)
108         {
109             list.Add(new KeyActionPair(componentType.Name + key, GenSelectorAction<T>(ControlState.Normal)));
110             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Focused), GenSelectorAction<T>(ControlState.Focused)));
111             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Pressed), GenSelectorAction<T>(ControlState.Pressed)));
112             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Disabled), GenSelectorAction<T>(ControlState.Disabled)));
113             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Other), GenSelectorAction<T>(ControlState.Other)));
114
115             foreach (var state in additionalStates)
116             {
117                 list.Add(new KeyActionPair(componentType.Name + key + nameof(state), GenSelectorAction<T>(state)));
118             }
119
120             list.Add(new KeyActionPair(GenSelectorFinalizer(setter)));
121
122             return this;
123         }
124
125         /// <summary>
126         /// Note that, the view's background can be either color or image value.
127         /// This method add keys for a selector and each action can handle both color and string (for image url).
128         /// </summary>
129         internal ExternalThemeKeyList AddBackgroundSelector(string key, Action<ViewStyle, Selector<Color>> colorSetter, Action<ViewStyle, Selector<string>> imageSetter, params ControlState[] additionalStates)
130         {
131             list.Add(new KeyActionPair(componentType.Name + key, GenBackgroundSelectorAction(ControlState.Normal)));
132             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Focused), GenBackgroundSelectorAction(ControlState.Focused)));
133             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Pressed), GenBackgroundSelectorAction(ControlState.Pressed)));
134             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Disabled), GenBackgroundSelectorAction(ControlState.Disabled)));
135             list.Add(new KeyActionPair(componentType.Name + key + nameof(ControlState.Other), GenBackgroundSelectorAction(ControlState.Other)));
136
137             foreach (var state in additionalStates)
138             {
139                 list.Add(new KeyActionPair(componentType.Name + key + nameof(state), GenBackgroundSelectorAction(state)));
140             }
141
142             list.Add(new KeyActionPair(GenColorOrImageSelectorFinalizer(colorSetter, imageSetter)));
143
144             return this;
145         }
146
147         internal void ApplyKeyActions(IExternalTheme externalTheme, Theme theme)
148         {
149             var style = theme.GetStyle(componentType.FullName);
150
151             foreach (var item in list)
152             {
153                 if (item.IsFinalizer)
154                 {
155                     item.Action(style, null);
156                     continue;
157                 }
158
159                 string newValue = externalTheme.GetValue(item.Key);
160                 if (newValue != null)
161                 {
162                     if (style == null || style.GetType() != styleType)
163                     {
164                         style = Activator.CreateInstance(styleType) as ViewStyle;
165                         theme.AddStyleWithoutClone(componentType.FullName, style);
166                     }
167
168                     // Invoke action with the existing style to overwrite properties of it.
169                     item.Action(style, newValue);
170                 }
171             }
172         }
173
174         private Action<ViewStyle, string> GenSelectorAction<T>(ControlState state)
175         {
176             return (ViewStyle viewStyle, string stringInput) =>
177             {
178                 if (ParseXamlStringToObject<T>(stringInput) is T tValue)
179                 {
180                     if (selectorData == null)
181                     {
182                         selectorData = new Selector<T>();
183                     }
184                     ((Selector<T>)selectorData).AddWithoutDuplicationCheck(state, tValue);
185                 }
186             };
187         }
188
189         private Action<ViewStyle, string> GenBackgroundSelectorAction(ControlState state)
190         {
191             return (ViewStyle viewStyle, string stringInput) =>
192             {
193                 var imageUrl = TryConvertToResourcePath(stringInput);
194
195                 if (imageUrl != null)
196                 {
197                     if (selectorData == null || (selectorData as Selector<string> == null))
198                     {
199                         selectorData = new Selector<string>();
200                     }
201                     ((Selector<string>)selectorData).AddWithoutDuplicationCheck(state, imageUrl);
202                 }
203                 else if (ParseXamlStringToObject<Color>(stringInput) is Color color)
204                 {
205                     if (selectorData == null || (selectorData as Selector<Color> == null))
206                     {
207                         selectorData = new Selector<Color>();
208                     }
209                     ((Selector<Color>)selectorData).AddWithoutDuplicationCheck(state, color);
210                 }
211             };
212         }
213
214         private Action<ViewStyle, string> GenSelectorFinalizer<T>(Action<ViewStyle, Selector<T>> setter)
215         {
216             return (ViewStyle viewStyle, string value) =>
217             {
218                 if (selectorData == null || viewStyle == null)
219                 {
220                     return;
221                 }
222                 setter(viewStyle, (Selector<T>)selectorData);
223                 selectorData = null;
224             };
225         }
226
227         private Action<ViewStyle, string> GenColorOrImageSelectorFinalizer(Action<ViewStyle, Selector<Color>> colorSetter, Action<ViewStyle, Selector<string>> imageSetter)
228         {
229             return (ViewStyle viewStyle, string value) =>
230             {
231                 if (viewStyle == null)
232                 {
233                     return;
234                 }
235                 if (selectorData is Selector<Color> colorSelector)
236                 {
237                     colorSetter(viewStyle, colorSelector);
238                 }
239                 else if (selectorData is Selector<string> imageSelector)
240                 {
241                     imageSetter(viewStyle, imageSelector);
242                 }
243                 selectorData = null;
244             };
245         }
246
247         private static object ParseXamlStringToObject<T>(string stringInput)
248         {
249             if (typeof(T) == typeof(string))
250             {
251                 return TryConvertToResourcePath(stringInput) ?? stringInput;
252             }
253             return stringInput.ConvertTo(typeof(T), () => typeof(T).GetTypeInfo(), null);
254         }
255
256         private static string TryConvertToResourcePath(string stringInput)
257         {
258             Debug.Assert(stringInput != null);
259             if (stringInput.StartsWith("{") && stringInput.EndsWith("}"))
260             {
261                 // TODO Need to use Tizen.Applications.ThemeManager.Theme.GetPath instead SharedResourcePath after fixing abort problem.
262                 return ExternalThemeManager.SharedResourcePath + "/" + stringInput.Substring(1, stringInput.Length - 2);
263             }
264             return null;
265         }
266
267         internal struct KeyActionPair
268         {
269             public KeyActionPair(string key, Action<ViewStyle, string> action)
270             {
271                 Key = key;
272                 Action = action;
273             }
274
275             public KeyActionPair(Action<ViewStyle, string> action)
276             {
277                 Key = null;
278                 Action = action;
279             }
280
281             public bool IsFinalizer => (Key == null);
282
283             public string Key { get; }
284
285             public Action<ViewStyle, string> Action { get; }
286         }
287     }
288 }