[NUI] Support Shadow Size Extents (#1335)
authorJiyun Yang <ji.yang@samsung.com>
Tue, 21 Jan 2020 03:49:36 +0000 (12:49 +0900)
committerGitHub <noreply@github.com>
Tue, 21 Jan 2020 03:49:36 +0000 (12:49 +0900)
Signed-off-by: Jiyun Yang <ji.yang@samsung.com>
src/Tizen.NUI/src/public/BaseComponents/Style/Selector.cs
src/Tizen.NUI/src/public/BaseComponents/View.cs
src/Tizen.NUI/src/public/BaseComponents/ViewBindableProperty.cs
src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs
src/Tizen.NUI/src/public/ViewProperty/ICloneable.cs
src/Tizen.NUI/src/public/ViewProperty/ImageShadow.cs
src/Tizen.NUI/src/public/ViewProperty/Shadow.cs
src/Tizen.NUI/src/public/ViewProperty/ShadowBase.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/PopupSample.cs

index f5d7d32..bd2096b 100755 (executable)
@@ -37,6 +37,19 @@ namespace Tizen.NUI.BaseComponents
             return selector;
         }
 
+        /// Default Contructor
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Selector()
+        {
+        }
+
+        /// Contructor with T
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Selector(T value) : this()
+        {
+            All = value;
+        }
+
         /// <summary>
         /// All State.
         /// </summary>
@@ -199,6 +212,22 @@ namespace Tizen.NUI.BaseComponents
             SelectedFocused = selector.SelectedFocused;
             Other = selector.Other;
         }
+
+        internal void Clone<U>(Selector<U> other) where U : T, Tizen.NUI.Internal.ICloneable
+        {
+            // TODO Apply constraint to the Selector (not to Clone method)
+
+            All = (T)(other.All)?.Clone();
+            Normal = (T)(other.Normal)?.Clone();
+            Focused = (T)(other.Focused)?.Clone();
+            Pressed = (T)(other.Pressed)?.Clone();
+            Disabled = (T)(other.Disabled)?.Clone();
+            Selected = (T)(other.Selected)?.Clone();
+            DisabledSelected = (T)(other.DisabledSelected)?.Clone();
+            DisabledFocused = (T)(other.DisabledFocused)?.Clone();
+            SelectedFocused = (T)(other.SelectedFocused)?.Clone();
+            Other = (T)(other.Other)?.Clone();
+        }
     }
 
     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
@@ -240,7 +269,79 @@ namespace Tizen.NUI.BaseComponents
         private BindableProperty targetBindableProperty;
     }
 
-    internal static class SelectorHelper<T> where T : class, Tizen.NUI.ICloneable
+    /// <summary>
+    /// A class that helps binding a non-selector property in View to selector property in ViewStyle.
+    /// </summary>
+    internal class ViewSelector<T> where T : class, Tizen.NUI.Internal.ICloneable
+    {
+        private Selector<T> selector;
+        private View view;
+        private View.ControlStateChangesDelegate controlStateChanged;
+
+        internal ViewSelector(View view, View.ControlStateChangesDelegate controlStateChanged)
+        {
+            if (view == null || controlStateChanged == null)
+            {
+                throw new global::System.ArgumentNullException();
+            }
+            this.view = view;
+            this.controlStateChanged = controlStateChanged;
+            this.selector = null;
+        }
+
+        internal T GetValue()
+        {
+            return selector == null ? null : selector.GetValue(view.ControlState);
+        }
+
+        internal void Clone(object value)
+        {
+            bool hadMultiValue = HasMultiValue();
+            var type = value?.GetType();
+
+            if (type == typeof(T))
+            {
+                selector = new Selector<T>();
+                selector.All = (T)((T)value).Clone();
+            }
+            else if (type == typeof(Selector<T>))
+            {
+                selector = new Selector<T>();
+                selector.Clone<T>((Selector<T>)value);
+            }
+            else
+            {
+                selector = null;
+            }
+
+            if (hadMultiValue != HasMultiValue())
+            {
+                if (hadMultiValue) view.ControlStateChangeEvent -= controlStateChanged;
+                else view.ControlStateChangeEvent += controlStateChanged;
+            }
+        }
+
+        internal void Clear()
+        {
+            if (HasMultiValue())
+            {
+                view.ControlStateChangeEvent -= controlStateChanged;
+            }
+            selector = null;
+        }
+
+        internal bool IsEmpty()
+        {
+            return selector == null;
+        }
+
+        private bool HasMultiValue()
+        {
+            return (selector != null && selector.All == null);
+        }
+    }
+
+    internal static class SelectorHelper<T> where T : class, Tizen.NUI.Internal.ICloneable
     {
         /// <summary>
         /// For the object type of T or Selector T, convert it to Selector T and return the cloned one.
@@ -253,15 +354,13 @@ namespace Tizen.NUI.BaseComponents
             if (type == typeof(Selector<T>))
             {
                 var result = new Selector<T>();
-                result.Clone((Selector<T>)value);
+                result.Clone<T>((Selector<T>)value);
                 return result;
             }
 
             if (type == typeof(T))
             {
-                var result = new Selector<T>();
-                result.Clone((T)value);
-                return result;
+                return new Selector<T>((T)((T)value).Clone());
             }
 
             return null;
index c513026..502a927 100755 (executable)
@@ -59,9 +59,9 @@ namespace Tizen.NUI.BaseComponents
         private string[] transitionNames;
         private Rectangle backgroundImageBorder;
 
-        private ImageShadow imageShadow;
+        private ViewSelector<ImageShadow> imageShadow;
 
-        private Shadow boxShadow;
+        private ViewSelector<Shadow> boxShadow;
 
         internal Size2D sizeSetExplicitly = new Size2D(); // Store size set by API, will be used in place of NaturalSize if not set.
 
index 1b54fac..85eb288 100755 (executable)
@@ -1383,14 +1383,18 @@ namespace Tizen.NUI.BaseComponents
         public static readonly BindableProperty ImageShadowProperty = BindableProperty.Create(nameof(ImageShadow), typeof(ImageShadow), typeof(View), null, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var view = (View)bindable;
-            view.imageShadow = SelectorHelper<ImageShadow>.Clone(newValue, view);
-            Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), ImageShadow.ToPropertyValue(view.imageShadow));
+            bool hadShadowExtents = view.HasShadowExtents();
 
-            if (view.imageShadow != null) view.boxShadow = null;
+            (view.imageShadow ?? (view.imageShadow = new ViewSelector<ImageShadow>(view, view.OnControlStateChangedForShadow))).Clone(newValue);
+            Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), ImageShadow.ToPropertyValue(view.imageShadow.GetValue(), view));
+
+            view.boxShadow?.Clear();
+            view.UpdateRelayoutCallbackForShadow(hadShadowExtents);
         },
         defaultValueCreator: (bindable) =>
         {
-            return ((View)bindable).imageShadow;
+            var view = (View)bindable;
+            return view.imageShadow?.GetValue();
         });
 
         /// <summary>
@@ -1400,14 +1404,18 @@ namespace Tizen.NUI.BaseComponents
         public static readonly BindableProperty BoxShadowProperty = BindableProperty.Create(nameof(BoxShadow), typeof(Shadow), typeof(View), null, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var view = (View)bindable;
-            view.boxShadow = SelectorHelper<Shadow>.Clone(newValue, view);
-            Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), Shadow.ToPropertyValue(view.boxShadow));
+            bool hadShadowExtents = view.HasShadowExtents();
 
-            if (view.boxShadow != null) view.imageShadow = null;
+            (view.boxShadow ?? (view.boxShadow = new ViewSelector<Shadow>(view, view.OnControlStateChangedForShadow))).Clone(newValue);
+            Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), Shadow.ToPropertyValue(view.boxShadow.GetValue(), view));
+
+            view.imageShadow?.Clear();
+            view.UpdateRelayoutCallbackForShadow(hadShadowExtents);
         },
         defaultValueCreator: (bindable) =>
         {
-            return ((View)bindable).boxShadow;
+            var view = (View)bindable;
+            return view.boxShadow?.GetValue();
         });
 
         /// <summary>
index fc35ad3..0321800 100755 (executable)
@@ -1253,5 +1253,42 @@ namespace Tizen.NUI.BaseComponents
         {
             SizeModeFactor = new Vector3(x, y, z);
         }
+
+        private bool HasShadowExtents()
+        {
+            bool verifyImageShadow = imageShadow?.GetValue()?.HasValidSizeExtents() ?? false;
+            bool verifyBoxShadow = boxShadow?.GetValue()?.HasValidSizeExtents() ?? false;
+            return verifyImageShadow || verifyBoxShadow;
+        }
+
+        private void UpdateRelayoutCallbackForShadow(bool hadShadowExtents)
+        {
+            bool hasShadowExtents = HasShadowExtents();
+
+            if (!hadShadowExtents && hasShadowExtents)
+            {
+                Relayout += OnRelayoutForShadow;
+            }
+            else if (hadShadowExtents && !hasShadowExtents)
+            {
+                Relayout -= OnRelayoutForShadow;
+            }
+        }
+
+        private void OnRelayoutForShadow(object sender, global::System.EventArgs e)
+        {
+            UpdateShadowVisual();
+        }
+
+        private void OnControlStateChangedForShadow(View obj, NUI.Components.ControlStates state)
+        {
+            UpdateShadowVisual();
+        }
+
+        private void UpdateShadowVisual()
+        {
+            ShadowBase shadow = (boxShadow != null && !boxShadow.IsEmpty()) ? (ShadowBase)boxShadow.GetValue() : (ShadowBase)imageShadow?.GetValue();
+            Tizen.NUI.Object.SetProperty(swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), ShadowBase.ToPropertyValue(shadow, this));
+        }
     }
 }
index 4304e7e..83ef182 100644 (file)
@@ -17,7 +17,7 @@
 
 using System.ComponentModel;
 
-namespace Tizen.NUI
+namespace Tizen.NUI.Internal
 {
 
     /// <summary>
index 9032365..bd60a24 100644 (file)
@@ -24,8 +24,10 @@ namespace Tizen.NUI
     /// The Shadow composed of image for View
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class ImageShadow : ShadowBase, Tizen.NUI.ICloneable
+    public class ImageShadow : ShadowBase, Tizen.NUI.Internal.ICloneable
     {
+        private static readonly Rectangle noBorder = new Rectangle();
+
         private string url;
 
         private Rectangle border;
@@ -69,7 +71,7 @@ namespace Tizen.NUI
         {
             return new ImageShadow() {
                 Offset = offset,
-                Scale = scale,
+                Extents = extents,
                 Url = url,
                 Border = border
             };
@@ -84,7 +86,7 @@ namespace Tizen.NUI
         {
             return instance == null ? null : new ImageShadow() {
                 Offset = instance.offset,
-                Scale = instance.scale,
+                Extents = instance.extents,
                 Url = instance.url,
                 Border = instance.border
             };
@@ -138,7 +140,7 @@ namespace Tizen.NUI
             }
             set
             {
-                border = value == null? null : new Rectangle(OnBorderChanged, value);
+                border = new Rectangle(OnBorderChanged, value ?? noBorder);
                 UpdateBorder();
             }
         }
index 6f79f92..58bd45f 100644 (file)
@@ -24,7 +24,7 @@ namespace Tizen.NUI
     /// The platform provided shadow drawing for View
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class Shadow : ShadowBase, Tizen.NUI.ICloneable
+    public class Shadow : ShadowBase, Tizen.NUI.Internal.ICloneable
     {
         private static readonly Color noColor = new Color(0, 0, 0, 0);
 
@@ -75,7 +75,7 @@ namespace Tizen.NUI
         {
             return new Shadow() {
                 Offset = offset,
-                Scale = scale,
+                Extents = extents,
                 Color = color,
                 BlurRadius = blurRadius
             };
@@ -110,7 +110,7 @@ namespace Tizen.NUI
             }
             set
             {
-                color = value == null? null : new Color(OnColorChanged, value);
+                color = new Color(OnColorChanged, value ?? noColor);
                 UpdateColor();
             }
         }
index 06ac918..a2428a7 100644 (file)
@@ -33,7 +33,7 @@ namespace Tizen.NUI
 
         private static readonly Vector2 noOffset = new Vector2(0, 0);
 
-        private static readonly Vector2 noScale = new Vector2(1, 1);
+        private static readonly Vector2 noExtents = new Vector2(0, 0);
 
         private static readonly Vector2 absoluteTransformPolicy = new Vector2((int)VisualTransformPolicyType.Absolute, (int)VisualTransformPolicyType.Absolute);
 
@@ -44,9 +44,9 @@ namespace Tizen.NUI
         protected internal Vector2 offset;
 
         /// <summary>
-        /// The size value in scale
+        /// The size value in extension length
         /// </summary>
-        protected internal Vector2 scale;
+        protected internal Vector2 extents;
 
         /// <summary>
         /// The output property map
@@ -57,7 +57,7 @@ namespace Tizen.NUI
         /// Constructor
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public ShadowBase() : this(noOffset, noScale)
+        public ShadowBase() : this(noOffset, noExtents)
         {
         }
 
@@ -65,27 +65,22 @@ namespace Tizen.NUI
         /// Copy Constructor
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public ShadowBase(ShadowBase other) : this(other.offset, other.scale)
+        public ShadowBase(ShadowBase other) : this(other.offset, other.extents)
         {
         }
 
         /// <summary>
         /// Constructor
         /// </summary>
-        protected internal ShadowBase(Vector2 offset, Vector2 scale)
+        private ShadowBase(Vector2 offset, Vector2 extents)
         {
             propertyMap = new PropertyMap();
 
             Offset = offset;
-            Scale = scale;
+            Extents = extents;
         }
 
-        private void OnOffsetChanged(float x, float y)
-        {
-            OnPropertyChanged?.Invoke(this);
-        }
-
-        private void OnScaleChanged(float widht, float height)
+        private void OnOffsetOrSizeChanged(float x, float y)
         {
             OnPropertyChanged?.Invoke(this);
         }
@@ -102,7 +97,7 @@ namespace Tizen.NUI
             }
             set
             {
-                offset = new Vector2(OnOffsetChanged, value ?? noOffset);
+                offset = new Vector2(OnOffsetOrSizeChanged, value ?? noOffset);
                 OnPropertyChanged?.Invoke(this);
             }
         }
@@ -111,21 +106,40 @@ namespace Tizen.NUI
         /// The value indicates percentage of the container size. <br />
         /// e.g. (0.5f, 1.0f) means 50% of the container's width and 100% of container's height.
         /// </summary>
+        /// <Remarks> It is not guaranteed that this would work well. It will be removed in the next version.</Remarks>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public Vector2 Scale
         {
             get
             {
-                return scale;
+                return new Vector2();
+            }
+            set
+            {
+            }
+        }
+
+        /// <summary>
+        /// The shadow will extend its size by specified amount of length.<br />
+        /// If the value is negative then the shadow will shrink.
+        /// For example, when View's size is (100, 100) and the Shadow's Extents is (5, -5),
+        /// the output shadow will have size (105, 95).
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Vector2 Extents
+        {
+            get
+            {
+                return extents;
             }
             set
             {
-                scale = new Vector2(OnScaleChanged, value ?? noScale);
+                extents = new Vector2(OnOffsetOrSizeChanged, value ?? noExtents);
                 OnPropertyChanged?.Invoke(this);
             }
         }
 
-        private PropertyValue GetTransformMap()
+        private PropertyValue GetTransformMap(BaseComponents.View attachedView)
         {
             var transformMap = new PropertyMap();
 
@@ -135,9 +149,13 @@ namespace Tizen.NUI
                 transformMap[(int)VisualTransformPropertyType.Offset] = PropertyValue.CreateWithGuard(offset);
             }
 
-            if (!scale.Equals(noScale))
+            if (!extents.Equals(noExtents))
             {
-                transformMap[(int)VisualTransformPropertyType.Size] = PropertyValue.CreateWithGuard(scale);
+                var viewSize = new Vector2(attachedView.GetRelayoutSize(DimensionType.Width), attachedView.GetRelayoutSize(DimensionType.Height));
+                var shadowSize = viewSize + extents;
+
+                transformMap[(int)VisualTransformPropertyType.SizePolicy] = new PropertyValue(absoluteTransformPolicy);
+                transformMap[(int)VisualTransformPropertyType.Size] = PropertyValue.CreateWithGuard(shadowSize);
             }
 
             return transformMap.Count() == 0 ? new PropertyValue() : new PropertyValue(transformMap);
@@ -145,14 +163,19 @@ namespace Tizen.NUI
 
         abstract internal bool IsValid();
 
-        static internal PropertyValue ToPropertyValue(ShadowBase instance)
+        internal bool HasValidSizeExtents()
+        {
+            return IsValid() && !extents.Equals(noExtents);
+        }
+
+        static internal PropertyValue ToPropertyValue(ShadowBase instance, BaseComponents.View attachedView)
         {
             if (instance == null || !instance.IsValid())
             {
                 return new PropertyValue();
             }
 
-            instance.propertyMap[Visual.Property.Transform] = instance.GetTransformMap();
+            instance.propertyMap[Visual.Property.Transform] = instance.GetTransformMap(attachedView);
 
             return new PropertyValue(instance.propertyMap);
         }
index f0bcbac..56106cc 100755 (executable)
@@ -68,8 +68,7 @@ namespace Tizen.NUI.Samples
                 Url = CommonResource.GetFHResourcePath() + "11. Popup/popup_background_shadow.png",
                 Border = new Rectangle(24, 24, 24, 24),
                 Offset = new Vector2(-24, -24),
-                // TODO We do not have shadow extents now, so replace it to scale value
-                Scale = new Vector2(1080f/1032f, 448f/400f),
+                Extents = new Vector2(48, 48),
             };
 
             // Background
@@ -124,8 +123,7 @@ namespace Tizen.NUI.Samples
                     Url = CommonResource.GetFHResourcePath() + "11. Popup/popup_background_shadow.png",
                     Border = new Rectangle(24, 24, 24, 24),
                     Offset = new Vector2(-24, -24),
-                    // TODO We do not have shadow extents now, so replace it to scale value
-                    Scale = new Vector2(1080f/1032f, 448f/400f),
+                    Extents = new Vector2(48, 48),
                 },
                 Title = new TextLabelStyle
                 {