823ca55af397b4b05cc5e69a6f37ac3d96bf681b
[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 GetChildren();
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) && (xNameToElements[pair.Key] is View holdedXElements))
205                             {
206                                 holdedXElements.CopyBindingRelationShip(view);
207                                 holdedXElements.CopyFrom(view);
208
209                                 parent.ReplaceChild(view, holdedXElements);
210                             }
211                         }
212                     }
213                 }
214             }
215
216             ReplaceBindingElementInWholeTree(xNameToElementsOfOther, xNameToElements);
217
218             if (null != xNameToElementsOfOther)
219             {
220                 foreach (var pair in xNameToElementsOfOther)
221                 {
222                     if (pair.Value is View view)
223                     {
224                         view.Dispose();
225                     }
226                 }
227             }
228         }
229
230         /// <summary>
231         /// Dispose itself and all children recursively.
232         /// We can call this even itself is disposed.
233         /// </summary>
234         /// <remarks>
235         /// Note that Container.DisposeIncludeChildren() disposed only if it created by xaml.
236         /// </remarks>
237         /// <remarks>
238         /// This is a hidden API(inhouse API) only for internal purpose.
239         /// </remarks>
240         [EditorBrowsable(EditorBrowsableState.Never)]
241         public void DisposeRecursively()
242         {
243             // To avoid useless "OnChildRemoved" callback invoke, Dispose itself before children.
244             if(!Disposed && !IsDisposeQueued)
245             {
246                 Dispose();
247             }
248
249             foreach (View child in Children)
250             {
251                 child.DisposeRecursively();
252             }
253
254             // Make sure that itself don't have children anymore.
255             childViews?.Clear();
256         }
257
258         /// <summary>
259         /// Adds a child view to this Container.
260         /// </summary>
261         /// <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>
262         /// <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>
263         /// <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>
264         /// <param name="view">The child view to add.</param>
265         /// <since_tizen> 4 </since_tizen>
266         public abstract void Add(View view);
267
268         /// <summary>
269         /// Removes a child view from this view. If the view was not a child of this view, this is a no-op.
270         /// </summary>
271         /// <pre>This View(the parent) has been initialized. The child view is not the same as the parent view.</pre>
272         /// <param name="view">The view to remove</param>
273         /// <since_tizen> 4 </since_tizen>
274         public abstract void Remove(View view);
275
276         /// <summary>
277         /// Retrieves the child view by the index.
278         /// </summary>
279         /// <pre>The view has been initialized.</pre>
280         /// <param name="index">The index of the child to retrieve.</param>
281         /// <returns>The view for the given index or empty handle if children are not initialized.</returns>
282         /// <since_tizen> 4 </since_tizen>
283         public abstract View GetChildAt(uint index);
284
285         /// <summary>
286         /// Gets the parent of this container.
287         /// </summary>
288         /// <pre>The child container has been initialized.</pre>
289         /// <returns>The parent container.</returns>
290         /// <since_tizen> 4 </since_tizen>
291         [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1721: Property names should not match get methods")]
292         public abstract Container GetParent();
293
294         /// <summary>
295         /// Gets the number of children for this container.
296         /// </summary>
297         /// <pre>The container has been initialized.</pre>
298         /// <returns>The number of children.</returns>
299         /// <since_tizen> 4 </since_tizen>
300         [Obsolete("This has been deprecated in API9 and will be removed in API11. Use ChildCount property instead.")]
301         public abstract UInt32 GetChildCount();
302
303         internal abstract View FindCurrentChildById(uint id);
304
305         internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
306         {
307             if (values == null)
308                 return;
309
310             if (!((IResourcesProvider)this).IsResourcesCreated || XamlResources.Count == 0)
311             {
312                 base.OnParentResourcesChanged(values);
313                 return;
314             }
315
316             var innerKeys = new HashSet<string>();
317             var changedResources = new List<KeyValuePair<string, object>>();
318             foreach (KeyValuePair<string, object> c in XamlResources)
319                 innerKeys.Add(c.Key);
320             foreach (KeyValuePair<string, object> value in values)
321             {
322                 if (innerKeys.Add(value.Key))
323                     changedResources.Add(value);
324             }
325             if (changedResources.Count != 0)
326                 OnResourcesChanged(changedResources);
327         }
328
329         /// <summary>
330         /// Some type which inherit from Container overwrite the Add/Remove, so the getter of children should also be overwrite.
331         /// </summary>
332         /// <returns></returns>
333         [EditorBrowsable(EditorBrowsableState.Never)]
334         protected virtual List<View> GetChildren()
335         {
336             return childViews;
337         }
338
339         /// <summary>
340         /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
341         /// </summary>
342         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
343         [EditorBrowsable(EditorBrowsableState.Never)]
344         protected override void OnBindingContextChanged()
345         {
346             var gotBindingContext = false;
347             object bc = null;
348
349             for (var index = 0; index < Children.Count; index++)
350             {
351                 Element child = Children[index];
352
353                 if (!gotBindingContext)
354                 {
355                     bc = BindingContext;
356                     gotBindingContext = true;
357                 }
358
359                 SetChildInheritedBindingContext(child, bc);
360             }
361             base.OnBindingContextChanged();
362         }
363
364         private void DisposeIncludeChildren()
365         {
366             foreach (var child in Children)
367             {
368                 child.DisposeIncludeChildren();
369             }
370
371             if (IsCreateByXaml)
372             {
373                 Dispose();
374                 ClearBinding();
375             }
376         }
377
378         private void CopyChildren(Container other)
379         {
380             var childrenOfOtherView = new List<View>();
381
382             foreach (var child in other.Children)
383             {
384                 childrenOfOtherView.Add(child);
385             }
386
387             foreach (var child in childrenOfOtherView)
388             {
389                 Add(child);
390             }
391         }
392
393         private void ReplaceChild(View child, View newChild)
394         {
395             int indexOfView = Children.FindIndex((View v) => { return v == child; });
396
397             var childrenNeedtoReAdd = new Stack<View>();
398
399             for (int i = Children.Count - 1; i > indexOfView; i--)
400             {
401                 childrenNeedtoReAdd.Push(Children[i]);
402                 Remove(Children[i]);
403             }
404
405             Remove(child);
406
407             childrenNeedtoReAdd.Push(newChild);
408
409             while (0 < childrenNeedtoReAdd.Count)
410             {
411                 Add(childrenNeedtoReAdd.Pop());
412             }
413         }
414
415         private void ReplaceBindingElementInWholeTree(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
416         {
417             if (IsCreateByXaml)
418             {
419                 ReplaceBindingElement(oldNameScope, newNameScope);
420
421                 foreach (var child in Children)
422                 {
423                     child.ReplaceBindingElementInWholeTree(oldNameScope, newNameScope);
424                 }
425             }
426         }
427     }
428 } // namespace Tizen.NUI