From abc25cd0d8c1551e54f6ac0e2c612300f650d2a5 Mon Sep 17 00:00:00 2001 From: Umar Date: Mon, 20 Feb 2017 18:50:07 +0000 Subject: [PATCH] Custom keyboard focus algorithm support Change-Id: Iff543774fea50a5abb60da7f243224adb1a3c096 --- .../dali-toolkit/utc-Dali-KeyboardFocusManager.cpp | 258 ++++++++++++++++++++- dali-toolkit/dali-toolkit.h | 2 +- dali-toolkit/devel-api/file.list | 4 +- .../focus-manager/keyboard-focus-manager-devel.cpp | 40 ++++ .../focus-manager/keyboard-focus-manager-devel.h | 80 +++++++ .../focus-manager/keyboard-focus-manager-impl.cpp | 111 +++++++-- .../focus-manager/keyboard-focus-manager-impl.h | 10 + plugins/dali-swig/SWIG/dali-toolkit.i | 2 + plugins/dali-swig/SWIG/dali.i | 3 + plugins/dali-swig/examples/hello-world.cs | 1 + plugins/dali-swig/manual/csharp/FocusManager.cs | 33 +++ 11 files changed, 525 insertions(+), 19 deletions(-) create mode 100644 dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.cpp create mode 100644 dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h diff --git a/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp b/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp index d32b7ff..1788df2 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -24,6 +24,7 @@ #include #include +#include #include using namespace Dali; @@ -43,6 +44,45 @@ void utc_dali_toolkit_keyboard_focus_manager_cleanup(void) 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 { @@ -434,6 +474,222 @@ int UtcDaliKeyboardFocusManagerMoveFocus(void) 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(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(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(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(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(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; diff --git a/dali-toolkit/dali-toolkit.h b/dali-toolkit/dali-toolkit.h index c155cc2..1812c4d 100644 --- a/dali-toolkit/dali-toolkit.h +++ b/dali-toolkit/dali-toolkit.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_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. diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index b9e6263..7e66e66 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -24,6 +24,7 @@ devel_api_src_files = \ $(devel_api_src_dir)/controls/text-controls/text-selection-toolbar.cpp \ $(devel_api_src_dir)/controls/tool-bar/tool-bar.cpp \ $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.cpp \ + $(devel_api_src_dir)/focus-manager/keyboard-focus-manager-devel.cpp \ $(devel_api_src_dir)/image-loader/atlas-upload-observer.cpp \ $(devel_api_src_dir)/image-loader/image-atlas.cpp \ $(devel_api_src_dir)/scripting/script.cpp \ @@ -105,7 +106,8 @@ devel_api_shadow_view_header_files = \ $(devel_api_src_dir)/controls/shadow-view/shadow-view.h devel_api_focus_manager_header_files = \ - $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.h + $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.h \ + $(devel_api_src_dir)/focus-manager/keyboard-focus-manager-devel.h devel_api_image_loader_header_files = \ $(devel_api_src_dir)/image-loader/atlas-upload-observer.h \ diff --git a/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.cpp b/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.cpp new file mode 100644 index 0000000..a56ece5 --- /dev/null +++ b/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.cpp @@ -0,0 +1,40 @@ +/* + * 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. + * + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace DevelKeyboardFocusManager +{ + +void SetCustomAlgorithm(KeyboardFocusManager keyboardFocusManager, CustomAlgorithmInterface& interface) +{ + GetImpl(keyboardFocusManager).SetCustomAlgorithm(interface); +} + +} // namespace DevelKeyboardFocusManager + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h b/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h new file mode 100644 index 0000000..18885a8 --- /dev/null +++ b/dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h @@ -0,0 +1,80 @@ +#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 + +// INTERNAL INCLUDES +#include + +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 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 29073b7..72f9359 100644 --- a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp +++ b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -121,7 +121,8 @@ KeyboardFocusManager::KeyboardFocusManager() 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); @@ -268,33 +269,106 @@ bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocus::Direction 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(); + + // 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 ); } } } @@ -798,6 +872,11 @@ bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTracke return connected; } +void KeyboardFocusManager::SetCustomAlgorithm(CustomAlgorithmInterface& interface) +{ + mCustomAlgorithmInterface = &interface; +} + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h index 468687a..6816200 100644 --- a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h +++ b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h @@ -23,6 +23,7 @@ // INTERNAL INCLUDES #include +#include namespace Dali { @@ -40,6 +41,8 @@ class KeyboardFocusManager : public Dali::BaseObject { public: + typedef Toolkit::DevelKeyboardFocusManager::CustomAlgorithmInterface CustomAlgorithmInterface; + /** * @copydoc Toolkit::KeyboardFocusManager::Get */ @@ -110,6 +113,11 @@ public: */ void MoveFocusBackward(); + /** + * @copydoc Toolkit::DevelKeyboardFocusManager::SetCustomAlgorithm + */ + void SetCustomAlgorithm(CustomAlgorithmInterface& interface); + public: /** @@ -254,6 +262,8 @@ private: SlotDelegate< KeyboardFocusManager > mSlotDelegate; + CustomAlgorithmInterface* mCustomAlgorithmInterface; ///< The user's (application / toolkit) implementation of CustomAlgorithmInterface + }; } // namespace Internal diff --git a/plugins/dali-swig/SWIG/dali-toolkit.i b/plugins/dali-swig/SWIG/dali-toolkit.i index 751e653..e420c95 100755 --- a/plugins/dali-swig/SWIG/dali-toolkit.i +++ b/plugins/dali-swig/SWIG/dali-toolkit.i @@ -299,6 +299,8 @@ typedef Dali::IntrusivePtr RulerPtr; %include %include +%include + %template(ItemIdContainer) std::vector; %template(Item) std::pair; %template(ItemContainer) std::vector>; diff --git a/plugins/dali-swig/SWIG/dali.i b/plugins/dali-swig/SWIG/dali.i index 861bc43..4e3fdb5 100755 --- a/plugins/dali-swig/SWIG/dali.i +++ b/plugins/dali-swig/SWIG/dali.i @@ -70,6 +70,7 @@ #include #include +#include #include #include @@ -245,6 +246,8 @@ using namespace Dali::Toolkit; %feature("notabstract") Dali::Toolkit::FixedRuler; %feature("notabstract") Dali::Toolkit::DefaultRuler; +%feature("director") Dali::Toolkit::DevelKeyboardFocusManager::CustomAlgorithmInterface; + // Note... all the typemap declarations have to be included before the DALi C++ head files are include otherwise // they have no effect. diff --git a/plugins/dali-swig/examples/hello-world.cs b/plugins/dali-swig/examples/hello-world.cs index a392996..d775fbe 100755 --- a/plugins/dali-swig/examples/hello-world.cs +++ b/plugins/dali-swig/examples/hello-world.cs @@ -49,6 +49,7 @@ namespace MyCSharpExample _text.PointSize = 32.0f; _text.TextColor = Color.Magenta; stage.Add(_text); + } // Callback for _animation finished signal handling diff --git a/plugins/dali-swig/manual/csharp/FocusManager.cs b/plugins/dali-swig/manual/csharp/FocusManager.cs index cb7ae8f..ec64610 100755 --- a/plugins/dali-swig/manual/csharp/FocusManager.cs +++ b/plugins/dali-swig/manual/csharp/FocusManager.cs @@ -22,6 +22,7 @@ using System.Runtime.InteropServices; public class FocusManager : BaseHandle { private global::System.Runtime.InteropServices.HandleRef swigCPtr; + private CustomAlgorithmInterfaceWrapper _customAlgorithmInterfaceWrapper; internal FocusManager(global::System.IntPtr cPtr, bool cMemoryOwn) : base(NDalicManualPINVOKE.FocusManager_SWIGUpcast(cPtr), cMemoryOwn) { swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); @@ -533,6 +534,14 @@ public class PreFocusChangeEventArgs : EventArgs return ret; } + public void SetCustomAlgorithm(ICustomFocusAlgorithm arg0) { + _customAlgorithmInterfaceWrapper = new CustomAlgorithmInterfaceWrapper(); + _customAlgorithmInterfaceWrapper.SetFocusAlgorithm(arg0); + + NDalicPINVOKE.SetCustomAlgorithm(swigCPtr, CustomAlgorithmInterface.getCPtr(_customAlgorithmInterfaceWrapper)); + if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); + } + public PreFocusChangeSignal PreFocusChangeSignal() { PreFocusChangeSignal ret = new PreFocusChangeSignal(NDalicManualPINVOKE.FocusManager_PreFocusChangeSignal(swigCPtr), false); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); @@ -567,7 +576,31 @@ public class PreFocusChangeEventArgs : EventArgs } } + public interface ICustomFocusAlgorithm + { + View GetNextFocusableActor(View current, View proposed, View.KeyboardFocus.Direction direction); + } + + private class CustomAlgorithmInterfaceWrapper : CustomAlgorithmInterface + { + private FocusManager.ICustomFocusAlgorithm _customFocusAlgorithm; + public CustomAlgorithmInterfaceWrapper() + { + } + + public void SetFocusAlgorithm(FocusManager.ICustomFocusAlgorithm customFocusAlgorithm) + { + _customFocusAlgorithm = customFocusAlgorithm; + } + + public override Actor GetNextFocusableActor(Actor current, Actor proposed, View.KeyboardFocus.Direction direction) + { + View currentView = View.DownCast(current); + View proposedView = View.DownCast(proposed); + return _customFocusAlgorithm.GetNextFocusableActor(currentView, proposedView, direction); + } + } } } -- 2.7.4