Currently, GetNextFocusableView of FocusableBase was Index-based of ContentContainer Child.
This is a problem.
Changes the search for the next focus view from index-based to position-based.
}
}
-
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
{
- View nextFocusedView = null;
-
- int currentIndex = ContentContainer.Children.IndexOf(currentFocusedView);
-
- switch (direction)
+ if (currentFocusedView == this)
{
- case View.FocusDirection.Left:
- case View.FocusDirection.Up:
- {
- if (currentIndex > 0)
- {
- nextFocusedView = ContentContainer.Children[--currentIndex];
- }
- break;
- }
- case View.FocusDirection.Right:
- case View.FocusDirection.Down:
- {
- if (currentIndex < ContentContainer.Children.Count - 1)
- {
- nextFocusedView = ContentContainer.Children[++currentIndex];
- }
- break;
- }
+ return null;
}
+ View nextFocusedView = FocusManager.Instance.GetNearestFocusableActor(this, currentFocusedView, direction);
+
if (nextFocusedView != null)
{
- // Check next focused view is inside of visible area.
- // If it is not, move scroll position to make it visible.
- Position scrollPosition = ContentContainer.CurrentPosition;
- float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
-
- float left = nextFocusedView.Position.X;
- float right = nextFocusedView.Position.X + nextFocusedView.Size.Width;
- float top = nextFocusedView.Position.Y;
- float bottom = nextFocusedView.Position.Y + nextFocusedView.Size.Height;
-
- float visibleRectangleLeft = -scrollPosition.X;
- float visibleRectangleRight = -scrollPosition.X + Size.Width;
- float visibleRectangleTop = -scrollPosition.Y;
- float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
-
- if (ScrollingDirection == Direction.Horizontal)
+ View view = nextFocusedView;
+ while (view.GetParent() is View && view.GetParent() != ContentContainer)
{
- if (left < visibleRectangleLeft)
- {
- targetPosition = left;
- }
- else if (right > visibleRectangleRight)
- {
- targetPosition = right - Size.Width;
- }
+ view = (View)view.GetParent();
}
- else
+ if (view.GetParent() == ContentContainer)
{
- if (top < visibleRectangleTop)
+ // Check next focused view is inside of visible area.
+ // If it is not, move scroll position to make it visible.
+ Position scrollPosition = ContentContainer.CurrentPosition;
+ float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
+
+ float left = view.Position.X;
+ float right = view.Position.X + view.Size.Width;
+ float top = view.Position.Y;
+ float bottom = view.Position.Y + view.Size.Height;
+
+ float visibleRectangleLeft = -scrollPosition.X;
+ float visibleRectangleRight = -scrollPosition.X + Size.Width;
+ float visibleRectangleTop = -scrollPosition.Y;
+ float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
+
+ if (ScrollingDirection == Direction.Horizontal)
{
- targetPosition = top;
+ if (left < visibleRectangleLeft)
+ {
+ targetPosition = left;
+ }
+ else if (right > visibleRectangleRight)
+ {
+ targetPosition = right - Size.Width;
+ }
}
- else if (bottom > visibleRectangleBottom)
+ else
{
- targetPosition = bottom - Size.Height;
+ if (top < visibleRectangleTop)
+ {
+ targetPosition = top;
+ }
+ else if (bottom > visibleRectangleBottom)
+ {
+ targetPosition = bottom - Size.Height;
+ }
}
+ ScrollTo(targetPosition, true);
}
- ScrollTo(targetPosition, true);
}
-
return nextFocusedView;
}
}
[return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.U1)]
public static extern bool IsDefaultAlgorithmEnabled(global::System.Runtime.InteropServices.HandleRef jarg1);
+ [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_KeyboardFocusManager_GetNearestFocusableActor")]
+ public static extern global::System.IntPtr GetNearestFocusableActor(global::System.Runtime.InteropServices.HandleRef rootView, global::System.Runtime.InteropServices.HandleRef currentView, int direction);
+
[global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_KeyboardFocusManager_SWIGUpcast")]
public static extern global::System.IntPtr Upcast(global::System.IntPtr jarg1);
}
return ret;
}
+ /// <summary>
+ /// Get the nearest focusable view.
+ /// </summary>
+ /// <param name="rootView">The view group in which to find the next focusable view.</param>
+ /// <param name="focusedView">The current focused view.</param>
+ /// <param name="direction">The direction.</param>
+ /// <returns>The nearest focusable view, or an empty handle if none exists.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public View GetNearestFocusableActor(View rootView, View focusedView, View.FocusDirection direction)
+ {
+ //to fix memory leak issue, match the handle count with native side.
+ IntPtr cPtr = Interop.FocusManager.GetNearestFocusableActor(View.getCPtr(rootView), View.getCPtr(focusedView), (int)direction);
+ View ret = this.GetInstanceSafely<View>(cPtr);
+ return ret;
+ }
+
internal static FocusManager Get()
{
FocusManager ret = new FocusManager(Interop.FocusManager.Get(), true);
--- /dev/null
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+ public class NestedScrollViewSamle : IExample
+ {
+ private View root;
+
+
+ public void Activate()
+ {
+ Window window = NUIApplication.GetDefaultWindow();
+
+ FocusManager.Instance.EnableDefaultAlgorithm(true);
+
+ root = new View();
+ root.Layout = new AbsoluteLayout();
+ root.Size = new Size(300, 800);
+
+ var scrollview = new ScrollableBase{
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ HeightSpecification = LayoutParamPolicies.MatchParent,
+ ScrollingDirection = ScrollableBase.Direction.Vertical,
+ Focusable = true,
+ FocusableInTouch = true,
+ };
+
+ root.Add(scrollview);
+
+ scrollview.ContentContainer.Layout = new LinearLayout
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical
+ };
+
+ scrollview.ContentContainer.Add(new View
+ {
+ BackgroundColor = new Vector4(1.0f, 0.6f, 0.2f, 1.0f),
+ SizeHeight = 100,
+ WidthResizePolicy = ResizePolicyType.FillToParent,
+ });
+
+ var scroll2 = new ScrollableBase{
+ BackgroundColor = Color.Gray,
+ ScrollingDirection = ScrollableBase.Direction.Vertical,
+ Focusable = true,
+ FocusableInTouch = true,
+ };
+
+ scroll2.WidthSpecification = LayoutParamPolicies.MatchParent;
+ scroll2.HeightSpecification = LayoutParamPolicies.MatchParent;
+ scroll2.WidthResizePolicy = ResizePolicyType.FillToParent;
+ scroll2.SizeHeight = 400;
+
+ var abscontainer = new View
+ {
+ Layout = new AbsoluteLayout(),
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ SizeHeight = 400,
+ };
+ abscontainer.Add(scroll2);
+
+ scrollview.ContentContainer.Add(abscontainer);
+
+ scroll2.ContentContainer.Layout = new LinearLayout
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical
+ };
+
+ for(int i=0; i<50; i++)
+ {
+ scroll2.ContentContainer.Add(new TextLabel
+ {
+ Text = $"Text {i}",
+ WidthResizePolicy = ResizePolicyType.FillToParent,
+ Focusable = true,
+ FocusableInTouch = true,
+ // Position = new Position(0, i*50+200),
+ });
+ }
+
+ scrollview.ContentContainer.Add(new View{
+ BackgroundColor = Color.Yellow,
+ SizeHeight = 400,
+ WidthResizePolicy = ResizePolicyType.FillToParent,
+ });
+
+ TextLabel titleView = new TextLabel
+ {
+ Text = "Title",
+ Focusable = true,
+ FocusableInTouch = true,
+ Size = new Size(300, 100),
+ };
+
+ TextLabel footView = new TextLabel
+ {
+ Text = "Foot",
+ Focusable = true,
+ FocusableInTouch = true,
+ Size = new Size(300, 100),
+ Position = new Position(0, 500),
+ };
+
+ window.Add(root);
+ window.Add(titleView);
+ window.Add(footView);
+ }
+
+
+ public void Deactivate()
+ {
+ if (root != null)
+ {
+ NUIApplication.GetDefaultWindow().Remove(root);
+ root.Dispose();
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+ public class ScrollableFocusSample : IExample
+ {
+ public View root;
+
+ public void Activate()
+ {
+ Window window = NUIApplication.GetDefaultWindow();
+
+ root = new View();
+ root.Layout = new AbsoluteLayout();
+ root.Size = new Size(500, 800);
+
+ root.BackgroundColor = Color.White;
+ window.Add(root);
+
+ FocusManager.Instance.EnableDefaultAlgorithm(true);
+ root.Layout = new LinearLayout
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical
+ };
+ root.WidthSpecification = LayoutParamPolicies.MatchParent;
+ root.HeightSpecification = LayoutParamPolicies.MatchParent;
+
+
+ var topbtn = new Button
+ {
+ Focusable = true,
+ FocusableInTouch = true,
+ Text = "Top"
+ };
+ root.Add(topbtn);
+
+ var scrollview = new ScrollableBase
+ {
+ ScrollingDirection = ScrollableBase.Direction.Vertical,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ HeightSpecification = LayoutParamPolicies.MatchParent,
+ BackgroundColor = Color.Gray
+ };
+ scrollview.ContentContainer.Layout = new AbsoluteLayout();
+ scrollview.ContentContainer.WidthSpecification = LayoutParamPolicies.MatchParent;
+ scrollview.ContentContainer.SizeHeight = 1800;
+ root.Add(scrollview);
+ for (int i = 0; i < 40; i++)
+ {
+ scrollview.ContentContainer.Add(CreateButton(i));
+ }
+
+ var middle = new Button
+ {
+ Focusable = true,
+ FocusableInTouch = true,
+ Text = "Middle"
+ };
+ root.Add(middle);
+
+ var myscrollview = new ScrollableBase
+ {
+ ScrollingDirection = ScrollableBase.Direction.Vertical,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ HeightSpecification = LayoutParamPolicies.MatchParent,
+ BackgroundColor = Color.Yellow
+ };
+ myscrollview.ContentContainer.Layout = new AbsoluteLayout();
+ myscrollview.ContentContainer.WidthSpecification = LayoutParamPolicies.MatchParent;
+ myscrollview.ContentContainer.SizeHeight = 1800;
+ root.Add(myscrollview);
+ for (int i = 0; i < 40; i++)
+ {
+ myscrollview.ContentContainer.Add(CreateButton(i));
+ }
+
+ var bottom = new Button
+ {
+ Focusable = true,
+ FocusableInTouch = true,
+ Text = "bottom"
+ };
+ root.Add(bottom);
+
+ }
+
+ static View CreateButton(int index)
+ {
+ var rnd = new Random();
+
+ var btn = new Button
+ {
+ Focusable = true,
+ FocusableInTouch = true,
+ Text = $"Item {index}",
+ };
+ // btn.FocusGained += (s, e) =>
+ // {
+ // Tizen.Log.Error("NUI", $"[[{btn.Text}]] \n");
+ // };
+
+ var item = Wrapping(btn);
+ item.SizeWidth = 200;
+ item.SizeHeight = 90;
+
+ item.Position = new Position(220 * (index % 3), 100 * (index / 3) );
+
+ if (item is Button button)
+ {
+ button.Text = $"[{button.Text}]";
+ }
+
+ return item;
+ }
+
+ static View Wrapping(View view)
+ {
+ int cnt = new Random().Next(0, 4);
+
+ for (int i = 0; i < cnt; i++)
+ {
+ var wrapper = new View();
+ view.WidthSpecification = LayoutParamPolicies.MatchParent;
+ view.HeightSpecification = LayoutParamPolicies.MatchParent;
+ wrapper.Add(view);
+ view = wrapper;
+ }
+
+ return view;
+ }
+
+ public void Deactivate()
+ {
+ if (root != null)
+ {
+ NUIApplication.GetDefaultWindow().Remove(root);
+ root.Dispose();
+ }
+ }
+ }
+
+}
--- /dev/null
+using System;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+
+namespace Tizen.NUI.Samples
+{
+ public class ScrollableFocusSample2 : IExample
+ {
+ public View root;
+ TextLabel _label;
+
+ public void Activate()
+ {
+ Window window = NUIApplication.GetDefaultWindow();
+
+ root = new View();
+ root.Layout = new AbsoluteLayout();
+ root.Size = new Size(300, 800);
+
+ root.BackgroundColor = Color.White;
+ window.Add(root);
+
+ FocusManager.Instance.EnableDefaultAlgorithm(true);
+ root.Layout = new AbsoluteLayout();
+ root.WidthSpecification = LayoutParamPolicies.MatchParent;
+ root.HeightSpecification = LayoutParamPolicies.MatchParent;
+
+
+ _label = new TextLabel();
+ root.Add(_label);
+ _label.Position = new Position(0, 0);
+ _label.SizeWidth = 300;
+ _label.SizeHeight = 100;
+
+ var topPanel = new View
+ {
+ Layout = new LinearLayout
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical
+ },
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ HeightSpecification = LayoutParamPolicies.MatchParent,
+ };
+
+ for (int i = 0; i < 10; i++)
+ {
+ topPanel.Add(CreateButton(i, false));
+ }
+ root.Add(topPanel);
+ topPanel.Position = new Position(0, 100);
+
+ var bottomPanel = new View
+ {
+ Layout = new LinearLayout
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical,
+ },
+ BackgroundColor = Color.Yellow,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ SizeHeight = 500,
+ };
+
+ for (int i = 0; i < 10; i++)
+ {
+ bottomPanel.Add(CreateButton(11 + i, true));
+ }
+
+ root.Add(bottomPanel);
+ bottomPanel.Position = new Position(0, 500);
+
+ topPanel.RaiseToTop();
+
+ }
+
+ View CreateButton(int index, bool second)
+ {
+ var rnd = new Random();
+
+ var btn = new Button
+ {
+ Focusable = true,
+ FocusableInTouch = true,
+ Text = $"Item {index}",
+ };
+ if (second)
+ btn.BackgroundColor = Color.Red;
+
+ btn.WidthSpecification = LayoutParamPolicies.MatchParent;
+ btn.SizeHeight = 60;
+
+ btn.FocusGained += (s, e) =>
+ {
+ btn.Text = $"[Item {index}]";
+ _label.Text = btn.Text;
+ };
+ btn.FocusLost += (s, e) =>
+ {
+ btn.Text = $"Item {index}";
+ };
+
+ return btn;
+ }
+
+ public void Deactivate()
+ {
+ if (root != null)
+ {
+ NUIApplication.GetDefaultWindow().Remove(root);
+ root.Dispose();
+ }
+ }
+ }
+
+}