Custom keyboard focus algorithm support 25/115625/26
authorUmar <m.umar@partner.samsung.com>
Mon, 20 Feb 2017 18:50:07 +0000 (18:50 +0000)
committerUmar <m.umar@partner.samsung.com>
Thu, 23 Mar 2017 19:01:00 +0000 (19:01 +0000)
Change-Id: Iff543774fea50a5abb60da7f243224adb1a3c096

automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp
dali-toolkit/dali-toolkit.h
dali-toolkit/devel-api/file.list
dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.cpp [new file with mode: 0644]
dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h [new file with mode: 0644]
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h
plugins/dali-swig/SWIG/dali-toolkit.i
plugins/dali-swig/SWIG/dali.i
plugins/dali-swig/examples/hello-world.cs
plugins/dali-swig/manual/csharp/FocusManager.cs

index d32b7ff..1788df2 100644 (file)
@@ -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 <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;
@@ -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<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;
index c155cc2..1812c4d 100644 (file)
@@ -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.
index b9e6263..7e66e66 100755 (executable)
@@ -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 (file)
index 0000000..a56ece5
--- /dev/null
@@ -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 <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
+#include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
+
+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 (file)
index 0000000..18885a8
--- /dev/null
@@ -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 <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
index 29073b7..72f9359 100644 (file)
@@ -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<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 );
       }
     }
   }
@@ -798,6 +872,11 @@ bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTracke
   return connected;
 }
 
+void KeyboardFocusManager::SetCustomAlgorithm(CustomAlgorithmInterface& interface)
+{
+  mCustomAlgorithmInterface = &interface;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 468687a..6816200 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
+#include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
 
 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
index 751e653..e420c95 100755 (executable)
@@ -299,6 +299,8 @@ typedef Dali::IntrusivePtr<Dali::Toolkit::Ruler> RulerPtr;
 %include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
 %include <dali-toolkit/public-api/text/rendering-backend.h>
 
+%include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
+
 %template(ItemIdContainer) std::vector<unsigned int>;
 %template(Item) std::pair<unsigned int, Dali::Actor>;
 %template(ItemContainer) std::vector<std::pair<unsigned int, Dali::Actor>>;
index 861bc43..4e3fdb5 100755 (executable)
@@ -70,6 +70,7 @@
 #include <dali-toolkit/devel-api/builder/builder.h>
 
 #include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
+#include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
 
 #include <dali-toolkit/devel-api/controls/popup/popup.h>
 #include <dali-toolkit/devel-api/controls/progress-bar/progress-bar.h>
@@ -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.
 
index a392996..d775fbe 100755 (executable)
@@ -49,6 +49,7 @@ namespace MyCSharpExample
       _text.PointSize = 32.0f;
       _text.TextColor = Color.Magenta;
       stage.Add(_text);
+
     }
 
     // Callback for _animation finished signal handling
index cb7ae8f..ec64610 100755 (executable)
@@ -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<View>(current);
+          View proposedView = View.DownCast<View>(proposed);
+          return _customFocusAlgorithm.GetNextFocusableActor(currentView, proposedView, direction);
+      }
+  }
 }
 
 }