ab48b4e032f105af09af6d0467a5e8e65d06a22d
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Theme / Theme.cs
1 /*
2  * Copyright(c) 2020 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;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Xml;
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding;
24 using Tizen.NUI.Xaml;
25
26 namespace Tizen.NUI
27 {
28     /// <summary></summary>
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class Theme : BindableObject
31     {
32         private readonly Dictionary<string, ViewStyle> map;
33         private string baseTheme;
34
35         /// <summary>Create an empty theme.</summary>
36         [EditorBrowsable(EditorBrowsableState.Never)]
37         public Theme()
38         {
39             map = new Dictionary<string, ViewStyle>();
40         }
41
42         /// <summary>Create a new theme from the xaml file.</summary>
43         /// <param name="xamlFile">An absolute path to the xaml file.</param>
44         /// <exception cref="ArgumentNullException">Thrown when the given xamlFile is null or empty string.</exception>
45         /// <exception cref="global::System.IO.IOException">Thrown when there are file IO problems.</exception>
46         /// <exception cref="Exception">Thrown when the content of the xaml file is not valid xaml form.</exception>
47         [EditorBrowsable(EditorBrowsableState.Never)]
48         public Theme(string xamlFile) : this()
49         {
50             if (string.IsNullOrEmpty(xamlFile))
51             {
52                 throw new ArgumentNullException("The xaml file path cannot be null or empty string", nameof(xamlFile));
53             }
54
55             try
56             {
57                 using(var reader = XmlReader.Create(xamlFile))
58                 {
59                     XamlLoader.Load(this, reader);
60                 }
61             }
62             catch (global::System.IO.IOException e)
63             {
64                 Tizen.Log.Info("NUI", $"Could not load \"{xamlFile}\".\n");
65                 throw e;
66             }
67             catch (Exception e)
68             {
69                 Tizen.Log.Info("NUI", $"Could not parse \"{xamlFile}\".\n");
70                 Tizen.Log.Info("NUI", "Make sure the all used assemblies (e.g. Tizen.NUI.Components) are included in the application project.\n");
71                 Tizen.Log.Info("NUI", "Make sure the type and namespace are correct.\n");
72                 throw e;
73             }
74         }
75
76         /// <summary></summary>
77         [EditorBrowsable(EditorBrowsableState.Never)]
78         public string Id { get; set; }
79
80         /// <summary>
81         /// For Xaml use only.
82         /// The bulit-in theme id that will be used as base of this.
83         /// View styles with same key are merged.
84         /// </summary>
85         internal string BasedOn
86         {
87             get => baseTheme;
88             set
89             {
90                 baseTheme = value;
91
92                 if (string.IsNullOrEmpty(baseTheme)) return;
93
94                 var baseThemeInstance = ThemeManager.GetBuiltinTheme(baseTheme);
95
96                 if (baseThemeInstance != null)
97                 {
98                     foreach (var item in baseThemeInstance)
99                     {
100                         var baseStyle = item.Value?.Clone();
101                         if (map.ContainsKey(item.Key))
102                         {
103                             baseStyle.Merge(map[item.Key]);
104                         }
105                         map[item.Key] = baseStyle;
106                     }
107                 }
108             }
109         }
110
111         /// <summary>
112         /// For Xaml use only.
113         /// Note that it is not a normal indexer.
114         /// Setter will merge the new value with existing item.
115         /// </summary>
116         internal ViewStyle this[string styleName]
117         {
118             get => map[styleName];
119             set
120             {
121                 if (value == null)
122                 {
123                     map.Remove(styleName);
124                     return;
125                 }
126
127                 if (map.TryGetValue(styleName, out ViewStyle style) && style != null && style.GetType() == value.GetType())
128                 {
129                     style.Merge(value);
130                 }
131                 else
132                 {
133                     map[styleName] = value;
134                 }
135             }
136         }
137
138         internal int Count => map.Count;
139
140         /// <summary>
141         /// Get an enumerator of the theme.
142         /// </summary>
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public IEnumerator<KeyValuePair<string, ViewStyle>> GetEnumerator() => map.GetEnumerator();
145
146         /// <summary>
147         /// Removes all styles in the theme.
148         /// </summary>
149         [EditorBrowsable(EditorBrowsableState.Never)]
150         public void Clear() => map.Clear();
151
152         /// <summary>
153         /// Determines whether the theme contains the specified style name.
154         /// </summary>
155         /// <exception cref="ArgumentNullException">The given style name is null.</exception>
156         [EditorBrowsable(EditorBrowsableState.Never)]
157         public bool HasStyle(string styleName) => map.ContainsKey(styleName);
158
159         /// <summary>
160         /// Removes the style with the specified style name.
161         /// </summary>
162         /// <exception cref="ArgumentNullException">The given style name is null.</exception>
163         [EditorBrowsable(EditorBrowsableState.Never)]
164         public bool RemoveStyle(string styleName) => map.Remove(styleName);
165
166         /// <summary>
167         /// Gets a style of given style name.
168         /// </summary>
169         /// <param name="styleName">The string key to find a ViewStyle.</param>
170         /// <returns>Founded style instance.</returns>
171         [EditorBrowsable(EditorBrowsableState.Never)]
172         public ViewStyle GetStyle(string styleName) => map.ContainsKey(styleName) ? map[styleName] : null;
173
174         /// <summary>
175         /// Gets a style of given view type.
176         /// </summary>
177         /// <param name="viewType">The type of View.</param>
178         /// <returns>Founded style instance.</returns>
179         [EditorBrowsable(EditorBrowsableState.Never)]
180         public ViewStyle GetStyle(Type viewType)
181         {
182             var currentType = viewType;
183             ViewStyle resultStyle = null;
184
185             do
186             {
187                 if (currentType.Equals(typeof(View))) break;
188                 resultStyle = GetStyle(currentType.FullName);
189                 currentType = currentType.BaseType;
190             }
191             while (resultStyle == null && currentType != null);
192
193             return resultStyle;
194         }
195
196         /// <summary>
197         /// Adds the specified style name and value to the theme.
198         /// This replace existing value if the theme already has a style with given name.
199         /// </summary>
200         /// <param name="styleName">The style name to add.</param>
201         /// <param name="value">The style instance to add.</param>
202         [EditorBrowsable(EditorBrowsableState.Never)]
203         public void AddStyle(string styleName, ViewStyle value) => map[styleName] = value?.Clone();
204
205
206         /// <inheritdoc/>
207         [EditorBrowsable(EditorBrowsableState.Never)]
208         public object Clone()
209         {
210             var result = new Theme()
211             {
212                 Id = this.Id,
213             };
214
215             foreach (var item in this)
216             {
217                 result.AddStyle(item.Key, item.Value);
218             }
219             return result;
220         }
221
222         /// <summary>Merge other Theme into this.</summary>
223         /// <param name="xamlFile">An absolute path to the xaml file of the theme.</param>
224         /// <exception cref="ArgumentException">Thrown when the given xamlFile is null or empty string.</exception>
225         /// <exception cref="global::System.IO.IOException">Thrown when there are file IO problems.</exception>
226         /// <exception cref="XamlParseException">Thrown when the content of the xaml file is not valid xaml form.</exception>
227         [EditorBrowsable(EditorBrowsableState.Never)]
228         public void Merge(string xamlFile)
229         {
230             Merge(new Theme(xamlFile));
231         }
232
233         /// <summary>Merge other Theme into this.</summary>
234         /// <param name="theme">The Theme.</param>
235         [EditorBrowsable(EditorBrowsableState.Never)]
236         public void Merge(Theme theme)
237         {
238             if (theme == null)
239                 throw new ArgumentNullException(nameof(theme));
240             
241             foreach (var item in theme)
242             {
243                 if (item.Value == null)
244                 {
245                     map[item.Key] = null;
246                 }
247                 else if (map.ContainsKey(item.Key) && !item.Value.SolidNull)
248                 {
249                     map[item.Key].Merge(theme.GetStyle(item.Key));
250                 }
251                 else
252                 {
253                     map[item.Key] = theme.GetStyle(item.Key).Clone();
254                 }
255             }
256         }
257
258         /// <summary>
259         /// Internal use only.
260         /// </summary>
261         internal void AddStyleWithoutClone(string styleName, ViewStyle value) => map[styleName] = value;
262     }
263 }