2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.Reflection;
21 using Tizen.NUI.BaseComponents;
27 /// This class holds a list of string key and corresponding setter function.
30 /// Represents a specific property in a style, for example,
31 /// the key "/Text/PixelSize" of styleType "Button" means "ButtonStyle.Text.PixelSize".
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.
41 internal class ExternalThemeKeyList
43 private Type componentType;
44 private Type styleType;
45 private object selectorData;
46 private List<KeyActionPair> list = new List<KeyActionPair>();
49 /// Create a new key list
52 /// new ExternalThemeKeyList(typeof(Button), typeof(ButtonStyle));
54 /// This means that the keys are related with ButtonStyle and they are defined to decorate Button component.
56 internal ExternalThemeKeyList(Type componentType, Type styleType)
58 this.componentType = componentType;
59 this.styleType = styleType;
63 /// Add a key with a corresponding action.
66 /// var keyList = new ExternalThemeKeyList(typeof(Button), typeof(ButtonStyle));
67 /// keyList.Add<Extents>("/Margin", (ViewStyle style, Extents value) => viewStyle.Margin = value);
70 internal ExternalThemeKeyList Add<T>(string key, Action<ViewStyle, T> setter)
72 list.Add(new KeyActionPair(componentType.Name + key, (ViewStyle viewStyle, string stringInput) =>
74 if (ParseXamlStringToObject<T>(stringInput) is T tValue)
78 setter(viewStyle, tValue);
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",
91 /// AddSelector<Color>("/TrackColor", setter);
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"
101 /// Note that if you want to add actions for additional control states, please specify them as parameter.
105 /// AddSelector<Color>("/TrackColor", setter, ControlState.DisabledSelected);
107 internal ExternalThemeKeyList AddSelector<T>(string key, Action<ViewStyle, Selector<T>> setter, params ControlState[] additionalStates)
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)));
115 foreach (var state in additionalStates)
117 list.Add(new KeyActionPair(componentType.Name + key + nameof(state), GenSelectorAction<T>(state)));
120 list.Add(new KeyActionPair(GenSelectorFinalizer(setter)));
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).
129 internal ExternalThemeKeyList AddBackgroundSelector(string key, Action<ViewStyle, Selector<Color>> colorSetter, Action<ViewStyle, Selector<string>> imageSetter, params ControlState[] additionalStates)
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)));
137 foreach (var state in additionalStates)
139 list.Add(new KeyActionPair(componentType.Name + key + nameof(state), GenBackgroundSelectorAction(state)));
142 list.Add(new KeyActionPair(GenColorOrImageSelectorFinalizer(colorSetter, imageSetter)));
147 internal void ApplyKeyActions(IExternalTheme externalTheme, Theme theme)
149 var style = theme.GetStyle(componentType.FullName);
151 foreach (var item in list)
153 if (item.IsFinalizer)
155 item.Action(style, null);
159 string newValue = externalTheme.GetValue(item.Key);
160 if (newValue != null)
162 if (style == null || style.GetType() != styleType)
164 style = Activator.CreateInstance(styleType) as ViewStyle;
165 theme.AddStyleWithoutClone(componentType.FullName, style);
168 // Invoke action with the existing style to overwrite properties of it.
169 item.Action(style, newValue);
174 private Action<ViewStyle, string> GenSelectorAction<T>(ControlState state)
176 return (ViewStyle viewStyle, string stringInput) =>
178 if (ParseXamlStringToObject<T>(stringInput) is T tValue)
180 if (selectorData == null)
182 selectorData = new Selector<T>();
184 ((Selector<T>)selectorData).AddWithoutDuplicationCheck(state, tValue);
189 private Action<ViewStyle, string> GenBackgroundSelectorAction(ControlState state)
191 return (ViewStyle viewStyle, string stringInput) =>
193 var imageUrl = TryConvertToResourcePath(stringInput);
195 if (imageUrl != null)
197 if (selectorData == null || (selectorData as Selector<string> == null))
199 selectorData = new Selector<string>();
201 ((Selector<string>)selectorData).AddWithoutDuplicationCheck(state, imageUrl);
203 else if (ParseXamlStringToObject<Color>(stringInput) is Color color)
205 if (selectorData == null || (selectorData as Selector<Color> == null))
207 selectorData = new Selector<Color>();
209 ((Selector<Color>)selectorData).AddWithoutDuplicationCheck(state, color);
214 private Action<ViewStyle, string> GenSelectorFinalizer<T>(Action<ViewStyle, Selector<T>> setter)
216 return (ViewStyle viewStyle, string value) =>
218 if (selectorData == null || viewStyle == null)
222 setter(viewStyle, (Selector<T>)selectorData);
227 private Action<ViewStyle, string> GenColorOrImageSelectorFinalizer(Action<ViewStyle, Selector<Color>> colorSetter, Action<ViewStyle, Selector<string>> imageSetter)
229 return (ViewStyle viewStyle, string value) =>
231 if (viewStyle == null)
235 if (selectorData is Selector<Color> colorSelector)
237 colorSetter(viewStyle, colorSelector);
239 else if (selectorData is Selector<string> imageSelector)
241 imageSetter(viewStyle, imageSelector);
247 private static object ParseXamlStringToObject<T>(string stringInput)
249 if (typeof(T) == typeof(string))
251 return TryConvertToResourcePath(stringInput) ?? stringInput;
253 return stringInput.ConvertTo(typeof(T), () => typeof(T).GetTypeInfo(), null);
256 private static string TryConvertToResourcePath(string stringInput)
258 Debug.Assert(stringInput != null);
259 if (stringInput.StartsWith("{") && stringInput.EndsWith("}"))
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);
267 internal struct KeyActionPair
269 public KeyActionPair(string key, Action<ViewStyle, string> action)
275 public KeyActionPair(Action<ViewStyle, string> action)
281 public bool IsFinalizer => (Key == null);
283 public string Key { get; }
285 public Action<ViewStyle, string> Action { get; }