[NUI] Make BindableObject.IsBound public.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / BindableObject.cs
index 60a89d8..c656697 100755 (executable)
@@ -1,5 +1,5 @@
-/*
- * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+/*
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -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;
 
@@ -36,44 +37,14 @@ namespace Tizen.NUI.Binding
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public static readonly BindableProperty BindingContextProperty =
-            BindableProperty.Create(nameof(BindingContext), typeof(object), typeof(BindableObject), null, propertyChanged: (BindableProperty.BindingPropertyChangedDelegate)((bindable, oldValue, newValue) =>
-            {
-                var bindableObject = (BindableObject)bindable;
-                if (newValue != null)
-                {
-                    bindableObject.bindingContext = newValue;
-                    bindableObject.FlushBinding();
+            BindableProperty.Create(nameof(BindingContext), typeof(object), typeof(BindableObject),  default(object), BindingMode.OneWay, null, BindingContextPropertyChanged,
+            null, null, BindingContextPropertyBindingChanging);
 
-                    if (newValue is BindableObject targetBindableObject)
-                    {
-                        targetBindableObject.IsBinded = true;
-                    }
-                }
-            }),
-            defaultValueCreator: (BindableProperty.CreateDefaultValueDelegate)((bindable) =>
-            {
-                if (null != bindable.bindingContext)
-                {
-                    return bindable.bindingContext;
-                }
-
-                if (bindable is Container container)
-                {
-                    return container.Parent?.BindingContext;
-                }
-                else
-                {
-                    return null;
-                }
-            }));
-
-        readonly List<BindablePropertyContext> properties = new List<BindablePropertyContext>(4);
+        readonly Dictionary<BindableProperty, BindablePropertyContext> properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
 
         bool applying;
         object inheritedContext;
 
-        private object bindingContext;
-
         /// <summary>
         /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject.
         /// </summary>
@@ -85,6 +56,12 @@ namespace Tizen.NUI.Binding
             set { SetValue(BindingContextProperty, value); }
         }
 
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int LineNumber { get; set; } = -1;
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int LinePosition { get; set; } = -1;
+
         void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
         {
             SetDynamicResource(property, key, false);
@@ -115,20 +92,44 @@ namespace Tizen.NUI.Binding
                 {
                     nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
 
-                    if (null != bindableProperty)
+                    if (null != bindableProperty && other.IsSet(bindableProperty))
                     {
                         object value = other.GetValue(bindableProperty);
-
-                        if (null != value)
-                        {
-                            SetValue(keyValuePair.Value, value);
-                        }
+                        InternalSetValue(keyValuePair.Value, value);
                     }
                 }
             }
         }
 
         /// <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.
@@ -188,6 +189,11 @@ namespace Tizen.NUI.Binding
             if (property == null)
                 throw new ArgumentNullException(nameof(property));
 
+            if (!IsBound && property.ValueGetter != null)
+            {
+                return property.ValueGetter(this);
+            }
+
             BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
 
             if (context == null)
@@ -231,18 +237,56 @@ namespace Tizen.NUI.Binding
             SetBinding(targetProperty, binding, false);
         }
 
-        private bool isCreateByXaml = false;
-        /// Only used by the IL of xaml, will never changed to not hidden.
+        /// Internal used, will never changed to not hidden.
         [EditorBrowsable(EditorBrowsableState.Never)]
         public virtual bool IsCreateByXaml
         {
-            get
-            {
-                return isCreateByXaml;
-            }
-            set
+            get;
+            set;
+        }
+
+        /// This will be public opened in next ACR.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void EnforceNotifyBindedInstance(BindableProperty property)
+        {
+            if (null != property)
             {
-                isCreateByXaml = value;
+                BindablePropertyContext context = GetOrCreateContext(property);
+                BindingBase binding = context.Binding;
+
+                var currentlyApplying = applying;
+
+                if (binding != null && !currentlyApplying)
+                {
+                    applying = true;
+                    binding.Apply(true);
+                    applying = false;
+                }
+
+                OnPropertyChanged(property.PropertyName);
+
+                PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
+
+                if (null != propertyGroup)
+                {
+                    foreach (var relativeProperty in propertyGroup)
+                    {
+                        if (relativeProperty != property)
+                        {
+                            var relativeContext = GetOrCreateContext(relativeProperty);
+                            var relativeBinding = relativeContext.Binding;
+
+                            if (null != relativeBinding)
+                            {
+                                applying = true;
+                                relativeBinding.Apply(true);
+                                applying = false;
+                            }
+
+                            OnPropertyChanged(relativeProperty.PropertyName);
+                        }
+                    }
+                }
             }
         }
 
@@ -256,7 +300,13 @@ namespace Tizen.NUI.Binding
         [EditorBrowsable(EditorBrowsableState.Never)]
         public void SetValue(BindableProperty property, object value)
         {
-            if (true == IsBinded)
+            InternalSetValue(property, value);
+            ChangedPropertiesSetExcludingStyle.Add(property);
+        }
+
+        internal void InternalSetValue(BindableProperty property, object value)
+        {
+            if (true == IsBound)
             {
                 SetValue(property, value, false, true);
             }
@@ -267,38 +317,33 @@ namespace Tizen.NUI.Binding
                     throw new ArgumentNullException(nameof(property));
                 }
 
-                if (null == property.DefaultValueCreator)
+                object oldvalue = null;
+                BindablePropertyContext context = GetOrCreateContext(property);
+                if (null != context)
                 {
-                    BindablePropertyContext context = GetOrCreateContext(property);
-                    if (null != context)
-                    {
-                        context.Value = value;
-                    }
+                    context.Attributes |= BindableContextAttributes.IsManuallySet;
+                    oldvalue = context.Value;
+                    context.Value = value;
                 }
 
-                property.PropertyChanged?.Invoke(this, null, value);
+                property.PropertyChanged?.Invoke(this, oldvalue, value);
 
                 OnPropertyChanged(property.PropertyName);
                 OnPropertyChangedWithData(property);
             }
         }
 
-        internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
+        private HashSet<BindableProperty> changedPropertiesSetExcludingStyle;
+        internal protected HashSet<BindableProperty> ChangedPropertiesSetExcludingStyle
         {
-            if (property == null)
-                throw new ArgumentNullException(nameof(property));
-
-            if (true == isCreateByXaml)
+            get
             {
-                if (property.IsReadOnly)
-                    throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
+                if (null == changedPropertiesSetExcludingStyle)
+                {
+                    changedPropertiesSetExcludingStyle = new HashSet<BindableProperty>();
+                }
 
-                SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
-                    SetValuePrivateFlags.ManuallySet | SetValuePrivateFlags.CheckAccess, true);
-            }
-            else
-            {
-                property.PropertyChanged?.Invoke(this, null, value);
+                return changedPropertiesSetExcludingStyle;
             }
         }
 
@@ -424,9 +469,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;
 
@@ -453,10 +497,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;
@@ -491,10 +533,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;
@@ -533,9 +573,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;
@@ -578,7 +617,7 @@ namespace Tizen.NUI.Binding
             if (fromStyle && !CanBeSetFromStyle(targetProperty))
                 return;
 
-            IsBinded = true;
+            IsBound = true;
 
             var context = GetOrCreateContext(targetProperty);
             if (fromStyle)
@@ -643,7 +682,7 @@ namespace Tizen.NUI.Binding
 
         internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
         {
-            SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None, false);
+            SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None);
         }
 
         /// <summary>
@@ -655,10 +694,10 @@ namespace Tizen.NUI.Binding
         [EditorBrowsable(EditorBrowsableState.Never)]
         internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
         {
-            SetValueCore(property, value, attributes, SetValuePrivateFlags.Default, false);
+            SetValueCore(property, value, attributes, SetValuePrivateFlags.Default);
         }
 
-        internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, bool forceSendChangeSignal)
+        internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
         {
             bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
             bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
@@ -716,7 +755,7 @@ namespace Tizen.NUI.Binding
             else
             {
                 context.Attributes |= BindableContextAttributes.IsBeingSet;
-                SetValueActual(property, context, value, currentlyApplying, forceSendChangeSignal, attributes, silent);
+                SetValueActual(property, context, value, currentlyApplying, attributes, silent);
 
                 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
                 if (delayQueue != null)
@@ -724,7 +763,7 @@ namespace Tizen.NUI.Binding
                     while (delayQueue.Count > 0)
                     {
                         SetValueArgs s = delayQueue.Dequeue();
-                        SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, forceSendChangeSignal, s.Attributes);
+                        SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, s.Attributes);
                     }
 
                     context.DelayedSetters = null;
@@ -736,7 +775,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];
@@ -752,12 +791,28 @@ namespace Tizen.NUI.Binding
             }
         }
 
-        internal bool IsBinded
+        /// <summary>
+        /// Check if object is bound or not.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool IsBound
         {
             get;
             set;
         } = false;
 
+        static void BindingContextPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+        {
+            bindable.inheritedContext = null;
+            bindable.ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
+            bindable.OnBindingContextChanged();
+
+            if (newvalue is BindableObject targetBindableObject)
+            {
+                targetBindableObject.IsBound = true;
+            }
+        }
+
         static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
         {
             object context = bindable.inheritedContext;
@@ -822,24 +877,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)
@@ -849,7 +892,11 @@ namespace Tizen.NUI.Binding
             {
                 context = CreateAndAddContext(property);
             }
-            else if (property.DefaultValueCreator != null)
+            else if (property.ValueGetter != null)
+            {
+                context.Value = property.ValueGetter(this); //Update Value from dali
+            }//added by xiaohui.fang
+            else if (property.DefaultValueCreator != null) //This will be removed in the future.
             {
                 context.Value = property.DefaultValueCreator(this); //Update Value from dali
             }//added by xb.teng
@@ -878,11 +925,10 @@ namespace Tizen.NUI.Binding
                 return;
 
             SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
-                (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0),
-                false);
+                (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0));
         }
 
-        void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, bool forceSendChangeSignal, SetValueFlags attributes, bool silent = false)
+        void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
         {
             object original = context.Value;
             bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
@@ -921,71 +967,36 @@ namespace Tizen.NUI.Binding
 
             PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
 
-            if (!silent)
+            if (!silent && (!same || raiseOnEqual))
             {
-                if ((!same || raiseOnEqual))
-                {
-                    property.PropertyChanged?.Invoke(this, original, value);
+                property.PropertyChanged?.Invoke(this, original, value);
 
-                    if (binding != null && !currentlyApplying)
-                    {
-                        applying = true;
-                        binding.Apply(true);
-                        applying = false;
-                    }
-
-                    OnPropertyChanged(property.PropertyName);
-
-                    if (null != propertyGroup)
-                    {
-                        foreach (var relativeProperty in propertyGroup)
-                        {
-                            if (relativeProperty != property)
-                            {
-                                var relativeContext = GetOrCreateContext(relativeProperty);
-                                var relativeBinding = relativeContext.Binding;
-
-                                if (null != relativeBinding)
-                                {
-                                    applying = true;
-                                    relativeBinding.Apply(true);
-                                    applying = false;
-                                }
-
-                                OnPropertyChanged(relativeProperty.PropertyName);
-                            }
-                        }
-                    }
-                }
-                else if (true == same && true == forceSendChangeSignal)
+                if (binding != null && !currentlyApplying)
                 {
-                    if (binding != null && !currentlyApplying)
-                    {
-                        applying = true;
-                        binding.Apply(true);
-                        applying = false;
-                    }
+                    applying = true;
+                    binding.Apply(true);
+                    applying = false;
+                }
 
-                    OnPropertyChanged(property.PropertyName);
+                OnPropertyChanged(property.PropertyName);
 
-                    if (null != propertyGroup)
+                if (null != propertyGroup)
+                {
+                    foreach (var relativeProperty in propertyGroup)
                     {
-                        foreach (var relativeProperty in propertyGroup)
+                        if (relativeProperty != property)
                         {
-                            if (relativeProperty != property)
-                            {
-                                var relativeContext = GetOrCreateContext(relativeProperty);
-                                var relativeBinding = relativeContext.Binding;
-
-                                if (null != relativeBinding)
-                                {
-                                    applying = true;
-                                    relativeBinding.Apply(true);
-                                    applying = false;
-                                }
+                            var relativeContext = GetOrCreateContext(relativeProperty);
+                            var relativeBinding = relativeContext.Binding;
 
-                                OnPropertyChanged(relativeProperty.PropertyName);
+                            if (null != relativeBinding)
+                            {
+                                applying = true;
+                                relativeBinding.Apply(true);
+                                applying = false;
                             }
+
+                            OnPropertyChanged(relativeProperty.PropertyName);
                         }
                     }
                 }
@@ -1047,30 +1058,53 @@ namespace Tizen.NUI.Binding
             }
         }
 
-        internal void AddChildBindableObject(BindableObject child)
+        internal void ReplaceBindingElement(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
         {
-            if (null != child)
+            var xElementToNameOfOld = new Dictionary<object, string>();
+
+            foreach (var pair in oldNameScope)
             {
-                children.Add(child);
-                child.FlushBinding();
+                xElementToNameOfOld.Add(pair.Value, pair.Key);
             }
-        }
 
-        internal void RemoveChildBindableObject(BindableObject child)
-        {
-            children.Remove(child);
-        }
+            foreach (var property in properties)
+            {
+                if (property.Value.Binding is Binding binding && null != binding.Source)
+                {
+                    string xName;
+                    xElementToNameOfOld.TryGetValue(binding.Source, out xName);
 
-        private List<BindableObject> children = new List<BindableObject>();
+                    if (null != xName)
+                    {
+                        var newObject = newNameScope[xName];
+                        binding.Unapply();
+                        binding.Source = newObject;
+                        SetBinding(property.Key, binding);
+                    }
+                }
+            }
 
-        private void FlushBinding()
-        {
-            ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
-            OnBindingContextChanged();
+            if (null != BindingContext)
+            {
+                string xName;
+                xElementToNameOfOld.TryGetValue(BindingContext, out xName);
 
-            foreach (var child in children)
+                if (null != xName)
+                {
+                    var newObject = newNameScope[xName];
+                    BindingContext = newObject;
+                }
+            }
+        }
+
+        internal void ClearBinding()
+        {
+            foreach (var property in properties)
             {
-                child.FlushBinding();
+                if (null != property.Value.Binding)
+                {
+                    property.Value.Binding.Unapply();
+                }
             }
         }
     }