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