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