/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2021 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.
END_TEST;
}
+int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline(" UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus");
+
+ // Register Type
+ TypeInfo type;
+ type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" );
+ DALI_TEST_CHECK( type );
+ BaseHandle handle = type.CreateInstance();
+ DALI_TEST_CHECK( handle );
+
+ KeyboardFocusManager manager = KeyboardFocusManager::Get();
+ DALI_TEST_CHECK(manager);
+
+ bool focusChangedSignalVerified = false;
+ FocusChangedCallback focusChangedCallback(focusChangedSignalVerified);
+ manager.FocusChangedSignal().Connect( &focusChangedCallback, &FocusChangedCallback::Callback );
+
+ PushButton button1 = PushButton::New();
+ PushButton button2 = PushButton::New();
+ PushButton button3 = PushButton::New();
+ PushButton button4 = PushButton::New();
+ PushButton button5 = PushButton::New();
+
+ button1.SetProperty(Actor::Property::SIZE, Vector2(50, 50));
+ button2.SetProperty(Actor::Property::SIZE, Vector2(50, 50));
+ button3.SetProperty(Actor::Property::SIZE, Vector2(50, 50));
+ button4.SetProperty(Actor::Property::SIZE, Vector2(50, 50));
+ button5.SetProperty(Actor::Property::SIZE, Vector2(50, 50));
+
+ button1.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE,true);
+ button2.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE,true);
+ button3.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE,true);
+ button4.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE,true);
+ button5.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE,true);
+
+ application.GetScene().Add(button1);
+ application.GetScene().Add(button2);
+ application.GetScene().Add(button3);
+ button5.Add(button4);
+ application.GetScene().Add(button5);
+
+ // set position
+ // button1 -- button2
+ // | |
+ // | button5|
+ // button3 -- button4
+ button1.SetProperty(Actor::Property::POSITION, Vector2(0.0f, 0.0f));
+ button2.SetProperty(Actor::Property::POSITION, Vector2(100.0f, 0.0f));
+ button3.SetProperty(Actor::Property::POSITION, Vector2(0.0f, 100.0f));
+ button4.SetProperty(Actor::Property::POSITION, Vector2(40.0f, 40.0f));
+ button5.SetProperty(Actor::Property::POSITION, Vector2(60.0f, 60.0f));
+
+ // flush the queue and render once
+ application.SendNotification();
+ application.Render();
+
+ // Set the focus to the button1
+ // [button1] -- button2
+ // | |
+ // | button5|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.SetCurrentFocusActor(button1) == true);
+ DALI_TEST_CHECK(manager.GetCurrentFocusActor() == button1);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+ focusChangedCallback.Reset();
+
+ // without set the navigation properties, but we can focus move
+ // enable the default algorithm
+ Dali::Toolkit::DevelKeyboardFocusManager::EnableDefaultAlgorithm(manager, true);
+ DALI_TEST_CHECK( Dali::Toolkit::DevelKeyboardFocusManager::IsDefaultAlgorithmEnabled(manager) );
+
+ // Move the focus towards right
+ // button1 -- [button2]
+ // | |
+ // | button5|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true);
+
+ // Confirm whether focus is moved to button2
+ DALI_TEST_EQUALS(button2.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button1);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button2);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards down
+ // button1 -- button2
+ // | |
+ // | [button5]|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::DOWN) == true);
+
+ // Confirm whether focus is moved to button5
+ DALI_TEST_EQUALS(button5.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button2);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button5);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards right
+ // button1 -- button2
+ // | |
+ // | button5|
+ // button3 -- [button4]
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true);
+
+ // Confirm whether focus is moved to button4
+ DALI_TEST_EQUALS(button4.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button5);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button4);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards left
+ // button1 -- button2
+ // | |
+ // | [button5]|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true);
+
+ // Confirm whether focus is moved to button5
+ DALI_TEST_EQUALS(button5.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button4);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button5);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards left
+ // button1 -- button2
+ // | |
+ // | button5|
+ //[button3] -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true);
+
+ // Confirm whether focus is moved to button3
+ DALI_TEST_EQUALS(button3.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button5);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button3);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards right
+ // button1 -- button2
+ // | |
+ // | [button5]|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true);
+
+ // Confirm whether focus is moved to button5
+ DALI_TEST_EQUALS(button5.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button3);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button5);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards left
+ // button1 -- button2
+ // | |
+ // | button5|
+ //[button3] -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true);
+
+ // Confirm whether focus is moved to button3
+ DALI_TEST_EQUALS(button3.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button5);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button3);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards up
+ //[button1]-- button2
+ // | |
+ // | button5|
+ // button3 -- button4
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::UP) == true);
+
+ // Confirm whether focus is moved to button1
+ DALI_TEST_EQUALS(button1.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button3);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+ focusChangedCallback.Reset();
+
+
+ // Move the focus towards left. The focus move will fail as no way to move it upwards
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == false);
+
+ // Move the focus toward page up/down. The focus move will fail as invalid direction.
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::PAGE_UP) == false);
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::PAGE_DOWN) == false);
+ focusChangedCallback.Reset();
+
+ END_TEST;
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+/*\r
+ * Copyright (C) 2017 The Android Open Source Project\r
+ *\r
+ * Modified by joogab yun(joogab.yun@samsung.com)\r
+ */\r
+\r
+// CLASS HEADER\r
+#include "focus-finder.h"\r
+\r
+// EXTERNAL INCLUDES\r
+#include <dali/devel-api/actors/actor-devel.h>\r
+#include <dali/integration-api/adaptor-framework/scene-holder.h>\r
+#include <dali/public-api/actors/layer.h>\r
+\r
+namespace Dali\r
+{\r
+namespace Toolkit\r
+{\r
+namespace FocusFinder\r
+{\r
+namespace\r
+{\r
+static constexpr float FULLY_TRANSPARENT(0.01f); ///< Alpha values must rise above this, before an object is considered to be visible.\r
+\r
+static int MajorAxisDistanceRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ {\r
+ return source.left - dest.right;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ return dest.left - source.right;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ {\r
+ return source.top - dest.bottom;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ return dest.top - source.bottom;\r
+ }\r
+ default:\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * @return The distance from the edge furthest in the given direction\r
+ * of source to the edge nearest in the given direction of dest.\r
+ * If the dest is not in the direction from source, return 0.\r
+ */\r
+static int MajorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+ return std::max(0, MajorAxisDistanceRaw(direction, source, dest));\r
+}\r
+\r
+static int MajorAxisDistanceToFarEdgeRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ {\r
+ return source.left - dest.left;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ return dest.right - source.right;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ {\r
+ return source.top - dest.top;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ return dest.bottom - source.bottom;\r
+ }\r
+ default:\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * @return The distance along the major axis w.r.t the direction from the\r
+ * edge of source to the far edge of dest.\r
+ * If the dest is not in the direction from source, return 1\r
+ */\r
+static int MajorAxisDistanceToFarEdge(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+ return std::max(1, MajorAxisDistanceToFarEdgeRaw(direction, source, dest));\r
+}\r
+\r
+/**\r
+ * Find the distance on the minor axis w.r.t the direction to the nearest\r
+ * edge of the destination rectangle.\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param source The source rect.\r
+ * @param dest The destination rect.\r
+ * @return The distance.\r
+ */\r
+static int MinorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ // the distance between the center verticals\r
+ return std::abs((source.top + (source.bottom - source.top) * 0.5f) -\r
+ (dest.top + (dest.bottom - dest.top) * 0.5f));\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ // the distance between the center horizontals\r
+ return std::abs((source.left + (source.right - source.left) * 0.5f) -\r
+ (dest.left + (dest.right - dest.left) * 0.5f));\r
+ }\r
+ default:\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * Calculate distance given major and minor axis distances.\r
+ * @param majorAxisDistance The majorAxisDistance\r
+ * @param minorAxisDistance The minorAxisDistance\r
+ * @return The distance\r
+ */\r
+static int GetWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance)\r
+{\r
+ return 13 * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance;\r
+}\r
+\r
+/**\r
+ * Convert x,y,width,height coordinates into left, right, bottom, top coordinates.\r
+ * @param[in,out] rect The rect\r
+ */\r
+static void ConvertCoordinate(Dali::Rect<float>& rect)\r
+{\r
+ // convert x, y, width, height -> left, right, bottom, top\r
+ float left = rect.x;\r
+ float right = rect.x + rect.width;\r
+ float bottom = rect.y + rect.height;\r
+ float top = rect.y;\r
+\r
+ rect.left = left;\r
+ rect.right = right;\r
+ rect.bottom = bottom;\r
+ rect.top = top;\r
+}\r
+\r
+/**\r
+ * Is destRect a candidate for the next focus given the direction?\r
+ * @param srcRect The source rect.\r
+ * @param destRect The dest rect.\r
+ * @param direction The direction (up, down, left, right)\r
+ * @return Whether destRect is a candidate.\r
+ */\r
+static bool IsCandidate(Dali::Rect<float> srcRect, Dali::Rect<float> destRect, Dali::Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ {\r
+ return (srcRect.right > destRect.right || srcRect.left >= destRect.right) && srcRect.left > destRect.left;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ return (srcRect.left < destRect.left || srcRect.right <= destRect.left) && srcRect.right < destRect.right;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ {\r
+ return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom) && srcRect.top > destRect.top;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top) && srcRect.bottom < destRect.bottom;\r
+ }\r
+ default:\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ return false;\r
+}\r
+\r
+/**\r
+ * Is dest in a given direction from src?\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param src The source rect\r
+ * @param dest The dest rect\r
+ */\r
+static bool IsToDirectionOf(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> src, Dali::Rect<float> dest)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ {\r
+ return src.left >= dest.right;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ return src.right <= dest.left;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ {\r
+ return src.top >= dest.bottom;\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ return src.bottom <= dest.top;\r
+ }\r
+ default:\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * Do the given direction's axis of rect1 and rect2 overlap?\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param rect1 The first rect\r
+ * @param rect2 The second rect\r
+ * @return whether the beams overlap\r
+ */\r
+static bool BeamsOverlap(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> rect1, Dali::Rect<float> rect2)\r
+{\r
+ switch(direction)\r
+ {\r
+ case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+ case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);\r
+ }\r
+ case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+ case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);\r
+ }\r
+ default:\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * One rectangle may be another candidate than another by virtue of being exclusively in the beam of the source rect.\r
+ * @param direction The direction (up, down, left, right)\r
+ * @param source The source rect\r
+ * @param rect1 The first rect\r
+ * @param rect2 The second rect\r
+ * @return Whether rect1 is a better candidate than rect2 by virtue of it being in src's beam\r
+ */\r
+static bool BeamBeats(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> rect1, Dali::Rect<float> rect2)\r
+{\r
+ const bool rect1InSrcBeam = BeamsOverlap(direction, source, rect1);\r
+ const bool rect2InSrcBeam = BeamsOverlap(direction, source, rect2);\r
+ // if rect1 isn't exclusively in the src beam, it doesn't win\r
+ if(rect2InSrcBeam || !rect1InSrcBeam)\r
+ {\r
+ return false;\r
+ }\r
+ // we know rect1 is in the beam, and rect2 is not\r
+ // if rect1 is to the direction of, and rect2 is not, rect1 wins.\r
+ // for example, for direction left, if rect1 is to the left of the source\r
+ // and rect2 is below, then we always prefer the in beam rect1, since rect2\r
+ // could be reached by going down.\r
+ if(!IsToDirectionOf(direction, source, rect2))\r
+ {\r
+ return true;\r
+ }\r
+ // for horizontal directions, being exclusively in beam always wins\r
+ if((direction == Dali::Toolkit::Control::KeyboardFocus::LEFT || direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT))\r
+ {\r
+ return true;\r
+ }\r
+ // for vertical directions, beams only beat up to a point:\r
+ // now, as long as rect2 isn't completely closer, rect1 wins\r
+ // e.g for direction down, completely closer means for rect2's top\r
+ // edge to be closer to the source's top edge than rect1's bottom edge.\r
+ return (MajorAxisDistance(direction, source, rect1) < MajorAxisDistanceToFarEdge(direction, source, rect2));\r
+}\r
+\r
+bool IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect<float>& focusedRect, Rect<float>& candidateRect, Rect<float>& bestCandidateRect)\r
+{\r
+ // to be a better candidate, need to at least be a candidate in the first place\r
+ if(!IsCandidate(focusedRect, candidateRect, direction))\r
+ {\r
+ return false;\r
+ }\r
+ // we know that candidateRect is a candidate.. if bestCandidateRect is not a candidate,\r
+ // candidateRect is better\r
+ if(!IsCandidate(focusedRect, bestCandidateRect, direction))\r
+ {\r
+ return true;\r
+ }\r
+ // if candidateRect is better by beam, it wins\r
+ if(BeamBeats(direction, focusedRect, candidateRect, bestCandidateRect))\r
+ {\r
+ return true;\r
+ }\r
+ // if bestCandidateRect is better, then candidateRect cant' be :)\r
+ if(BeamBeats(direction, focusedRect, bestCandidateRect, candidateRect))\r
+ {\r
+ return false;\r
+ }\r
+\r
+ // otherwise, do fudge-tastic comparison of the major and minor axis\r
+ return (GetWeightedDistanceFor(\r
+ MajorAxisDistance(direction, focusedRect, candidateRect),\r
+ MinorAxisDistance(direction, focusedRect, candidateRect)) < GetWeightedDistanceFor(MajorAxisDistance(direction, focusedRect, bestCandidateRect),\r
+ MinorAxisDistance(direction, focusedRect, bestCandidateRect)));\r
+}\r
+\r
+bool IsFocusable(Actor& actor)\r
+{\r
+ return (actor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) &&\r
+ actor.GetProperty<bool>(Actor::Property::VISIBLE) &&\r
+ actor.GetProperty<Vector4>(Actor::Property::WORLD_COLOR).a > FULLY_TRANSPARENT);\r
+}\r
+\r
+Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect<float>& focusedRect, Rect<float>& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+ Actor nearestActor;\r
+ if(actor && actor.GetProperty<bool>(Actor::Property::VISIBLE))\r
+ {\r
+ // Recursively children\r
+ const auto childCount = actor.GetChildCount();\r
+ for(auto i = 0u; i < childCount; ++i)\r
+ {\r
+ Dali::Actor child = actor.GetChildAt(i);\r
+ if(child && child != focusedActor && IsFocusable(child))\r
+ {\r
+ Rect<float> candidateRect = DevelActor::CalculateScreenExtents(child);\r
+\r
+ // convert x, y, width, height -> left, right, bottom, top\r
+ ConvertCoordinate(candidateRect);\r
+\r
+ if(IsBetterCandidate(direction, focusedRect, candidateRect, bestCandidateRect))\r
+ {\r
+ bestCandidateRect = candidateRect;\r
+ nearestActor = child;\r
+ }\r
+ }\r
+ Actor nextActor = FindNextFocus(child, focusedActor, focusedRect, bestCandidateRect, direction);\r
+ if(nextActor)\r
+ {\r
+ nearestActor = nextActor;\r
+ }\r
+ }\r
+ }\r
+ return nearestActor;\r
+}\r
+\r
+} // unnamed namespace\r
+\r
+Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+ Actor nearestActor;\r
+ if(!focusedActor)\r
+ {\r
+ return nearestActor;\r
+ }\r
+\r
+ Rect<float> focusedRect = DevelActor::CalculateScreenExtents(focusedActor);\r
+\r
+ // initialize the best candidate to something impossible\r
+ // (so the first plausible actor will become the best choice)\r
+ Rect<float> bestCandidateRect = focusedRect;\r
+ switch(direction)\r
+ {\r
+ case Toolkit::Control::KeyboardFocus::LEFT:\r
+ {\r
+ bestCandidateRect.x += 1;\r
+ break;\r
+ }\r
+ case Toolkit::Control::KeyboardFocus::RIGHT:\r
+ {\r
+ bestCandidateRect.x -= 1;\r
+ break;\r
+ }\r
+ case Toolkit::Control::KeyboardFocus::UP:\r
+ {\r
+ bestCandidateRect.y += 1;\r
+ break;\r
+ }\r
+ case Toolkit::Control::KeyboardFocus::DOWN:\r
+ {\r
+ bestCandidateRect.y -= 1;\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ ConvertCoordinate(bestCandidateRect);\r
+\r
+ ConvertCoordinate(focusedRect);\r
+\r
+ Integration::SceneHolder window = Integration::SceneHolder::Get(focusedActor);\r
+ if(window)\r
+ {\r
+ Actor rootActor = window.GetRootLayer();\r
+ nearestActor = FindNextFocus(rootActor, focusedActor, focusedRect, bestCandidateRect, direction);\r
+ }\r
+ return nearestActor;\r
+}\r
+\r
+} // namespace FocusFinder\r
+\r
+} // namespace Toolkit\r
+\r
+} // namespace Dali\r