[NUI] Support Refactoring key focusable feature for non-focusable browsing.
authoreverLEEst(SangHyeon Lee) <dltkdgus1764@gmail.com>
Fri, 13 May 2022 04:30:53 +0000 (21:30 -0700)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Fri, 27 May 2022 06:02:37 +0000 (15:02 +0900)
src/Tizen.NUI.Components/Controls/ScrollableBase.cs
src/Tizen.NUI.Components/Controls/ScrollableBaseBindableProperty.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusAllChildrenSample.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusNoneChildrenSample.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSample.cs [moved from test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocusSample.cs with 100% similarity]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSample2.cs [moved from test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocusSample2.cs with 100% similarity]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSparseChildrenSample.cs [new file with mode: 0755]
test/Tizen.NUI.Tests/Tizen.NUI.Components.Devel.Tests/testcase/Controls/TSScrollableBase.cs

index 02f60af..e478e55 100755 (executable)
@@ -777,6 +777,30 @@ namespace Tizen.NUI.Components
             set => noticeAnimationEndBeforePosition = value;
         }
 
+        /// <summary>
+        /// Step scroll move distance.
+        /// Key focus originally moves focusable objects, but in ScrollableBase,
+        /// if focusable object is too far or un-exist and ScrollableBase is focusable,
+        /// it can scroll move itself by key input.
+        /// this value decide how long distance will it moves in one step.
+        /// if any value is not set, step will be moved quater size of ScrollableBase length.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float StepScrollDistance
+        {
+            get
+            {
+                return (float)GetValue(StepScrollDistanceProperty);
+            }
+            set
+            {
+                SetValue(StepScrollDistanceProperty, value);
+                NotifyPropertyChanged();
+            }
+        }
+        private float stepScrollDistance = 0f;
+
+
         // Let's consider more whether this needs to be set as protected.
         private float finalTargetPosition;
 
@@ -1012,6 +1036,35 @@ namespace Tizen.NUI.Components
             AnimateChildTo(ScrollDuration, -targetPosition);
         }
 
+        internal void ScrollToChild(View child, bool anim = false)
+        {
+            if (null == FindDescendantByID(child.ID)) return;
+
+            bool isHorizontal = (ScrollingDirection == Direction.Horizontal);
+
+            float viewScreenPosition = (isHorizontal? ScreenPosition.X : ScreenPosition.Y);
+            float childScreenPosition = (isHorizontal? child.ScreenPosition.X : child.ScreenPosition.Y);
+            float scrollPosition = (isHorizontal? ScrollPosition.X : ScrollPosition.Y);
+            float viewSize = (isHorizontal? SizeWidth : SizeHeight);
+            float childSize = (isHorizontal? child.SizeWidth : child.SizeHeight);
+
+            if (viewScreenPosition > childScreenPosition ||
+                viewScreenPosition + viewSize < childScreenPosition + childSize)
+            {// if object is outside
+                float targetPosition;
+                float dist = viewScreenPosition - childScreenPosition;
+                if (dist > 0)
+                {// if object is upper side
+                    targetPosition = scrollPosition - dist;
+                }
+                else
+                {// if object is down side
+                    targetPosition = scrollPosition - dist + childSize - viewSize;
+                }
+                ScrollTo(targetPosition, anim);
+            }
+        }
+
         private void OnScrollDragStarted()
         {
             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
@@ -1830,62 +1883,85 @@ namespace Tizen.NUI.Components
             }
         }
 
+        internal bool IsChildNearlyVisble(View child, float offset = 0)
+        {
+            if (ScreenPosition.X - offset < child.ScreenPosition.X + child.SizeWidth &&
+                ScreenPosition.X + SizeWidth + offset > child.ScreenPosition.X &&
+                ScreenPosition.Y - offset < child.ScreenPosition.Y + child.SizeHeight &&
+                ScreenPosition.Y + SizeHeight + offset > child.ScreenPosition.Y)
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+
         /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
         {
-            if (currentFocusedView == this)
-            {
-                return null;
-            }
+            bool isHorizontal = (ScrollingDirection == Direction.Horizontal);
+            float targetPosition = -(ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+            float stepDistance = (stepScrollDistance != 0? stepScrollDistance : (isHorizontal ? Size.Width * 0.25f :  Size.Height * 0.25f));
 
             View nextFocusedView = FocusManager.Instance.GetNearestFocusableActor(this, currentFocusedView, direction);
 
             if (nextFocusedView != null)
             {
-                View view = nextFocusedView;
-                while (view.GetParent() is View && view.GetParent() != ContentContainer)
+                if (null != FindDescendantByID(nextFocusedView.ID))
                 {
-                    view = (View)view.GetParent();
-                }
-                if (view.GetParent() == ContentContainer)
-                {
-                    // Check next focused view is inside of visible area.
-                    // If it is not, move scroll position to make it visible.
-                    float left = nextFocusedView.ScreenPosition.X;
-                    float right = nextFocusedView.ScreenPosition.X + nextFocusedView.Size.Width;
-                    float top = nextFocusedView.ScreenPosition.Y;
-                    float bottom = nextFocusedView.ScreenPosition.Y + nextFocusedView.Size.Height;
-
-                    float visibleRectangleLeft = ScreenPosition.X;
-                    float visibleRectangleRight = ScreenPosition.X + Size.Width;
-                    float visibleRectangleTop = ScreenPosition.Y;
-                    float visibleRectangleBottom = ScreenPosition.Y + Size.Height;
-
-                    if (ScrollingDirection == Direction.Horizontal)
+                    if (IsChildNearlyVisble(nextFocusedView, stepDistance) == true)
                     {
-                        if (left < visibleRectangleLeft)
-                        {
-                            ScrollTo(left- ContentContainer.ScreenPosition.X, true);
-                        }
-                        else if (right > visibleRectangleRight)
-                        {
-                            ScrollTo(right - Size.Width - ContentContainer.ScreenPosition.X, true);
-                        }
+                        ScrollToChild(nextFocusedView, true);
                     }
                     else
                     {
-                        if (top < visibleRectangleTop)
+                        if ((isHorizontal && direction == View.FocusDirection.Right) ||
+                            (!isHorizontal && direction == View.FocusDirection.Down))
                         {
-                            ScrollTo(top - ContentContainer.ScreenPosition.Y, true);
+                            targetPosition += stepDistance;
+                            targetPosition = targetPosition > maxScrollDistance ? maxScrollDistance : targetPosition;
+
                         }
-                        else if (bottom > visibleRectangleBottom)
+                        else if ((isHorizontal && direction == View.FocusDirection.Left) ||
+                                 (!isHorizontal && direction == View.FocusDirection.Up))
                         {
-                            ScrollTo(bottom - Size.Height - ContentContainer.ScreenPosition.Y, true);
+                            targetPosition -= stepDistance;
+                            targetPosition = targetPosition < 0 ? 0 : targetPosition;
                         }
+
+                        ScrollTo(targetPosition, true);
                     }
                 }
             }
+            else
+            {
+                if((isHorizontal && direction == View.FocusDirection.Right) ||
+                    (!isHorizontal && direction == View.FocusDirection.Down))
+                {
+                    targetPosition += stepDistance;
+                    targetPosition = targetPosition > maxScrollDistance ? maxScrollDistance : targetPosition;
+
+                }
+                else if((isHorizontal && direction == View.FocusDirection.Left) ||
+                        (!isHorizontal && direction == View.FocusDirection.Up))
+                {
+                    targetPosition -= stepDistance;
+                    targetPosition = targetPosition < 0 ? 0 : targetPosition;
+                }
+
+                ScrollTo(targetPosition, true);
+
+                // End of scroll. escape.
+                if ((targetPosition == 0 || targetPosition == maxScrollDistance) == false)
+                {
+                    return this;
+                }
+
+            }
             return nextFocusedView;
         }
 
index ff05089..4198a35 100755 (executable)
@@ -292,5 +292,23 @@ namespace Tizen.NUI.Components
             var instance = (ScrollableBase)bindable;
             return instance.InternalEnableOverShootingEffect;
         });
+
+        /// <summary>
+        /// StepScrollDistanceProperty
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static readonly BindableProperty StepScrollDistanceProperty = BindableProperty.Create(nameof(StepScrollDistance), typeof(float), typeof(ScrollableBase), default(float), propertyChanged: (bindable, oldValue, newValue) =>
+        {
+            var instance = (ScrollableBase)bindable;
+            if (newValue != null)
+            {
+                instance.stepScrollDistance = (float)newValue;
+            }
+        },
+        defaultValueCreator: (bindable) =>
+        {
+            var instance = (ScrollableBase)bindable;
+            return instance.stepScrollDistance;
+        });
     }
 }
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusAllChildrenSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusAllChildrenSample.cs
new file mode 100755 (executable)
index 0000000..50318ad
--- /dev/null
@@ -0,0 +1,183 @@
+using System;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+    public class ScrollableFocusAllChildrenSample : IExample
+    {
+        int SCROLLMAX = 50;
+        public View root;
+
+        public void Activate()
+        {
+
+            Random rnd = new Random();
+            Window window = NUIApplication.GetDefaultWindow();
+
+            root = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            window.Add(root);
+
+            FocusManager.Instance.EnableDefaultAlgorithm(true);
+
+            var top = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Top"
+            };
+            root.Add(top);
+
+            var verticalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Vertical,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            root.Add(verticalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.MatchParent,
+                    HeightSpecification = LayoutParamPolicies.WrapContent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Focusable = true,
+                    FocusableInTouch = true,
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Horizontal,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+                colorItem.Add(label);
+                verticalScrollView.Add(colorItem);
+            }
+
+            var middle = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Middle"
+            };
+            root.Add(middle);
+
+            var horizontalLayout = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            root.Add(horizontalLayout);
+
+            var leftIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(leftIcon);
+
+            var horizontalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Horizontal,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            horizontalLayout.Add(horizontalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.WrapContent,
+                    HeightSpecification = LayoutParamPolicies.MatchParent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Focusable = true,
+                    FocusableInTouch = true,
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Vertical,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+                colorItem.Add(label);
+                horizontalScrollView.Add(colorItem);
+            }
+
+            var rightIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(rightIcon);
+
+            var bottom = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Bottom"
+            };
+            root.Add(bottom);
+        }
+
+        public void Deactivate()
+        {
+            if (root != null)
+            {
+                NUIApplication.GetDefaultWindow().Remove(root);
+                root.Dispose();
+            }
+        }
+    }
+
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusNoneChildrenSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusNoneChildrenSample.cs
new file mode 100755 (executable)
index 0000000..280e740
--- /dev/null
@@ -0,0 +1,188 @@
+using System;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+    public class ScrollableFocusNoneChildrenSample : IExample
+    {
+        int SCROLLMAX = 50;
+        public View root;
+
+        public void Activate()
+        {
+
+            Random rnd = new Random();
+            Window window = NUIApplication.GetDefaultWindow();
+
+            root = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            window.Add(root);
+
+            FocusManager.Instance.EnableDefaultAlgorithm(true);
+
+            var top = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Top"
+            };
+            root.Add(top);
+
+            /* Important Notice : To navigate ScrollableBase over the none-focusable childrens,
+             *                    ScrollableBase itself need to be focusable.
+             */
+            var verticalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Vertical,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Focusable = true,
+                FocusableInTouch = true,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            root.Add(verticalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.MatchParent,
+                    HeightSpecification = LayoutParamPolicies.WrapContent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Horizontal,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+                colorItem.Add(label);
+                verticalScrollView.Add(colorItem);
+            }
+
+            var middle = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Middle"
+            };
+            root.Add(middle);
+
+            var horizontalLayout = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            root.Add(horizontalLayout);
+
+            var leftIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(leftIcon);
+
+            /* Important Notice : To navigate ScrollableBase over the none-focusable childrens,
+             *                    ScrollableBase itself need to be focusable.
+             */
+            var horizontalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Horizontal,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Focusable = true,
+                FocusableInTouch = true,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            horizontalLayout.Add(horizontalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.WrapContent,
+                    HeightSpecification = LayoutParamPolicies.MatchParent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Vertical,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+                colorItem.Add(label);
+                horizontalScrollView.Add(colorItem);
+            }
+
+            var rightIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(rightIcon);
+
+            var bottom = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Bottom"
+            };
+            root.Add(bottom);
+        }
+
+        public void Deactivate()
+        {
+            if (root != null)
+            {
+                NUIApplication.GetDefaultWindow().Remove(root);
+                root.Dispose();
+            }
+        }
+    }
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSparseChildrenSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSparseChildrenSample.cs
new file mode 100755 (executable)
index 0000000..3ef9526
--- /dev/null
@@ -0,0 +1,201 @@
+using System;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+    public class ScrollableFocusSparseChildrenSample : IExample
+    {
+        int SCROLLMAX = 50;
+        public View root;
+
+        public void Activate()
+        {
+
+            Random rnd = new Random();
+            Window window = NUIApplication.GetDefaultWindow();
+
+            root = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            window.Add(root);
+
+            FocusManager.Instance.EnableDefaultAlgorithm(true);
+
+            var top = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Top"
+            };
+            root.Add(top);
+
+            /* Important Notice : To navigate ScrollableBase over the none-focusable childrens,
+             *                    ScrollableBase itself need to be focusable.
+             */
+            var verticalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Vertical,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Focusable = true,
+                FocusableInTouch = true,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            root.Add(verticalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.MatchParent,
+                    HeightSpecification = LayoutParamPolicies.WrapContent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Horizontal,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+
+                if (i % 25 == 0 || i % 26 == 0 || i % 27 == 0 || i % 28 == 0 || i % 29 == 0)
+                {
+                    colorItem.Focusable = true;
+                    colorItem.FocusableInTouch = true;
+                }
+
+                colorItem.Add(label);
+                verticalScrollView.Add(colorItem);
+            }
+
+            var middle = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Middle"
+            };
+            root.Add(middle);
+
+            var horizontalLayout = new View()
+            {
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size(10, 10),
+                },
+            };
+            root.Add(horizontalLayout);
+
+            var leftIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(leftIcon);
+
+            /* Important Notice : To navigate ScrollableBase over the none-focusable childrens,
+             *                    ScrollableBase itself need to be focusable.
+             */
+            var horizontalScrollView = new ScrollableBase()
+            {
+                ScrollingDirection = ScrollableBase.Direction.Horizontal,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                BackgroundColor = Color.Gray,
+                Focusable = true,
+                FocusableInTouch = true,
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size2D(10, 10),
+                }
+            };
+            horizontalLayout.Add(horizontalScrollView);
+
+            for (int i = 0; i < SCROLLMAX; i++)
+            {
+                var colorItem = new View()
+                {
+                    WidthSpecification = LayoutParamPolicies.WrapContent,
+                    HeightSpecification = LayoutParamPolicies.MatchParent,
+                    BackgroundColor = new Color((float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, (float)rnd.Next(256)/256f, 1),
+                    Layout = new LinearLayout
+                    {
+                        LinearOrientation = LinearLayout.Orientation.Vertical,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        CellPadding = new Size2D(10, 10),
+                    }
+                };
+                var label = new TextLabel()
+                {
+                    Text = $"[{i}]",
+                    PointSize = 20,
+                };
+
+                if (i % 25 == 0 || i % 26 == 0 || i % 27 == 0 || i % 28 == 0 || i % 29 == 0)
+                {
+                    colorItem.Focusable = true;
+                    colorItem.FocusableInTouch = true;
+                }
+                colorItem.Add(label);
+                horizontalScrollView.Add(colorItem);
+            }
+
+            var rightIcon = new RadioButton()
+            {
+                WidthSpecification = 50,
+                HeightSpecification = 50,
+                Focusable = true,
+                FocusableInTouch = true,
+            };
+            horizontalLayout.Add(rightIcon);
+
+            var bottom = new Button()
+            {
+                Focusable = true,
+                FocusableInTouch = true,
+                Text = "Bottom"
+            };
+            root.Add(bottom);
+        }
+
+        public void Deactivate()
+        {
+            if (root != null)
+            {
+                NUIApplication.GetDefaultWindow().Remove(root);
+                root.Dispose();
+            }
+        }
+    }
+}
index 1ab32a8..ad3835a 100755 (executable)
@@ -239,7 +239,7 @@ namespace Tizen.NUI.Components.Devel.Tests
             Assert.IsInstanceOf<ScrollableBase>(testingTarget, "Should return ScrollableBase instance.");
 
             View view1 = new View()
-            { 
+            {
                 Size = new Size(100, 200),
                 Position = new Position(0, 0),
                 BackgroundColor = Color.Cyan
@@ -563,5 +563,31 @@ namespace Tizen.NUI.Components.Devel.Tests
             testingTarget.Dispose();
             tlog.Debug(tag, $"ScrollableBaseBaseRemove END (OK)");
         }
+
+        [Test]
+        [Category("P1")]
+        [Description("ScrollableBase StepScrollDisance")]
+        [Property("SPEC", "Tizen.NUI.Components.ScrollableBase.StepScrollDisance A")]
+        [Property("SPEC_URL", "-")]
+        [Property("CRITERIA", "PRW")]
+        [Property("COVPARAM", "")]
+        [Property("AUTHOR", "sh10233.lee@samsung.com")]
+        public void ScrollableBaseStepScrollDistance()
+        {
+            tlog.Debug(tag, $"ScrollableBaseStepScrollDistance START");
+
+            var testingTarget = new ScrollableBase();
+            Assert.IsNotNull(testingTarget, "null handle");
+            Assert.IsInstanceOf<ScrollableBase>(testingTarget, "Should return ScrollableBase instance.");
+
+            testingTarget.StepScrollDistance = 200f;
+            tlog.Debug(tag, "StepScrollDistance : " + testingTarget.StepScrollDistance);
+
+            testingTarget.StepScrollDistance = 0f;
+            tlog.Debug(tag, "StepScrollDistance : " + testingTarget.StepScrollDistance);
+
+            testingTarget.Dispose();
+            tlog.Debug(tag, $"ScrollableBaseStepScrollDistance END (OK)");
+        }
     }
 }