/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
#include <dali-toolkit/dali-toolkit.h>
#include <dali/integration-api/events/key-event-integ.h>
+#include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
#include <dali-toolkit/devel-api/controls/control-devel.h>
using namespace Dali;
namespace
{
+// Functors to test whether GetNextFocusableActor() method of CustomAlgorithmInterface is called when the keyboard focus is about to change
+class CustomAlgorithm : public Dali::Toolkit::DevelKeyboardFocusManager::CustomAlgorithmInterface
+{
+public:
+ CustomAlgorithm(bool& interfaceVerified)
+ : mInterfaceVerified(interfaceVerified),
+ mCurrentFocusedActor(),
+ mProposedActorToFocus(),
+ mDirection(Control::KeyboardFocus::LEFT)
+ {
+ }
+
+ Actor GetNextFocusableActor(Actor currentFocusedActor, Actor proposedActorToFocus, Control::KeyboardFocus::Direction direction)
+ {
+ tet_infoline("Verifying CustomAlgorithm()");
+
+ mInterfaceVerified = true;
+
+ mCurrentFocusedActor = currentFocusedActor;
+ mProposedActorToFocus = proposedActorToFocus;
+ mDirection = direction;
+
+ return mProposedActorToFocus;
+ }
+
+ void Reset()
+ {
+ mInterfaceVerified = false;
+ mCurrentFocusedActor = Actor();
+ mProposedActorToFocus = Actor();
+ mDirection = Control::KeyboardFocus::LEFT;
+ }
+
+ bool& mInterfaceVerified;
+ Actor mCurrentFocusedActor;
+ Actor mProposedActorToFocus;
+ Control::KeyboardFocus::Direction mDirection;
+};
+
// Functors to test whether PreFocusChange signal is emitted when the keyboard focus is about to change
class PreFocusChangeCallback : public Dali::ConnectionTracker
{
END_TEST;
}
+int UtcDaliKeyboardFocusManagerCustomAlgorithmMoveFocus(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline(" UtcDaliKeyboardFocusManagerCustomAlgorithmMoveFocus");
+
+ // 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 preFocusChangeSignalVerified = false;
+ PreFocusChangeCallback preFocusChangeCallback(preFocusChangeSignalVerified);
+ manager.PreFocusChangeSignal().Connect( &preFocusChangeCallback, &PreFocusChangeCallback::Callback );
+
+ bool focusChangedSignalVerified = false;
+ FocusChangedCallback focusChangedCallback(focusChangedSignalVerified);
+ manager.FocusChangedSignal().Connect( &focusChangedCallback, &FocusChangedCallback::Callback );
+
+ // Create the first actor and add it to the stage
+ Actor first = Actor::New();
+ first.SetKeyboardFocusable(true);
+ Stage::GetCurrent().Add(first);
+
+ // Create the second actor and add it to the stage
+ Actor second = Actor::New();
+ second.SetKeyboardFocusable(true);
+ Stage::GetCurrent().Add(second);
+
+ // Move the focus to the right
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == false);
+
+ // Because no layout control in the stage and no actor is focused, it should emit the PreFocusChange signal
+ DALI_TEST_CHECK(preFocusChangeCallback.mSignalVerified);
+ DALI_TEST_CHECK(preFocusChangeCallback.mCurrentFocusedActor == Actor());
+ DALI_TEST_CHECK(preFocusChangeCallback.mProposedActorToFocus == Actor());
+ DALI_TEST_CHECK(preFocusChangeCallback.mDirection == Control::KeyboardFocus::RIGHT);
+ preFocusChangeCallback.Reset();
+
+ bool customAlgorithmInterfaceVerified = false;
+ CustomAlgorithm customAlgorithm(customAlgorithmInterfaceVerified);
+ Toolkit::DevelKeyboardFocusManager::SetCustomAlgorithm(manager, customAlgorithm);
+
+ // Move the focus towards right
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == false);
+
+ // Because no layout control in the stage and the first actor is focused, it should invoke CustomAlgorithm
+ DALI_TEST_CHECK(customAlgorithm.mInterfaceVerified);
+ DALI_TEST_CHECK(customAlgorithm.mCurrentFocusedActor == Actor());
+ DALI_TEST_CHECK(customAlgorithm.mProposedActorToFocus == Actor());
+ DALI_TEST_CHECK(customAlgorithm.mDirection == Control::KeyboardFocus::RIGHT);
+ customAlgorithm.Reset();
+
+ // Check that the focus is set on the first actor
+ DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
+ DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards right
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == false);
+
+ // Because no layout control in the stage and the first actor is focused, it should invoke CustomAlgorithm
+ DALI_TEST_CHECK(customAlgorithm.mInterfaceVerified);
+ DALI_TEST_CHECK(customAlgorithm.mCurrentFocusedActor == first);
+ DALI_TEST_CHECK(customAlgorithm.mProposedActorToFocus == Actor());
+ DALI_TEST_CHECK(customAlgorithm.mDirection == Control::KeyboardFocus::RIGHT);
+ customAlgorithm.Reset();
+
+ // Check that the focus is set on the second actor
+ DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true);
+ DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == first);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == second);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards up
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::UP) == false);
+
+ // Because no layout control in the stage and no actor is focused, it should invoke CustomAlgorithm
+ DALI_TEST_CHECK(customAlgorithm.mInterfaceVerified);
+ DALI_TEST_CHECK(customAlgorithm.mCurrentFocusedActor == second);
+ DALI_TEST_CHECK(customAlgorithm.mProposedActorToFocus == Actor());
+ DALI_TEST_CHECK(customAlgorithm.mDirection == Control::KeyboardFocus::UP);
+ customAlgorithm.Reset();
+ DALI_TEST_CHECK(!focusChangedCallback.mSignalVerified);
+
+ END_TEST;
+}
+int UtcDaliKeyboardFocusManagerFocusablePropertiesMoveFocus(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline(" UtcDaliKeyboardFocusManagerCustomAlgorithmMoveFocus");
+
+ // 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();
+ button1.SetKeyboardFocusable(true);
+ button2.SetKeyboardFocusable(true);
+ Stage::GetCurrent().Add(button1);
+ Stage::GetCurrent().Add(button2);
+
+ // Set the focus to the button1
+ 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();
+
+ // set the navigation properties of button1
+ button1.SetProperty(Toolkit::DevelControl::Property::LEFT_FOCUSABLE_ACTOR_ID, Property::Value((int)button2.GetId()));
+ button1.SetProperty(Toolkit::DevelControl::Property::RIGHT_FOCUSABLE_ACTOR_ID, Property::Value((int)button2.GetId()));
+ button1.SetProperty(Toolkit::DevelControl::Property::UP_FOCUSABLE_ACTOR_ID, Property::Value((int)button2.GetId()));
+ button1.SetProperty(Toolkit::DevelControl::Property::DOWN_FOCUSABLE_ACTOR_ID, Property::Value((int)button2.GetId()));
+
+ // set the navigation properties of button2
+ button2.SetProperty(Toolkit::DevelControl::Property::LEFT_FOCUSABLE_ACTOR_ID, Property::Value((int)button1.GetId()));
+ button2.SetProperty(Toolkit::DevelControl::Property::RIGHT_FOCUSABLE_ACTOR_ID, Property::Value((int)button1.GetId()));
+ button2.SetProperty(Toolkit::DevelControl::Property::UP_FOCUSABLE_ACTOR_ID, Property::Value((int)button1.GetId()));
+ button2.SetProperty(Toolkit::DevelControl::Property::DOWN_FOCUSABLE_ACTOR_ID, Property::Value((int)button1.GetId()));
+
+ // Move the focus towards left
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == 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 right
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == 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 == button2);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards up
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::UP) == 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
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::DOWN) == 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 == button2);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+ focusChangedCallback.Reset();
+
+ // Create a 1x1 table view and try to move focus inside it
+ TableView tableView = TableView::New( 1, 1 );
+ Stage::GetCurrent().Add(tableView);
+
+ PushButton button = PushButton::New();
+ button.SetKeyboardFocusable(true);
+ tableView.AddChild(button, TableView::CellPosition(0, 0));
+
+ // set the navigation properties of button3
+ button.SetProperty(Toolkit::DevelControl::Property::LEFT_FOCUSABLE_ACTOR_ID, Property::Value((int)button1.GetId()));
+
+ // Set the focus to the button
+ DALI_TEST_CHECK(manager.SetCurrentFocusActor(button) == true);
+ DALI_TEST_CHECK(manager.GetCurrentFocusActor() == button);
+ DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+ DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button1);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button);
+ focusChangedCallback.Reset();
+
+ // Move the focus towards left
+ DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == 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 == button);
+ DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+ focusChangedCallback.Reset();
+
+ END_TEST;
+}
+
int UtcDaliKeyboardFocusManagerClearFocus(void)
{
ToolkitTestApplication application;
--- /dev/null
+#ifndef DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_DEVEL_H
+#define DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_DEVEL_H
+
+/*
+ * Copyright (c) 2017 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 <dali-toolkit/public-api/controls/control.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace DevelKeyboardFocusManager
+{
+/**
+ * @brief Interface used to provide custom keyboard focus algorithm for retrieving the next focusable actor.
+ *
+ * The application / toolkit can implement the interface and override the keyboard focus behaviour.
+ * Upon providing an implementation of this interface, the PreFocusChangeSignal is no longer emitted.
+ * If focus is changing within a layout container, then the layout container is queried first to provide
+ * the next focusable actor. If this does not provide a valid actor, then the Keyboard FocusManager will
+ * check focusable properties to determine next focusable actor. If focusable properties are not set,
+ * then the Keyboard FocusManager calls the GetNextFocusableActor() method of this interface.
+ */
+class CustomAlgorithmInterface
+{
+public:
+
+ /**
+ * @brief Virtual destructor.
+ */
+ virtual ~CustomAlgorithmInterface() {};
+
+ /**
+ * @brief Called by the KeyboardFocusManager to get the next focusable actor.
+ *
+ * @param[in] current The current focused actor
+ * @param[in] proposed The proposed focused actor
+ * @param[in] direction The direction of focus movement
+ * @return A handle to the next focusable actor
+ */
+ virtual Actor GetNextFocusableActor(Actor current, Actor proposed, Control::KeyboardFocus::Direction direction) = 0;
+};
+
+/**
+ * @brief Provide the implementation of custom Focus algorithm interface
+ *
+ * @param[in] keyboardFocusManager The instance of KeyboardFocusManager
+ * @param[in] interface The user's implementation of CustomAlgorithmInterface
+ * @see DevelKeyboardFocusManager::CustomAlgorithmInterface
+ */
+void SetCustomAlgorithm(KeyboardFocusManager keyboardFocusManager, CustomAlgorithmInterface& interface);
+
+} // namespace DevelKeyboardFocusManager
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_DEVEL_H
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
mIsFocusIndicatorEnabled( false ),
mIsWaitingKeyboardFocusChangeCommit( false ),
mFocusHistory(),
- mSlotDelegate( this )
+ mSlotDelegate( this ),
+ mCustomAlgorithmInterface(NULL)
{
// TODO: Get FocusIndicatorEnable constant from stylesheet to set mIsFocusIndicatorEnabled.
Toolkit::KeyInputFocusManager::Get().UnhandledKeyEventSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnKeyEvent);
bool succeed = false;
// Go through the actor's hierarchy until we find a layout control that knows how to move the focus
- Toolkit::Control parentLayoutControl = GetParentLayoutControl(currentFocusActor);
- while(parentLayoutControl && !succeed)
+ Toolkit::Control parentLayoutControl = GetParentLayoutControl( currentFocusActor );
+ while( parentLayoutControl && !succeed )
{
- succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, currentFocusActor, direction);
- parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
+ succeed = DoMoveFocusWithinLayoutControl( parentLayoutControl, currentFocusActor, direction );
+ parentLayoutControl = GetParentLayoutControl( parentLayoutControl );
}
- if(!succeed && !mPreFocusChangeSignal.Empty())
+ if( !succeed )
{
- // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
- mIsWaitingKeyboardFocusChangeCommit = true;
- Actor nextFocusableActor = mPreFocusChangeSignal.Emit(currentFocusActor, Actor(), direction);
- mIsWaitingKeyboardFocusChangeCommit = false;
+ Actor nextFocusableActor;
+
+ Toolkit::Control currentFocusControl = Toolkit::Control::DownCast(currentFocusActor);
+
+ // If the current focused actor is a control, then find the next focusable actor via the focusable properties.
+ if( currentFocusControl )
+ {
+ int actorId = -1;
+ Property::Index index = Property::INVALID_INDEX;
+ Property::Value value;
+
+ // Find property index based upon focus direction
+ switch ( direction )
+ {
+ case Toolkit::Control::KeyboardFocus::LEFT:
+ {
+ index = Toolkit::DevelControl::Property::LEFT_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::RIGHT:
+ {
+ index = Toolkit::DevelControl::Property::RIGHT_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::UP:
+ {
+ index = Toolkit::DevelControl::Property::UP_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::DOWN:
+ {
+ index = Toolkit::DevelControl::Property::DOWN_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // If the focusable property is set then determine next focusable actor
+ if( index != Property::INVALID_INDEX)
+ {
+ value = currentFocusActor.GetProperty( index );
+ actorId = value.Get<int>();
+
+ // If actor's id is valid then find actor form actor's id. The actor should be on the stage.
+ if( actorId != -1 )
+ {
+ if( currentFocusActor.GetParent() )
+ {
+ nextFocusableActor = currentFocusActor.GetParent().FindChildById( actorId );
+ }
- if ( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
+ if( !nextFocusableActor )
+ {
+ nextFocusableActor = Stage::GetCurrent().GetRootLayer().FindChildById( actorId );
+ }
+ }
+ }
+ }
+
+ if( !nextFocusableActor )
+ {
+ // If the implementation of CustomAlgorithmInterface is provided then the PreFocusChangeSignal is no longer emitted.
+ if( mCustomAlgorithmInterface )
+ {
+ mIsWaitingKeyboardFocusChangeCommit = true;
+ nextFocusableActor = mCustomAlgorithmInterface->GetNextFocusableActor( currentFocusActor, Actor(), direction );
+ mIsWaitingKeyboardFocusChangeCommit = false;
+ }
+ else if( !mPreFocusChangeSignal.Empty() )
+ {
+ // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
+ mIsWaitingKeyboardFocusChangeCommit = true;
+ nextFocusableActor = mPreFocusChangeSignal.Emit( currentFocusActor, Actor(), direction );
+ mIsWaitingKeyboardFocusChangeCommit = false;
+ }
+ }
+
+ if( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
{
// Whether the next focusable actor is a layout control
- if(IsLayoutControl(nextFocusableActor))
+ if( IsLayoutControl( nextFocusableActor ) )
{
// If so, move the focus inside it.
- Toolkit::Control layoutControl = Toolkit::Control::DownCast(nextFocusableActor);
- succeed = DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
+ Toolkit::Control layoutControl = Toolkit::Control::DownCast( nextFocusableActor) ;
+ succeed = DoMoveFocusWithinLayoutControl( layoutControl, currentFocusActor, direction );
}
else
{
// Otherwise, just set focus to the next focusable actor
- succeed = SetCurrentFocusActor(nextFocusableActor);
+ succeed = SetCurrentFocusActor( nextFocusableActor );
}
}
}
return connected;
}
+void KeyboardFocusManager::SetCustomAlgorithm(CustomAlgorithmInterface& interface)
+{
+ mCustomAlgorithmInterface = &interface;
+}
+
} // namespace Internal
} // namespace Toolkit