From 9b2486f5998dccbc50c3da9c63953a80df15e762 Mon Sep 17 00:00:00 2001 From: Heeyong Song Date: Wed, 9 Jun 2021 17:21:43 +0900 Subject: [PATCH 1/1] Change FocusFinder to a method Change-Id: I7513ef3142fe649ad3d2c614b76b863b6d6c0c1a --- .../dali-toolkit/utc-Dali-KeyboardFocusManager.cpp | 74 ++-- .../devel-api/focus-manager/focus-finder.cpp | 407 +++++++++++++++++-- .../devel-api/focus-manager/focus-finder.h | 47 +-- dali-toolkit/internal/file.list | 1 - .../internal/focus-manager/focus-finder-impl.cpp | 449 --------------------- .../internal/focus-manager/focus-finder-impl.h | 117 ------ .../focus-manager/keyboard-focus-manager-impl.cpp | 2 +- 7 files changed, 442 insertions(+), 655 deletions(-) delete mode 100644 dali-toolkit/internal/focus-manager/focus-finder-impl.cpp delete mode 100644 dali-toolkit/internal/focus-manager/focus-finder-impl.h diff --git a/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp b/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp index 55de778..053b25c 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp @@ -1643,22 +1643,22 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) 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); + 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); - application.GetScene().Add(button4); + button5.Add(button4); application.GetScene().Add(button5); // set position @@ -1666,11 +1666,11 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) // | | // | 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(100.0f, 100.0f)); - button5.SetProperty( Actor::Property::POSITION, Vector2(60.0f, 60.0f)); + 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(); @@ -1698,7 +1698,7 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true); // Confirm whether focus is moved to button2 - DALI_TEST_EQUALS(button2.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button2.GetProperty(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); @@ -1712,7 +1712,7 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::DOWN) == true); // Confirm whether focus is moved to button5 - DALI_TEST_EQUALS(button5.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button5.GetProperty(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); @@ -1726,7 +1726,7 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true); // Confirm whether focus is moved to button4 - DALI_TEST_EQUALS(button4.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button4.GetProperty(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); @@ -1740,7 +1740,7 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true); // Confirm whether focus is moved to button5 - DALI_TEST_EQUALS(button5.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button5.GetProperty(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); @@ -1754,7 +1754,35 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true); // Confirm whether focus is moved to button3 - DALI_TEST_EQUALS(button3.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button3.GetProperty(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(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(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); @@ -1768,7 +1796,7 @@ int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void) DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::UP) == true); // Confirm whether focus is moved to button1 - DALI_TEST_EQUALS(button1.GetProperty(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION ); + DALI_TEST_EQUALS(button1.GetProperty(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); diff --git a/dali-toolkit/devel-api/focus-manager/focus-finder.cpp b/dali-toolkit/devel-api/focus-manager/focus-finder.cpp index d12ed11..952c261 100644 --- a/dali-toolkit/devel-api/focus-manager/focus-finder.cpp +++ b/dali-toolkit/devel-api/focus-manager/focus-finder.cpp @@ -15,63 +15,422 @@ * */ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Modified by joogab yun(joogab.yun@samsung.com) + */ + // CLASS HEADER #include "focus-finder.h" // EXTERNAL INCLUDES -#include - -// INTERNAL INCLUDES -#include +#include +#include +#include namespace Dali { namespace Toolkit { -FocusFinder::FocusFinder() +namespace FocusFinder +{ +namespace +{ +static int MajorAxisDistanceRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) +{ + switch(direction) + { + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + { + return source.left - dest.right; + } + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + return dest.left - source.right; + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + { + return source.top - dest.bottom; + } + case Dali::Toolkit::Control::KeyboardFocus::DOWN: + { + return dest.top - source.bottom; + } + default: + { + return 0; + } + } +} + +/** + * @return The distance from the edge furthest in the given direction + * of source to the edge nearest in the given direction of dest. + * If the dest is not in the direction from source, return 0. + */ +static int MajorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) { + return std::max(0, MajorAxisDistanceRaw(direction, source, dest)); } -FocusFinder::~FocusFinder() +static int MajorAxisDistanceToFarEdgeRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) { + switch(direction) + { + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + { + return source.left - dest.left; + } + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + return dest.right - source.right; + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + { + return source.top - dest.top; + } + case Dali::Toolkit::Control::KeyboardFocus::DOWN: + { + return dest.bottom - source.bottom; + } + default: + { + return 0; + } + } } -FocusFinder FocusFinder::Get() +/** + * @return The distance along the major axis w.r.t the direction from the + * edge of source to the far edge of dest. + * If the dest is not in the direction from source, return 1 + */ +static int MajorAxisDistanceToFarEdge(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) { - FocusFinder finder; + return std::max(1, MajorAxisDistanceToFarEdgeRaw(direction, source, dest)); +} - // Check whether the focus finder is already created - SingletonService singletonService(SingletonService::Get()); - if(singletonService) +/** + * Find the distance on the minor axis w.r.t the direction to the nearest + * edge of the destination rectangle. + * @param direction the direction (up, down, left, right) + * @param source The source rect. + * @param dest The destination rect. + * @return The distance. + */ +static int MinorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) +{ + switch(direction) { - Dali::BaseHandle handle = singletonService.GetSingleton(typeid(FocusFinder)); - if(handle) + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + // the distance between the center verticals + return std::abs( + (((source.top + source.bottom) * 0.5f) - + (((dest.top + dest.bottom) * 0.5f)))); + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + case Dali::Toolkit::Control::KeyboardFocus::DOWN: { - // If so, downcast the handle of singleton to focus finder - finder = FocusFinder(dynamic_cast(handle.GetObjectPtr())); + // the distance between the center horizontals + return std::abs( + (((source.left + source.right) * 0.5f) - + (((dest.left + dest.right) * 0.5f)))); } + default: + { + return 0; + } + } +} + +/** + * Calculate distance given major and minor axis distances. + * @param majorAxisDistance The majorAxisDistance + * @param minorAxisDistance The minorAxisDistance + * @return The distance + */ +static int GetWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) +{ + return 13 * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance; +} + +/** + * Convert x,y,width,height coordinates into left, right, bottom, top coordinates. + * @param[in,out] rect The rect + */ +static void ConvertCoordinate(Dali::Rect& rect) +{ + // convert x, y, width, height -> left, right, bottom, top + float left = rect.x; + float right = rect.x + rect.width; + float bottom = rect.y + rect.height; + float top = rect.y; + + rect.left = left; + rect.right = right; + rect.bottom = bottom; + rect.top = top; +} - if(!finder) +/** + * Is destRect a candidate for the next focus given the direction? + * @param srcRect The source rect. + * @param destRect The dest rect. + * @param direction The direction (up, down, left, right) + * @return Whether destRect is a candidate. + */ +static bool IsCandidate(Dali::Rect srcRect, Dali::Rect destRect, Dali::Toolkit::Control::KeyboardFocus::Direction direction) +{ + switch(direction) + { + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + { + return (srcRect.right > destRect.right || srcRect.left >= destRect.right) && srcRect.left > destRect.left; + } + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + return (srcRect.left < destRect.left || srcRect.right <= destRect.left) && srcRect.right < destRect.right; + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + { + return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom) && srcRect.top > destRect.top; + } + case Dali::Toolkit::Control::KeyboardFocus::DOWN: { - // If not, create the focus finder and register it as a singleton - finder = FocusFinder(new Internal::FocusFinder()); - singletonService.Register(typeid(finder), finder); + return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top) && srcRect.bottom < destRect.bottom; + } + default: + { + return false; } } + return false; +} - return finder; +/** + * Is dest in a given direction from src? + * @param direction the direction (up, down, left, right) + * @param src The source rect + * @param dest The dest rect + */ +static bool IsToDirectionOf(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect src, Dali::Rect dest) +{ + switch(direction) + { + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + { + return src.left >= dest.right; + } + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + return src.right <= dest.left; + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + { + return src.top >= dest.bottom; + } + case Dali::Toolkit::Control::KeyboardFocus::DOWN: + { + return src.bottom <= dest.top; + } + default: + { + return false; + } + } +} + +/** + * Do the given direction's axis of rect1 and rect2 overlap? + * @param direction the direction (up, down, left, right) + * @param rect1 The first rect + * @param rect2 The second rect + * @return whether the beams overlap + */ +static bool BeamsOverlap(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect rect1, Dali::Rect rect2) +{ + switch(direction) + { + case Dali::Toolkit::Control::KeyboardFocus::LEFT: + case Dali::Toolkit::Control::KeyboardFocus::RIGHT: + { + return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom); + } + case Dali::Toolkit::Control::KeyboardFocus::UP: + case Dali::Toolkit::Control::KeyboardFocus::DOWN: + { + return (rect2.right >= rect1.left) && (rect2.left <= rect1.right); + } + default: + { + return false; + } + } } -FocusFinder::FocusFinder(Internal::FocusFinder* impl) -: BaseHandle(impl) +/** + * One rectangle may be another candidate than another by virtue of being exclusively in the beam of the source rect. + * @param direction The direction (up, down, left, right) + * @param source The source rect + * @param rect1 The first rect + * @param rect2 The second rect + * @return Whether rect1 is a better candidate than rect2 by virtue of it being in src's beam + */ +static bool BeamBeats(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect rect1, Dali::Rect rect2) { + const bool rect1InSrcBeam = BeamsOverlap(direction, source, rect1); + const bool rect2InSrcBeam = BeamsOverlap(direction, source, rect2); + // if rect1 isn't exclusively in the src beam, it doesn't win + if(rect2InSrcBeam || !rect1InSrcBeam) + { + return false; + } + // we know rect1 is in the beam, and rect2 is not + // if rect1 is to the direction of, and rect2 is not, rect1 wins. + // for example, for direction left, if rect1 is to the left of the source + // and rect2 is below, then we always prefer the in beam rect1, since rect2 + // could be reached by going down. + if(!IsToDirectionOf(direction, source, rect2)) + { + return true; + } + // for horizontal directions, being exclusively in beam always wins + if((direction == Dali::Toolkit::Control::KeyboardFocus::LEFT || direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)) + { + return true; + } + // for vertical directions, beams only beat up to a point: + // now, as long as rect2 isn't completely closer, rect1 wins + // e.g for direction down, completely closer means for rect2's top + // edge to be closer to the source's top edge than rect1's bottom edge. + return (MajorAxisDistance(direction, source, rect1) < MajorAxisDistanceToFarEdge(direction, source, rect2)); } -Actor FocusFinder::GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction) +bool IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect& focusedRect, Rect& candidateRect, Rect& bestCandidateRect) { - return GetImpl(*this).GetNearestFocusableActor(focusedActor, direction); + // to be a better candidate, need to at least be a candidate in the first place + if(!IsCandidate(focusedRect, candidateRect, direction)) + { + return false; + } + // we know that candidateRect is a candidate.. if bestCandidateRect is not a candidate, + // candidateRect is better + if(!IsCandidate(focusedRect, bestCandidateRect, direction)) + { + return true; + } + // if candidateRect is better by beam, it wins + if(BeamBeats(direction, focusedRect, candidateRect, bestCandidateRect)) + { + return true; + } + // if bestCandidateRect is better, then candidateRect cant' be :) + if(BeamBeats(direction, focusedRect, bestCandidateRect, candidateRect)) + { + return false; + } + + // otherwise, do fudge-tastic comparison of the major and minor axis + return (GetWeightedDistanceFor( + MajorAxisDistance(direction, focusedRect, candidateRect), + MinorAxisDistance(direction, focusedRect, candidateRect)) < GetWeightedDistanceFor(MajorAxisDistance(direction, focusedRect, bestCandidateRect), + MinorAxisDistance(direction, focusedRect, bestCandidateRect))); } +Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect& focusedRect, Rect& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction) +{ + Actor nearestActor; + if(actor) + { + // Recursively children + const auto childCount = actor.GetChildCount(); + for(auto i = 0u; i < childCount; ++i) + { + Dali::Actor child = actor.GetChildAt(i); + if(child && child != focusedActor && child.GetProperty(Actor::Property::KEYBOARD_FOCUSABLE)) + { + Rect candidateRect = DevelActor::CalculateScreenExtents(child); + + // convert x, y, width, height -> left, right, bottom, top + ConvertCoordinate(candidateRect); + + if(IsBetterCandidate(direction, focusedRect, candidateRect, bestCandidateRect)) + { + bestCandidateRect = candidateRect; + nearestActor = child; + } + } + Actor nextActor = FindNextFocus(child, focusedActor, focusedRect, bestCandidateRect, direction); + if(nextActor) + { + nearestActor = nextActor; + } + } + } + return nearestActor; +} + +} // unnamed namespace + +Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction) +{ + Actor nearestActor; + if(!focusedActor) + { + return nearestActor; + } + + Rect focusedRect = DevelActor::CalculateScreenExtents(focusedActor); + + // initialize the best candidate to something impossible + // (so the first plausible actor will become the best choice) + Rect bestCandidateRect = focusedRect; + switch(direction) + { + case Toolkit::Control::KeyboardFocus::LEFT: + { + bestCandidateRect.x += 1; + break; + } + case Toolkit::Control::KeyboardFocus::RIGHT: + { + bestCandidateRect.x -= 1; + break; + } + case Toolkit::Control::KeyboardFocus::UP: + { + bestCandidateRect.y += 1; + break; + } + case Toolkit::Control::KeyboardFocus::DOWN: + { + bestCandidateRect.y -= 1; + break; + } + default: + { + break; + } + } + + ConvertCoordinate(bestCandidateRect); + + ConvertCoordinate(focusedRect); + + Integration::SceneHolder window = Integration::SceneHolder::Get(focusedActor); + if(window) + { + Actor rootActor = window.GetRootLayer(); + nearestActor = FindNextFocus(rootActor, focusedActor, focusedRect, bestCandidateRect, direction); + } + return nearestActor; +} + +} // namespace FocusFinder + } // namespace Toolkit } // namespace Dali diff --git a/dali-toolkit/devel-api/focus-manager/focus-finder.h b/dali-toolkit/devel-api/focus-manager/focus-finder.h index 8012675..47f313d 100644 --- a/dali-toolkit/devel-api/focus-manager/focus-finder.h +++ b/dali-toolkit/devel-api/focus-manager/focus-finder.h @@ -25,50 +25,17 @@ namespace Dali { namespace Toolkit { -namespace Internal DALI_INTERNAL +namespace FocusFinder { -class FocusFinder; -} - /** - * FocusFinder - * This class used for finding the next focusable actor in a given direction - * from a actor that currently has focus. + * Get the nearest focusable actor. + * @param [in] focusedActor The current focused actor. + * @param [in] direction The direction. + * @return The nearest focusable actor, or an empty handle if none exists. */ -class DALI_TOOLKIT_API FocusFinder : public BaseHandle -{ -public: - /** - * Create a FocusFinder handle; this can be initialised with FocusFinder::Get() - * Calling member functions with an uninitialised handle is not allowed. - */ - FocusFinder(); - - /** - * @brief Destructor - * - * This is non-virtual since derived Handle types must not contain data or virtual methods. - */ - ~FocusFinder(); - - /** - * @brief Get the singleton of FocusFinder object. - * @return A handle to the FocusFinder control. - */ - static FocusFinder Get(); - - /** - * Get the nearest focusable actor. - * @param [in] focusedActor The current focused actor. - * @param [in] direction The direction. - * @return The nearest focusable actor, or null if none exists. - */ - Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction); - -private: - explicit DALI_INTERNAL FocusFinder(Internal::FocusFinder* impl); +DALI_TOOLKIT_API Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction); -}; // class FocusFinder +} // namespace FocusFinder } // namespace Toolkit diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 30435f8..eca4322 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -114,7 +114,6 @@ SET( toolkit_src_files ${toolkit_src_dir}/focus-manager/keyboard-focus-manager-impl.cpp ${toolkit_src_dir}/focus-manager/keyinput-focus-manager-impl.cpp - ${toolkit_src_dir}/focus-manager/focus-finder-impl.cpp ${toolkit_src_dir}/helpers/color-conversion.cpp ${toolkit_src_dir}/helpers/property-helper.cpp ${toolkit_src_dir}/filters/blur-two-pass-filter.cpp diff --git a/dali-toolkit/internal/focus-manager/focus-finder-impl.cpp b/dali-toolkit/internal/focus-manager/focus-finder-impl.cpp deleted file mode 100644 index 93f1d65..0000000 --- a/dali-toolkit/internal/focus-manager/focus-finder-impl.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Modified by joogab yun(joogab.yun@samsung.com) - */ - -// CLASS HEADER -#include "focus-finder-impl.h" - -// INTERNAL INCLUDES -#include - -// EXTERNAL INCLUDES -#include -#include -#include -#include -#include - -namespace -{ -static int MajorAxisDistanceRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - { - return source.left - dest.right; - } - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - return dest.left - source.right; - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - { - return source.top - dest.bottom; - } - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - return dest.top - source.bottom; - } - default: - { - return 0; - } - } -} - -/** - * @return The distance from the edge furthest in the given direction - * of source to the edge nearest in the given direction of dest. - * If the dest is not in the direction from source, return 0. - */ -static int MajorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) -{ - return std::max(0, MajorAxisDistanceRaw(direction, source, dest)); -} - -static int MajorAxisDistanceToFarEdgeRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - { - return source.left - dest.left; - } - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - return dest.right - source.right; - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - { - return source.top - dest.top; - } - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - return dest.bottom - source.bottom; - } - default: - { - return 0; - } - } -} - -/** - * @return The distance along the major axis w.r.t the direction from the - * edge of source to the far edge of dest. - * If the dest is not in the direction from source, return 1 - */ -static int MajorAxisDistanceToFarEdge(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) -{ - return std::max(1, MajorAxisDistanceToFarEdgeRaw(direction, source, dest)); -} - -/** - * Find the distance on the minor axis w.r.t the direction to the nearest - * edge of the destination rectangle. - * @param direction the direction (up, down, left, right) - * @param source The source rect. - * @param dest The destination rect. - * @return The distance. - */ -static int MinorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect dest) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - // the distance between the center verticals - return std::abs( - (((source.top + source.bottom) * 0.5f) - - (((dest.top + dest.bottom) * 0.5f)))); - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - // the distance between the center horizontals - return std::abs( - (((source.left + source.right) * 0.5f) - - (((dest.left + dest.right) * 0.5f)))); - } - default: - { - return 0; - } - } -} - -/** - * Calculate distance given major and minor axis distances. - * @param majorAxisDistance The majorAxisDistance - * @param minorAxisDistance The minorAxisDistance - * @return The distance - */ -static int GetWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) -{ - return 13 * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance; -} - -/** - * Convert x,y,width,height coordinates into left, right, bottom, top coordinates. - * @param[in,out] rect The rect - */ -static void ConvertCoordinate(Dali::Rect& rect) -{ - // convert x, y, width, height -> left, right, bottom, top - float left = rect.x; - float right = rect.x + rect.width; - float bottom = rect.y + rect.height; - float top = rect.y; - - rect.left = left; - rect.right = right; - rect.bottom = bottom; - rect.top = top; -} - -/** - * Is destRect a candidate for the next focus given the direction? - * @param srcRect The source rect. - * @param destRect The dest rect. - * @param direction The direction (up, down, left, right) - * @return Whether destRect is a candidate. - */ -static bool IsCandidate(Dali::Rect srcRect, Dali::Rect destRect, Dali::Toolkit::Control::KeyboardFocus::Direction direction) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - { - return (srcRect.right > destRect.right || srcRect.left >= destRect.right) && srcRect.left > destRect.left; - } - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - return (srcRect.left < destRect.left || srcRect.right <= destRect.left) && srcRect.right < destRect.right; - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - { - return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom) && srcRect.top > destRect.top; - } - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top) && srcRect.bottom < destRect.bottom; - } - default: - { - return false; - } - } - return false; -} - -/** - * Is dest in a given direction from src? - * @param direction the direction (up, down, left, right) - * @param src The source rect - * @param dest The dest rect - */ -static bool IsToDirectionOf(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect src, Dali::Rect dest) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - { - return src.left >= dest.right; - } - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - return src.right <= dest.left; - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - { - return src.top >= dest.bottom; - } - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - return src.bottom <= dest.top; - } - default: - { - return false; - } - } -} - -/** - * Do the given direction's axis of rect1 and rect2 overlap? - * @param direction the direction (up, down, left, right) - * @param rect1 The first rect - * @param rect2 The second rect - * @return whether the beams overlap - */ -static bool BeamsOverlap(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect rect1, Dali::Rect rect2) -{ - switch(direction) - { - case Dali::Toolkit::Control::KeyboardFocus::LEFT: - case Dali::Toolkit::Control::KeyboardFocus::RIGHT: - { - return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom); - } - case Dali::Toolkit::Control::KeyboardFocus::UP: - case Dali::Toolkit::Control::KeyboardFocus::DOWN: - { - return (rect2.right >= rect1.left) && (rect2.left <= rect1.right); - } - default: - { - return false; - } - } -} - -/** - * One rectangle may be another candidate than another by virtue of being exclusively in the beam of the source rect. - * @param direction The direction (up, down, left, right) - * @param source The source rect - * @param rect1 The first rect - * @param rect2 The second rect - * @return Whether rect1 is a better candidate than rect2 by virtue of it being in src's beam - */ -static bool BeamBeats(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect source, Dali::Rect rect1, Dali::Rect rect2) -{ - const bool rect1InSrcBeam = BeamsOverlap(direction, source, rect1); - const bool rect2InSrcBeam = BeamsOverlap(direction, source, rect2); - // if rect1 isn't exclusively in the src beam, it doesn't win - if(rect2InSrcBeam || !rect1InSrcBeam) - { - return false; - } - // we know rect1 is in the beam, and rect2 is not - // if rect1 is to the direction of, and rect2 is not, rect1 wins. - // for example, for direction left, if rect1 is to the left of the source - // and rect2 is below, then we always prefer the in beam rect1, since rect2 - // could be reached by going down. - if(!IsToDirectionOf(direction, source, rect2)) - { - return true; - } - // for horizontal directions, being exclusively in beam always wins - if((direction == Dali::Toolkit::Control::KeyboardFocus::LEFT || direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)) - { - return true; - } - // for vertical directions, beams only beat up to a point: - // now, as long as rect2 isn't completely closer, rect1 wins - // e.g for direction down, completely closer means for rect2's top - // edge to be closer to the source's top edge than rect1's bottom edge. - return (MajorAxisDistance(direction, source, rect1) < MajorAxisDistanceToFarEdge(direction, source, rect2)); -} - -} // unnamed namespace - -namespace Dali -{ -namespace Toolkit -{ -namespace Internal -{ -FocusFinder::FocusFinder() -{ -} - -FocusFinder::~FocusFinder() -{ -} - -Actor FocusFinder::GetNearestFocusableActor(Actor& focusedActor, Toolkit::Control::KeyboardFocus::Direction direction) -{ - Actor nearestActor; - if(!focusedActor) - { - return nearestActor; - } - - Rect focusedRect = DevelActor::CalculateScreenExtents(focusedActor); - - // initialize the best candidate to something impossible - // (so the first plausible actor will become the best choice) - Rect bestCandidateRect = focusedRect; - switch(direction) - { - case Toolkit::Control::KeyboardFocus::LEFT: - { - bestCandidateRect.x += 1; - break; - } - case Toolkit::Control::KeyboardFocus::RIGHT: - { - bestCandidateRect.x -= 1; - break; - } - case Toolkit::Control::KeyboardFocus::UP: - { - bestCandidateRect.y += 1; - break; - } - case Toolkit::Control::KeyboardFocus::DOWN: - { - bestCandidateRect.y -= 1; - break; - } - default: - { - break; - } - } - - ConvertCoordinate(bestCandidateRect); - - ConvertCoordinate(focusedRect); - - Integration::SceneHolder window = Integration::SceneHolder::Get(focusedActor); - if(window) - { - Actor rootActor = window.GetRootLayer(); - nearestActor = FindNextFocus(rootActor, focusedActor, focusedRect, bestCandidateRect, direction); - } - return nearestActor; -} - -Actor FocusFinder::FindNextFocus(Actor& actor, Actor& focusedActor, Rect& focusedRect, Rect& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction) -{ - Actor nearestActor; - if(actor) - { - // Recursively children - const auto childCount = actor.GetChildCount(); - for(auto i = 0u; i < childCount; ++i) - { - Dali::Actor child = actor.GetChildAt(i); - if(child && child != focusedActor && child.GetProperty(Actor::Property::KEYBOARD_FOCUSABLE)) - { - Rect candidateRect = DevelActor::CalculateScreenExtents(child); - - // convert x, y, width, height -> left, right, bottom, top - ConvertCoordinate(candidateRect); - - if(IsBetterCandidate(direction, focusedRect, candidateRect, bestCandidateRect)) - { - bestCandidateRect = candidateRect; - nearestActor = child; - } - } - Actor nextActor = FindNextFocus(child, focusedActor, focusedRect, bestCandidateRect, direction); - if(nextActor) - { - nearestActor = nextActor; - } - } - } - return nearestActor; -} - -bool FocusFinder::IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect& focusedRect, Rect& candidateRect, Rect& bestCandidateRect) const -{ - // to be a better candidate, need to at least be a candidate in the first place - if(!IsCandidate(focusedRect, candidateRect, direction)) - { - return false; - } - // we know that candidateRect is a candidate.. if bestCandidateRect is not a candidate, - // candidateRect is better - if(!IsCandidate(focusedRect, bestCandidateRect, direction)) - { - return true; - } - // if candidateRect is better by beam, it wins - if(BeamBeats(direction, focusedRect, candidateRect, bestCandidateRect)) - { - return true; - } - // if bestCandidateRect is better, then candidateRect cant' be :) - if(BeamBeats(direction, focusedRect, bestCandidateRect, candidateRect)) - { - return false; - } - - // otherwise, do fudge-tastic comparison of the major and minor axis - return (GetWeightedDistanceFor( - MajorAxisDistance(direction, focusedRect, candidateRect), - MinorAxisDistance(direction, focusedRect, candidateRect)) < GetWeightedDistanceFor(MajorAxisDistance(direction, focusedRect, bestCandidateRect), - MinorAxisDistance(direction, focusedRect, bestCandidateRect))); -} - -} // namespace Internal - -} // namespace Toolkit - -} // namespace Dali diff --git a/dali-toolkit/internal/focus-manager/focus-finder-impl.h b/dali-toolkit/internal/focus-manager/focus-finder-impl.h deleted file mode 100644 index 012b1aa..0000000 --- a/dali-toolkit/internal/focus-manager/focus-finder-impl.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H -#define DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H - -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// EXTERNAL INCLUDES -#include - -// INTERNAL INCLUDES -#include - -namespace Dali -{ -namespace Integration -{ -class SceneHolder; - -} // namespace Integration - -namespace Toolkit -{ -namespace Internal -{ -class FocusFinder; - -/** - * @copydoc Toolkit::FocusFinder - */ -class FocusFinder : public Dali::BaseObject -{ -public: - /** - * Construct a new FocusFinder. - */ - FocusFinder(); - - /** - * @copydoc Toolkit::GetNearestFocusableActor - */ - Actor GetNearestFocusableActor(Actor& focusedActor, Toolkit::Control::KeyboardFocus::Direction direction); - -protected: - /** - * Destructor - */ - virtual ~FocusFinder(); - -private: - /** - * Find the next actor to take focus in root's descendants, starting from the actor. - * @param[in] actor The root actor. - * @param[in] focusedActor The current focused actor. - * @param[in] focusedRect The rect of current focused actor. - * @param[in] bestCandidateRect The current best candidate. - * @param[in] direction The direction. - * @return nearest Actor. - */ - Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect& focusedRect, Rect& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction); - - /** - * Is rect1 a better candidate than rect2 for a focus search in a particular - * direction from a source rect? This is the core routine that determines - * the order of focus searching. - * @param direction The direction (up, down, left, right) - * @param candidateRect The candidate rectangle - * @param bestCandidateRect The current best candidate. - * @return Whether the candidate is the new best. - */ - bool IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect& focusedRect, Rect& candidateRect, Rect& bestCandidateRect) const; - -private: - // Undefined - FocusFinder(const FocusFinder&); - - FocusFinder& operator=(const FocusFinder& rhs); -}; - -} // namespace Internal - -inline Internal::FocusFinder& GetImpl(Dali::Toolkit::FocusFinder& obj) -{ - DALI_ASSERT_ALWAYS(obj); - - Dali::BaseObject& handle = obj.GetBaseObject(); - - return static_cast(handle); -} - -inline const Internal::FocusFinder& GetImpl(const Dali::Toolkit::FocusFinder& obj) -{ - DALI_ASSERT_ALWAYS(obj); - - const Dali::BaseObject& handle = obj.GetBaseObject(); - - return static_cast(handle); -} - -} // namespace Toolkit - -} // namespace Dali - -#endif // DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H diff --git a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp index 0aa9ed1..8dadb11 100644 --- a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp +++ b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp @@ -500,7 +500,7 @@ bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocus::Direction else { // We should find it among the actors nearby. - nextFocusableActor = Toolkit::FocusFinder::Get().GetNearestFocusableActor(currentFocusActor, direction); + nextFocusableActor = Toolkit::FocusFinder::GetNearestFocusableActor(currentFocusActor, direction); } } -- 2.7.4