Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Common / Container.cs
1 /*
2  * Copyright(c) 2022 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.ComponentModel;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Binding;
23 using Tizen.NUI.Binding.Internals;
24
25 namespace Tizen.NUI
26 {
27     /// <summary>
28     /// The Container is an abstract class to be inherited from by classes that desire to have views
29     /// added to them.
30     /// </summary>
31     /// <since_tizen> 4 </since_tizen>
32     public abstract class Container : Animatable, IResourcesProvider
33     {
34         /// <summary> XamlStyleProperty </summary>
35         [EditorBrowsable(EditorBrowsableState.Never)]
36         public static readonly BindableProperty XamlStyleProperty = BindableProperty.Create(nameof(XamlStyle), typeof(XamlStyle), typeof(Container), default(XamlStyle), propertyChanged: (bindable, oldvalue, newvalue) => ((View)bindable).MergedStyle.Style = (XamlStyle)newvalue);
37
38         internal BaseHandle InternalParent;
39         private List<View> childViews = new List<View>();
40         private MergedStyle mergedStyle = null;
41         ResourceDictionary _resources;
42         bool IResourcesProvider.IsResourcesCreated => _resources != null;
43
44         internal Container(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
45         {
46             // No un-managed data hence no need to store a native ptr
47         }
48
49         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public ResourceDictionary XamlResources
52         {
53             get
54             {
55                 if (_resources != null)
56                     return _resources;
57                 _resources = new ResourceDictionary();
58                 ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
59                 return _resources;
60             }
61             set
62             {
63                 if (_resources == value)
64                     return;
65                 OnPropertyChanging();
66                 if (_resources != null)
67                     ((IResourceDictionary)_resources).ValuesChanged -= OnResourcesChanged;
68                 _resources = value;
69                 OnResourcesChanged(value);
70                 if (_resources != null)
71                     ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
72                 OnPropertyChanged();
73             }
74         }
75
76         [EditorBrowsable(EditorBrowsableState.Never)]
77         public XamlStyle XamlStyle
78         {
79             get
80             {
81                 return (XamlStyle)GetValue(XamlStyleProperty);
82             }
83             set
84             {
85                 SetValue(XamlStyleProperty, value);
86             }
87         }
88
89         internal MergedStyle MergedStyle
90         {
91             get
92             {
93                 if (null == mergedStyle)
94                 {
95                     mergedStyle = new MergedStyle(GetType(), this);
96                 }
97
98                 return mergedStyle;
99             }
100         }
101
102         /// <summary>
103         /// List of children of Container.
104         /// </summary>
105         /// <since_tizen> 4 </since_tizen>
106         public List<View> Children
107         {
108             get
109             {
110                 return childViews;
111             }
112         }
113
114         /// <summary>
115         /// Gets the parent container.
116         /// Read only
117         /// </summary>
118         /// <pre>The child container has been initialized.</pre>
119         /// <returns>The parent container.</returns>
120         /// <since_tizen> 4 </since_tizen>
121         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1721: Property names should not match get methods")]
122         public new Container Parent
123         {
124             get
125             {
126                 return GetParent();
127             }
128         }
129
130         /// <summary>
131         /// Gets the number of children for this container.
132         /// Read only
133         /// </summary>
134         /// <pre>The container has been initialized.</pre>
135         /// <returns>The number of children.</returns>
136         /// <since_tizen> 4 </since_tizen>
137         public uint ChildCount
138         {
139             get
140             {
141                 return Convert.ToUInt32(Children.Count);
142             }
143         }
144
145         /// <summary>
146         /// Copy all properties, bindings.
147         /// Copy children without xName from other container, copy all properties, bindings of children with xName.
148         /// </summary>
149         /// <param name="other"></param>
150         [EditorBrowsable(EditorBrowsableState.Never)]
151         public void CopyAndKeepXNameInstance(Container other)
152         {
153             CopyFrom(other);
154
155             var nameScopeOfOther = NameScope.GetNameScope(other) as NameScope;
156             var nameScope = NameScope.GetNameScope(this) as NameScope;
157
158             if (null == nameScopeOfOther)
159             {
160                 if (null != nameScope)
161                 {
162                     return;
163                 }
164             }
165             else if (!nameScopeOfOther.Equal(nameScope))
166             {
167                 return;
168             }
169
170             var xNameToElementsOfOther = nameScopeOfOther?.NameToElement;
171             var xNameToElements = nameScope?.NameToElement;
172
173             if (null != xNameToElements)
174             {
175                 foreach (var pair in xNameToElements)
176                 {
177                     if (pair.Value is View view)
178                     {
179                         view.Parent?.Remove(view);
180                     }
181                 }
182             }
183
184             for (int i = Children.Count - 1; i >= 0; i--)
185             {
186                 var child = GetChildAt((uint)i);
187                 Remove(child);
188
189                 child.DisposeIncludeChildren();
190             }
191
192             CopyChildren(other);
193
194             if (null != xNameToElementsOfOther)
195             {
196                 foreach (var pair in xNameToElementsOfOther)
197                 {
198                     if (pair.Value is View view)
199                     {
200                         var parent = view.Parent;
201
202                         if (null != parent)
203                         {
204                             if (null != xNameToElements)
205                             {
206                                 var holdedXElements = xNameToElements[pair.Key] as View;
207                                 holdedXElements.CopyBindingRelationShip(view);
208                                 holdedXElements.CopyFrom(view);
209
210                                 parent.ReplaceChild(view, holdedXElements);
211                             }
212                         }
213                     }
214                 }
215             }
216
217             ReplaceBindingElementInWholeTree(xNameToElementsOfOther, xNameToElements);
218
219             if (null != xNameToElementsOfOther)
220             {
221                 foreach (var pair in xNameToElementsOfOther)
222                 {
223                     if (pair.Value is View view)
224                     {
225                         view.Dispose();
226                     }
227                 }
228             }
229         }
230
231         /// <summary>
232         /// Adds a child view to this Container.
233         /// </summary>
234         /// <pre>This Container (the parent) has been initialized. The child view has been initialized. The child view is not the same as the parent view.</pre>
235         /// <post>The child will be referenced by its parent. This means that the child will be kept alive, even if the handle passed into this method is reset or destroyed.</post>
236         /// <remarks>If the child already has a parent, it will be removed from the old parent and reparented to this view. This may change child's position, color, scale, etc. as it now inherits them from this view.</remarks>
237         /// <param name="view">The child view to add.</param>
238         /// <since_tizen> 4 </since_tizen>
239         public abstract void Add(View view);
240
241         /// <summary>
242         /// Removes a child view from this view. If the view was not a child of this view, this is a no-op.
243         /// </summary>
244         /// <pre>This View(the parent) has been initialized. The child view is not the same as the parent view.</pre>
245         /// <param name="view">The view to remove</param>
246         /// <since_tizen> 4 </since_tizen>
247         public abstract void Remove(View view);
248
249         /// <summary>
250         /// Retrieves the child view by the index.
251         /// </summary>
252         /// <pre>The view has been initialized.</pre>
253         /// <param name="index">The index of the child to retrieve.</param>
254         /// <returns>The view for the given index or empty handle if children are not initialized.</returns>
255         /// <since_tizen> 4 </since_tizen>
256         public abstract View GetChildAt(uint index);
257
258         /// <summary>
259         /// Gets the parent of this container.
260         /// </summary>
261         /// <pre>The child container has been initialized.</pre>
262         /// <returns>The parent container.</returns>
263         /// <since_tizen> 4 </since_tizen>
264         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1721: Property names should not match get methods")]
265         public abstract Container GetParent();
266
267         /// <summary>
268         /// Gets the number of children for this container.
269         /// </summary>
270         /// <pre>The container has been initialized.</pre>
271         /// <returns>The number of children.</returns>
272         /// <since_tizen> 4 </since_tizen>
273         [Obsolete("Deprecated in API9, will be removed in API11. Please use ChildCount property instead!")]
274         public abstract UInt32 GetChildCount();
275
276         internal abstract View FindCurrentChildById(uint id);
277
278         internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
279         {
280             if (values == null)
281                 return;
282
283             if (!((IResourcesProvider)this).IsResourcesCreated || XamlResources.Count == 0)
284             {
285                 base.OnParentResourcesChanged(values);
286                 return;
287             }
288
289             var innerKeys = new HashSet<string>();
290             var changedResources = new List<KeyValuePair<string, object>>();
291             foreach (KeyValuePair<string, object> c in XamlResources)
292                 innerKeys.Add(c.Key);
293             foreach (KeyValuePair<string, object> value in values)
294             {
295                 if (innerKeys.Add(value.Key))
296                     changedResources.Add(value);
297             }
298             if (changedResources.Count != 0)
299                 OnResourcesChanged(changedResources);
300         }
301
302         /// <summary>
303         /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
304         /// </summary>
305         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
306         [EditorBrowsable(EditorBrowsableState.Never)]
307         protected override void OnBindingContextChanged()
308         {
309             var gotBindingContext = false;
310             object bc = null;
311
312             for (var index = 0; index < Children.Count; index++)
313             {
314                 Element child = Children[index];
315
316                 if (!gotBindingContext)
317                 {
318                     bc = BindingContext;
319                     gotBindingContext = true;
320                 }
321
322                 SetChildInheritedBindingContext(child, bc);
323             }
324             base.OnBindingContextChanged();
325         }
326
327         private void DisposeIncludeChildren()
328         {
329             foreach (var child in Children)
330             {
331                 child.DisposeIncludeChildren();
332             }
333
334             if (IsCreateByXaml)
335             {
336                 Dispose();
337                 ClearBinding();
338             }
339         }
340
341         private void CopyChildren(Container other)
342         {
343             var childrenOfOtherView = new List<View>();
344
345             foreach (var child in other.Children)
346             {
347                 childrenOfOtherView.Add(child);
348             }
349
350             foreach (var child in childrenOfOtherView)
351             {
352                 Add(child);
353             }
354         }
355
356         private void ReplaceChild(View child, View newChild)
357         {
358             int indexOfView = Children.FindIndex((View v) => { return v == child; });
359
360             var childrenNeedtoReAdd = new Stack<View>();
361
362             for (int i = Children.Count - 1; i > indexOfView; i--)
363             {
364                 childrenNeedtoReAdd.Push(Children[i]);
365                 Remove(Children[i]);
366             }
367
368             Remove(child);
369
370             childrenNeedtoReAdd.Push(newChild);
371
372             while (0 < childrenNeedtoReAdd.Count)
373             {
374                 Add(childrenNeedtoReAdd.Pop());
375             }
376         }
377
378         private void ReplaceBindingElementInWholeTree(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
379         {
380             if (IsCreateByXaml)
381             {
382                 ReplaceBindingElement(oldNameScope, newNameScope);
383
384                 foreach (var child in Children)
385                 {
386                     child.ReplaceBindingElementInWholeTree(oldNameScope, newNameScope);
387                 }
388             }
389         }
390     }
391 } // namespace Tizen.NUI