[NUI] Add API so that user can copy properties, bindings from other container
authorFang Xiaohui <xiaohui.fang@samsung.com>
Wed, 8 Dec 2021 06:40:11 +0000 (14:40 +0800)
committerSeoyeon2Kim <34738918+Seoyeon2Kim@users.noreply.github.com>
Tue, 14 Dec 2021 09:04:07 +0000 (18:04 +0900)
src/Tizen.NUI/src/public/Common/Container.cs
src/Tizen.NUI/src/public/XamlBinding/BindableObject.cs
src/Tizen.NUI/src/public/XamlBinding/Internals/NameScope.cs

index 799251c..b173715 100755 (executable)
@@ -17,7 +17,9 @@
 
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Binding.Internals;
 
 namespace Tizen.NUI
 {
@@ -80,6 +82,92 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// Copy all properties, bindings.
+        /// Copy children without xName from other container, copy all properties, bindings of children with xName.
+        /// </summary>
+        /// <param name="other"></param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void CopyAndKeepXNameInstance(Container other)
+        {
+            CopyFrom(other);
+
+            var nameScopeOfOther = NameScope.GetNameScope(other) as NameScope;
+            var nameScope = NameScope.GetNameScope(this) as NameScope;
+
+            if (null == nameScopeOfOther)
+            {
+                if (null != nameScope)
+                {
+                    return;
+                }
+            }
+            else if (!nameScopeOfOther.Equal(nameScope))
+            {
+                return;
+            }
+
+            var xNameToElementsOfOther = nameScopeOfOther.NameToElement;
+            var xNameToElements = nameScope.NameToElement;
+
+            if (null != xNameToElements)
+            {
+                foreach (var pair in xNameToElements)
+                {
+                    if (pair.Value is View view)
+                    {
+                        view.Parent?.Remove(view);
+                    }
+                }
+            }
+
+            for (int i = Children.Count - 1; i >= 0; i--)
+            {
+                var child = GetChildAt((uint)i);
+                Remove(child);
+
+                child.DisposeIncludeChildren();
+            }
+
+            CopyChildren(other);
+
+            if (null != xNameToElementsOfOther)
+            {
+                foreach (var pair in xNameToElementsOfOther)
+                {
+                    if (pair.Value is View view)
+                    {
+                        var parent = view.Parent;
+
+                        if (null != parent)
+                        {
+                            if (null != xNameToElements)
+                            {
+                                var holdedXElements = xNameToElements[pair.Key] as View;
+                                holdedXElements.CopyBindingRelationShip(view);
+                                holdedXElements.CopyFrom(view);
+
+                                parent.ReplaceChild(view, holdedXElements);
+                            }
+                        }
+                    }
+                }
+            }
+
+            ReplaceBindingElementInWholeTree(xNameToElementsOfOther, xNameToElements);
+
+            if (null != xNameToElementsOfOther)
+            {
+                foreach (var pair in xNameToElementsOfOther)
+                {
+                    if (pair.Value is View view)
+                    {
+                        view.Dispose();
+                    }
+                }
+            }
+        }
+
+        /// <summary>
         /// Adds a child view to this Container.
         /// </summary>
         /// <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>
@@ -125,5 +213,69 @@ namespace Tizen.NUI
         public abstract UInt32 GetChildCount();
 
         internal abstract View FindCurrentChildById(uint id);
+
+        private void DisposeIncludeChildren()
+        {
+            foreach (var child in Children)
+            {
+                child.DisposeIncludeChildren();
+            }
+
+            if (IsCreateByXaml)
+            {
+                Dispose();
+                ClearBinding();
+            }
+        }
+
+        private void CopyChildren(Container other)
+        {
+            var childrenOfOtherView = new List<View>();
+
+            foreach (var child in other.Children)
+            {
+                childrenOfOtherView.Add(child);
+            }
+
+            foreach (var child in childrenOfOtherView)
+            {
+                Add(child);
+            }
+        }
+
+        private void ReplaceChild(View child, View newChild)
+        {
+            int indexOfView = Children.FindIndex((View v) => { return v == child; });
+
+            var childrenNeedtoReAdd = new Stack<View>();
+
+            for (int i = Children.Count - 1; i > indexOfView; i--)
+            {
+                childrenNeedtoReAdd.Push(Children[i]);
+                Remove(Children[i]);
+            }
+
+            Remove(child);
+
+            childrenNeedtoReAdd.Push(newChild);
+
+            while (0 < childrenNeedtoReAdd.Count)
+            {
+                Add(childrenNeedtoReAdd.Pop());
+            }
+        }
+
+        private void ReplaceBindingElementInWholeTree(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
+        {
+            if (IsCreateByXaml)
+            {
+                ReplaceBindingElement(oldNameScope, newNameScope);
+
+                foreach (var child in Children)
+                {
+                    child.ReplaceBindingElementInWholeTree(oldNameScope, newNameScope);
+                }
+            }
+        }
     }
 } // namespace Tizen.NUI
index 6a0a2f8..7b8f413 100755 (executable)
@@ -20,6 +20,7 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Reflection;
+using System.Linq;
 using System.Runtime.CompilerServices;
 using Tizen.NUI.Binding.Internals;
 
@@ -67,7 +68,7 @@ namespace Tizen.NUI.Binding
                 }
             }));
 
-        readonly List<BindablePropertyContext> properties = new List<BindablePropertyContext>(4);
+        readonly Dictionary<BindableProperty, BindablePropertyContext> properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
 
         bool applying;
         object inheritedContext;
@@ -115,7 +116,7 @@ namespace Tizen.NUI.Binding
                 {
                     nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
 
-                    if (null != bindableProperty)
+                    if (null != bindableProperty && (SettedPropeties.Contains(bindableProperty) || other.SettedPropeties.Contains(bindableProperty)))
                     {
                         object value = other.GetValue(bindableProperty);
 
@@ -129,6 +130,34 @@ namespace Tizen.NUI.Binding
         }
 
         /// <summary>
+        /// Copy all binding from other object.
+        /// </summary>
+        /// <param name="other"></param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void CopyBindingRelationShip(BindableObject other)
+        {
+            if (null == other)
+            {
+                return;
+            }
+
+            foreach (var property in properties)
+            {
+                RemoveBinding(property.Key);
+            }
+
+            foreach (var property in other.properties)
+            {
+                if (null != property.Value.Binding)
+                {
+                    var binding = property.Value.Binding;
+                    other.RemoveBinding(property.Key);
+                    SetBinding(property.Key, binding);
+                }
+            }
+        }
+
+        /// <summary>
         /// Raised whenever the BindingContext property changes.
         /// </summary>
         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
@@ -292,6 +321,22 @@ namespace Tizen.NUI.Binding
                 OnPropertyChanged(property.PropertyName);
                 OnPropertyChangedWithData(property);
             }
+
+            SettedPropeties.Add(property);
+        }
+
+        private HashSet<BindableProperty> settedPropeties;
+        private HashSet<BindableProperty> SettedPropeties
+        {
+            get
+            {
+                if (null == settedPropeties)
+                {
+                    settedPropeties = new HashSet<BindableProperty>();
+                }
+
+                return settedPropeties;
+            }
         }
 
         internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
@@ -435,9 +480,8 @@ namespace Tizen.NUI.Binding
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected void UnapplyBindings()
         {
-            for (int i = 0, _propertiesCount = properties.Count; i < _propertiesCount; i++)
+            foreach (var context in properties.Values)
             {
-                BindablePropertyContext context = properties[i];
                 if (context.Binding == null)
                     continue;
 
@@ -464,10 +508,8 @@ namespace Tizen.NUI.Binding
         {
             var values = new object[2];
 
-            for (var i = 0; i < properties.Count; i++)
+            foreach (var context in properties.Values)
             {
-                BindablePropertyContext context = properties[i];
-
                 if (ReferenceEquals(context.Property, property0))
                 {
                     values[0] = context.Value;
@@ -502,10 +544,8 @@ namespace Tizen.NUI.Binding
         {
             var values = new object[3];
 
-            for (var i = 0; i < properties.Count; i++)
+            foreach (var context in properties.Values)
             {
-                BindablePropertyContext context = properties[i];
-
                 if (ReferenceEquals(context.Property, property0))
                 {
                     values[0] = context.Value;
@@ -544,9 +584,8 @@ namespace Tizen.NUI.Binding
         internal object[] GetValues(params BindableProperty[] properties)
         {
             var values = new object[properties.Length];
-            for (var i = 0; i < this.properties.Count; i++)
+            foreach (var context in this.properties.Values)
             {
-                var context = this.properties[i];
                 var index = properties.IndexOf(context.Property);
                 if (index < 0)
                     continue;
@@ -747,7 +786,7 @@ namespace Tizen.NUI.Binding
 
         internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
         {
-            var prop = properties.ToArray();
+            var prop = properties.Values.ToArray();
             for (int i = 0, propLength = prop.Length; i < propLength; i++)
             {
                 BindablePropertyContext context = prop[i];
@@ -833,24 +872,12 @@ namespace Tizen.NUI.Binding
             else
                 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
 
-            properties.Add(context);
+            properties.Add(property, context);
             return context;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        BindablePropertyContext GetContext(BindableProperty property)
-        {
-            List<BindablePropertyContext> propertyList = properties;
-
-            for (var i = 0; i < propertyList.Count; i++)
-            {
-                BindablePropertyContext context = propertyList[i];
-                if (ReferenceEquals(context.Property, property))
-                    return context;
-            }
-
-            return null;
-        }
+        BindablePropertyContext GetContext(BindableProperty property) => properties.TryGetValue(property, out var result) ? result : null;
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         BindablePropertyContext GetOrCreateContext(BindableProperty property)
@@ -1076,6 +1103,56 @@ namespace Tizen.NUI.Binding
             children.Remove(child);
         }
 
+        internal void ReplaceBindingElement(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
+        {
+            var xElementToNameOfOld = new Dictionary<object, string>();
+
+            foreach (var pair in oldNameScope)
+            {
+                xElementToNameOfOld.Add(pair.Value, pair.Key);
+            }
+
+            foreach (var property in properties)
+            {
+                if (property.Value.Binding is Binding binding && null != binding.Source)
+                {
+                    string xName;
+                    xElementToNameOfOld.TryGetValue(binding.Source, out xName);
+
+                    if (null != xName)
+                    {
+                        var newObject = newNameScope[xName];
+                        binding.Unapply();
+                        binding.Source = newObject;
+                        SetBinding(property.Key, binding);
+                    }
+                }
+            }
+
+            if (null != BindingContext)
+            {
+                string xName;
+                xElementToNameOfOld.TryGetValue(BindingContext, out xName);
+
+                if (null != xName)
+                {
+                    var newObject = newNameScope[xName];
+                    BindingContext = newObject;
+                }
+            }
+        }
+
+        internal void ClearBinding()
+        {
+            foreach (var property in properties)
+            {
+                if (null != property.Value.Binding)
+                {
+                    property.Value.Binding.Unapply();
+                }
+            }
+        }
+
         private List<BindableObject> children = new List<BindableObject>();
 
         private void FlushBinding()
index 1823b75..69f9683 100755 (executable)
@@ -32,7 +32,37 @@ namespace Tizen.NUI.Binding.Internals
         [EditorBrowsable(EditorBrowsableState.Never)]
         public static readonly BindableProperty NameScopeProperty = BindableProperty.CreateAttached("NameScope", typeof(INameScope), typeof(NameScope), default(INameScope));
 
-        readonly Dictionary<string, object> names = new Dictionary<string, object>();
+        private readonly Dictionary<string, object> names = new Dictionary<string, object>();
+        internal Dictionary<string, object> NameToElement
+        {
+            get
+            {
+                return names;
+            }
+        }
+
+        internal bool Equal(NameScope other)
+        {
+            if (null == other)
+            {
+                return false;
+            }
+
+            if (names.Count != other.names.Count)
+            {
+                return false;
+            }
+
+            foreach (var pair in names)
+            {
+                if (!other.names.ContainsKey(pair.Key))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
 
         object INameScope.FindByName(string name)
         {