Merge branch 'devel/master' into devel/graphics 95/258795/2
authorDavid Steele <david.steele@samsung.com>
Tue, 25 May 2021 23:28:35 +0000 (00:28 +0100)
committerDavid Steele <david.steele@samsung.com>
Wed, 26 May 2021 10:43:20 +0000 (11:43 +0100)
Change-Id: I51fef49392010ab166900229c74b05bb0520fcd3

18 files changed:
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-buffer.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
build/tizen/CMakeLists.txt
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/file.list
dali-toolkit/devel-api/focus-manager/focus-finder.cpp [new file with mode: 0644]
dali-toolkit/devel-api/focus-manager/focus-finder.h [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/focus-manager/focus-finder-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/focus-manager/focus-finder-impl.h [new file with mode: 0644]
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

index fb06c3d..21eecf4 100644 (file)
@@ -87,8 +87,8 @@ void TestGraphicsBuffer::BindAsUniformBuffer(const TestGraphicsProgram* program,
   Graphics::UniformBlockInfo uboInfo{};
   reflection->GetUniformBlock(0, uboInfo);
 
-  auto offset = uboBinding.offset;
-  auto* data = memory.data() + offset;
+  auto  offset = uboBinding.offset;
+  auto* data   = memory.data() + offset;
 
   for(const auto& member : uboInfo.members)
   {
index 29faa6d..1cd3de5 100644 (file)
@@ -2743,7 +2743,7 @@ namespace
 
 static int gResourceReadySignalCounter = 0;
 
-void OnResourceReadySignal( Control control )
+void OnResourceReadySignal01( Control control )
 {
   gResourceReadySignalCounter++;
 
@@ -2764,7 +2764,7 @@ void OnResourceReadySignal( Control control )
   }
 }
 
-void OnResourceReadySignal01( Control control )
+void OnResourceReadySignal02( Control control )
 {
   if(++gResourceReadySignalCounter == 1)
   {
@@ -2776,6 +2776,34 @@ void OnResourceReadySignal01( Control control )
   }
 }
 
+ImageView gImageView1;
+ImageView gImageView2;
+ImageView gImageView3;
+
+void OnResourceReadySignal03( Control control )
+{
+  if(gResourceReadySignalCounter == 0)
+  {
+    // Queue loading
+    // 1. Use cached image, then UploadComplete will be called right after OnResourceReadySignal03.
+    gImageView2[ImageView::Property::IMAGE] = gImage_34_RGBA;
+
+    // 2. Load a new image
+    gImageView3[ImageView::Property::IMAGE] = TEST_IMAGE_1;
+
+    // 3. Use the new image again
+    gImageView1[ImageView::Property::IMAGE] = TEST_IMAGE_1;
+    gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal03);
+  }
+  else if(gResourceReadySignalCounter == 1)
+  {
+    // This is called from TextureManager::ProcessQueuedTextures().
+    gImageView1.Unparent();
+    gImageView1.Reset();
+  }
+  gResourceReadySignalCounter++;
+}
+
 }
 
 int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
@@ -2787,7 +2815,7 @@ int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
   gResourceReadySignalCounter = 0;
 
   ImageView imageView = ImageView::New( gImage_34_RGBA );
-  imageView.ResourceReadySignal().Connect( &OnResourceReadySignal );
+  imageView.ResourceReadySignal().Connect( &OnResourceReadySignal01 );
 
   application.GetScene().Add( imageView );
 
@@ -2829,7 +2857,7 @@ int UtcDaliImageViewSetImageOnResourceReadySignal02(void)
   gResourceReadySignalCounter = 0;
 
   ImageView imageView = ImageView::New( gImage_34_RGBA );
-  imageView.ResourceReadySignal().Connect( &OnResourceReadySignal01 );
+  imageView.ResourceReadySignal().Connect( &OnResourceReadySignal02 );
 
   application.GetScene().Add( imageView );
 
@@ -2847,3 +2875,32 @@ int UtcDaliImageViewSetImageOnResourceReadySignal02(void)
 
   END_TEST;
 }
+
+int UtcDaliImageViewSetImageOnResourceReadySignal03(void)
+{
+  tet_infoline("Test setting image from within signal handler.");
+
+  ToolkitTestApplication application;
+
+  gResourceReadySignalCounter = 0;
+
+  gImageView1 = ImageView::New(gImage_34_RGBA);
+  application.GetScene().Add(gImageView1);
+
+  // Wait for loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  gImageView2 = ImageView::New(gImage_600_RGB);
+  gImageView2.ResourceReadySignal().Connect(&OnResourceReadySignal03);
+  application.GetScene().Add(gImageView2);
+
+  gImageView3 = ImageView::New();
+  application.GetScene().Add(gImageView3);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
index 720f596..be3cf44 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1616,4 +1616,172 @@ int UtcDaliKeyboardFocusManagerFocusPerWindow(void)
   END_TEST;
 }
 
+int UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" UtcDaliKeyboardFocusManagerWithoutFocusablePropertiesMoveFocus");
+
+  // Register Type
+  TypeInfo type;
+  type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" );
+  DALI_TEST_CHECK( type );
+  BaseHandle handle = type.CreateInstance();
+  DALI_TEST_CHECK( handle );
+
+  KeyboardFocusManager manager = KeyboardFocusManager::Get();
+  DALI_TEST_CHECK(manager);
+
+  bool focusChangedSignalVerified = false;
+  FocusChangedCallback focusChangedCallback(focusChangedSignalVerified);
+  manager.FocusChangedSignal().Connect( &focusChangedCallback, &FocusChangedCallback::Callback );
+
+  PushButton button1 = PushButton::New();
+  PushButton button2 = PushButton::New();
+  PushButton button3 = PushButton::New();
+  PushButton button4 = PushButton::New();
+  PushButton button5 = PushButton::New();
+
+  button1.SetProperty( Actor::Property::SIZE, Vector2( 50, 50 ) );
+  button2.SetProperty( Actor::Property::SIZE, Vector2( 50, 50 ) );
+  button3.SetProperty( Actor::Property::SIZE, Vector2( 50, 50 ) );
+  button4.SetProperty( Actor::Property::SIZE, Vector2( 50, 50 ) );
+  button5.SetProperty( Actor::Property::SIZE, Vector2( 50, 50 ) );
+
+  button1.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
+  button2.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
+  button3.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
+  button4.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
+  button5.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
+
+  application.GetScene().Add(button1);
+  application.GetScene().Add(button2);
+  application.GetScene().Add(button3);
+  application.GetScene().Add(button4);
+  application.GetScene().Add(button5);
+
+  // set position
+  // button1 -- button2
+  //   |           |
+  //   |    button5|
+  // button3 -- button4
+  button1.SetProperty( Actor::Property::POSITION, Vector2(0.0f, 0.0f));
+  button2.SetProperty( Actor::Property::POSITION, Vector2(100.0f, 0.0f));
+  button3.SetProperty( Actor::Property::POSITION, Vector2(0.0f, 100.0f));
+  button4.SetProperty( Actor::Property::POSITION, Vector2(100.0f, 100.0f));
+  button5.SetProperty( Actor::Property::POSITION, Vector2(60.0f, 60.0f));
+
+  // flush the queue and render once
+  application.SendNotification();
+  application.Render();
+
+  // Set the focus to the button1
+  // [button1] -- button2
+  //   |           |
+  //   |    button5|
+  // button3 -- button4
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(button1) == true);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == button1);
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+  focusChangedCallback.Reset();
+
+  // without set the navigation properties, but we can focus move
+
+  // Move the focus towards right
+  // button1 -- [button2]
+  //   |           |
+  //   |    button5|
+  // button3 -- button4
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true);
+
+  // Confirm whether focus is moved to button2
+  DALI_TEST_EQUALS(button2.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button1);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button2);
+  focusChangedCallback.Reset();
+
+  // Move the focus towards down
+  // button1 -- button2
+  //   |           |
+  //   |  [button5]|
+  // button3 -- button4
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::DOWN) == true);
+
+  // Confirm whether focus is moved to button5
+  DALI_TEST_EQUALS(button5.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button2);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button5);
+  focusChangedCallback.Reset();
+
+  // Move the focus towards right
+  // button1 -- button2
+  //   |           |
+  //   |    button5|
+  // button3 -- [button4]
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::RIGHT) == true);
+
+  // Confirm whether focus is moved to button4
+  DALI_TEST_EQUALS(button4.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button5);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button4);
+  focusChangedCallback.Reset();
+
+  // Move the focus towards left
+  // button1 -- button2
+  //   |           |
+  //   |  [button5]|
+  // button3 -- button4
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true);
+
+  // Confirm whether focus is moved to button5
+  DALI_TEST_EQUALS(button5.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button4);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button5);
+  focusChangedCallback.Reset();
+
+  // Move the focus towards left
+  // button1 -- button2
+  //   |           |
+  //   |    button5|
+  //[button3] -- button4
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == true);
+
+  // Confirm whether focus is moved to button3
+  DALI_TEST_EQUALS(button3.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button5);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button3);
+  focusChangedCallback.Reset();
+
+  // Move the focus towards up
+  //[button1]-- button2
+  //   |           |
+  //   |    button5|
+  // button3 -- button4
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::UP) == true);
+
+  // Confirm whether focus is moved to button1
+  DALI_TEST_EQUALS(button1.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION );
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == button3);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == button1);
+  focusChangedCallback.Reset();
+
+
+  // Move the focus towards left. The focus move will fail as no way to move it upwards
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::LEFT) == false);
+
+  // Move the focus toward page up/down. The focus move will fail as invalid direction.
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::PAGE_UP) == false);
+  DALI_TEST_CHECK(manager.MoveFocus(Control::KeyboardFocus::PAGE_DOWN) == false);
+  focusChangedCallback.Reset();
+
+  END_TEST;
+}
 
index 626432e..fd0a18e 100644 (file)
@@ -105,7 +105,7 @@ const char* const PROPERTY_NAME_MATCH_SYSTEM_LANGUAGE_DIRECTION      = "matchSys
 const char* const PROPERTY_NAME_MAX_LENGTH                           = "maxLength";
 const char* const PROPERTY_NAME_FONT_SIZE_SCALE                      = "fontSizeScale";
 const char* const PROPERTY_NAME_GRAB_HANDLE_COLOR                    = "grabHandleColor";
-
+const char* const PROPERTY_NAME_ENABLE_GRAB_HANDLE_POPUP             = "enableGrabHandlePopup";
 
 const Vector4 PLACEHOLDER_TEXT_COLOR( 0.8f, 0.8f, 0.8f, 0.8f );
 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color.
@@ -512,7 +512,7 @@ int UtcDaliTextEditorGetPropertyP(void)
   DALI_TEST_CHECK( editor.GetPropertyIndex( PROPERTY_NAME_MATCH_SYSTEM_LANGUAGE_DIRECTION ) == DevelTextEditor::Property::MATCH_SYSTEM_LANGUAGE_DIRECTION );
   DALI_TEST_CHECK( editor.GetPropertyIndex( PROPERTY_NAME_MAX_LENGTH ) == DevelTextEditor::Property::MAX_LENGTH );
   DALI_TEST_CHECK( editor.GetPropertyIndex( PROPERTY_NAME_GRAB_HANDLE_COLOR ) == DevelTextEditor::Property::GRAB_HANDLE_COLOR );
-
+  DALI_TEST_CHECK( editor.GetPropertyIndex( PROPERTY_NAME_ENABLE_GRAB_HANDLE_POPUP ) == DevelTextEditor::Property::ENABLE_GRAB_HANDLE_POPUP );
 
   END_TEST;
 }
@@ -931,6 +931,10 @@ int UtcDaliTextEditorSetPropertyP(void)
   editor.SetProperty( DevelTextEditor::Property::GRAB_HANDLE_COLOR, Color::GREEN );
   DALI_TEST_EQUALS( editor.GetProperty<Vector4>( DevelTextEditor::Property::GRAB_HANDLE_COLOR ), Color::GREEN, TEST_LOCATION );
 
+  // Test the ENABLE_GRAB_HANDLE_POPUP property
+  editor.SetProperty( DevelTextEditor::Property::ENABLE_GRAB_HANDLE_POPUP, false );
+  DALI_TEST_EQUALS( editor.GetProperty<bool>( DevelTextEditor::Property::ENABLE_GRAB_HANDLE_POPUP ), false, TEST_LOCATION);
+
   application.SendNotification();
   application.Render();
 
index cd463b5..2258fab 100644 (file)
@@ -308,6 +308,7 @@ ENDIF()
 
 IF(NOT ANDROID)
   ADD_EXECUTABLE(${SHADER_GENERATOR_NAME} ${SHADER_GENERATOR_SOURCES})
+  TARGET_LINK_LIBRARIES( ${SHADER_GENERATOR_NAME} ${COVERAGE} )
   INSTALL(TARGETS ${SHADER_GENERATOR_NAME} RUNTIME DESTINATION bin)
 ELSE()
   # Need to build dali-shader-generator using the host compiler, not the android cross-compiler so
index c73c582..4012afa 100644 (file)
@@ -187,6 +187,13 @@ enum Type
    * @details Name "grabHandleColor", type Property::VECTOR4.
    */
   GRAB_HANDLE_COLOR,
+
+  /**
+   * @brief Enables the grab handle popup for text selection.
+   * @details Name "enableGrabHandlePopup", type Property::BOOLEAN.
+   * @note The default value is true, which means the grab handle popup is enabled by default.
+   */
+  ENABLE_GRAB_HANDLE_POPUP,
 };
 
 } // namespace Property
index f583d3c..e94f60b 100755 (executable)
@@ -46,6 +46,7 @@ SET( devel_api_src_files
   ${devel_api_src_dir}/controls/web-view/web-view.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}/focus-manager/focus-finder.cpp
   ${devel_api_src_dir}/image-loader/async-image-loader-devel.cpp
   ${devel_api_src_dir}/image-loader/atlas-upload-observer.cpp
   ${devel_api_src_dir}/image-loader/image-atlas.cpp
@@ -188,6 +189,7 @@ SET( devel_api_shadow_view_header_files
 SET( devel_api_focus_manager_header_files
   ${devel_api_src_dir}/focus-manager/keyinput-focus-manager.h
   ${devel_api_src_dir}/focus-manager/keyboard-focus-manager-devel.h
+  ${devel_api_src_dir}/focus-manager/focus-finder.h
 )
 
 SET( devel_api_image_loader_header_files
diff --git a/dali-toolkit/devel-api/focus-manager/focus-finder.cpp b/dali-toolkit/devel-api/focus-manager/focus-finder.cpp
new file mode 100644 (file)
index 0000000..d12ed11
--- /dev/null
@@ -0,0 +1,77 @@
+/*\r
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+// CLASS HEADER\r
+#include "focus-finder.h"\r
+\r
+// EXTERNAL INCLUDES\r
+#include <dali/devel-api/common/singleton-service.h>\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali-toolkit/internal/focus-manager/focus-finder-impl.h>\r
+\r
+namespace Dali\r
+{\r
+namespace Toolkit\r
+{\r
+FocusFinder::FocusFinder()\r
+{\r
+}\r
+\r
+FocusFinder::~FocusFinder()\r
+{\r
+}\r
+\r
+FocusFinder FocusFinder::Get()\r
+{\r
+  FocusFinder finder;\r
+\r
+  // Check whether the focus finder is already created\r
+  SingletonService singletonService(SingletonService::Get());\r
+  if(singletonService)\r
+  {\r
+    Dali::BaseHandle handle = singletonService.GetSingleton(typeid(FocusFinder));\r
+    if(handle)\r
+    {\r
+      // If so, downcast the handle of singleton to focus finder\r
+      finder = FocusFinder(dynamic_cast<Internal::FocusFinder*>(handle.GetObjectPtr()));\r
+    }\r
+\r
+    if(!finder)\r
+    {\r
+      // If not, create the focus finder and register it as a singleton\r
+      finder = FocusFinder(new Internal::FocusFinder());\r
+      singletonService.Register(typeid(finder), finder);\r
+    }\r
+  }\r
+\r
+  return finder;\r
+}\r
+\r
+FocusFinder::FocusFinder(Internal::FocusFinder* impl)\r
+: BaseHandle(impl)\r
+{\r
+}\r
+\r
+Actor FocusFinder::GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+  return GetImpl(*this).GetNearestFocusableActor(focusedActor, direction);\r
+}\r
+\r
+} // namespace Toolkit\r
+\r
+} // namespace Dali\r
diff --git a/dali-toolkit/devel-api/focus-manager/focus-finder.h b/dali-toolkit/devel-api/focus-manager/focus-finder.h
new file mode 100644 (file)
index 0000000..8012675
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef DALI_TOOLKIT_FOCUS_FINDER_H\r
+#define DALI_TOOLKIT_FOCUS_FINDER_H\r
+\r
+/*\r
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali-toolkit/public-api/controls/control.h>\r
+\r
+namespace Dali\r
+{\r
+namespace Toolkit\r
+{\r
+namespace Internal DALI_INTERNAL\r
+{\r
+class FocusFinder;\r
+}\r
+\r
+/**\r
+ * FocusFinder\r
+ * This class used for finding the next focusable actor in a given direction\r
+ * from a actor that currently has focus.\r
+ */\r
+class DALI_TOOLKIT_API FocusFinder : public BaseHandle\r
+{\r
+public:\r
+  /**\r
+   * Create a FocusFinder handle; this can be initialised with FocusFinder::Get()\r
+   * Calling member functions with an uninitialised handle is not allowed.\r
+   */\r
+  FocusFinder();\r
+\r
+  /**\r
+   * @brief Destructor\r
+   *\r
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.\r
+   */\r
+  ~FocusFinder();\r
+\r
+  /**\r
+   * @brief Get the singleton of FocusFinder object.\r
+   * @return A handle to the FocusFinder control.\r
+   */\r
+  static FocusFinder Get();\r
+\r
+  /**\r
+   * Get the nearest focusable actor.\r
+   * @param [in] focusedActor The current focused actor.\r
+   * @param [in] direction The direction.\r
+   * @return The nearest focusable actor, or null if none exists.\r
+   */\r
+  Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction);\r
+\r
+private:\r
+  explicit DALI_INTERNAL FocusFinder(Internal::FocusFinder* impl);\r
+\r
+}; // class FocusFinder\r
+\r
+} // namespace Toolkit\r
+\r
+} // namespace Dali\r
+\r
+#endif // DALI_TOOLKIT_FOCUS_FINDER_H\r
index 7347c00..48e0118 100644 (file)
@@ -147,6 +147,7 @@ DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY(Toolkit, TextEditor, "selectedText",
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "fontSizeScale",                        FLOAT,     FONT_SIZE_SCALE                     )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "primaryCursorPosition",                INTEGER,   PRIMARY_CURSOR_POSITION             )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "grabHandleColor",                      VECTOR4,   GRAB_HANDLE_COLOR                   )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "enableGrabHandlePopup",                BOOLEAN,   ENABLE_GRAB_HANDLE_POPUP            )
 
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "textChanged",        SIGNAL_TEXT_CHANGED       )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputStyleChanged",  SIGNAL_INPUT_STYLE_CHANGED)
@@ -775,6 +776,14 @@ void TextEditor::SetProperty(BaseObject* object, Property::Index index, const Pr
         impl.RequestTextRelayout();
         break;
       }
+      case Toolkit::DevelTextEditor::Property::ENABLE_GRAB_HANDLE_POPUP:
+      {
+        const bool grabHandlePopupEnabled = value.Get<bool>();
+        DALI_LOG_INFO(gLogFilter, Debug::General, "TextEditor %p ENABLE_GRAB_HANDLE_POPUP %d\n", impl.mController.Get(), grabHandlePopupEnabled);
+
+        impl.mController->SetGrabHandlePopupEnabled(grabHandlePopupEnabled);
+        break;
+      }
     } // switch
   }   // texteditor
 }
@@ -1136,6 +1145,11 @@ Property::Value TextEditor::GetProperty(BaseObject* object, Property::Index inde
         value = impl.mDecorator->GetHandleColor();
         break;
       }
+      case Toolkit::DevelTextEditor::Property::ENABLE_GRAB_HANDLE_POPUP:
+      {
+        value = impl.mController->IsGrabHandlePopupEnabled();
+        break;
+      }
     } //switch
   }
 
@@ -1456,6 +1470,12 @@ void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
     ResizeActor(mActiveLayer, contentSize);
   }
 
+  // If there is text changed, callback is called.
+  if(mTextChanged)
+  {
+    EmitTextChangedSignal();
+  }
+
   const Text::Controller::UpdateTextType updateTextType = mController->Relayout(contentSize, layoutDirection);
 
   if((Text::Controller::NONE_UPDATED != updateTextType) ||
@@ -1475,12 +1495,6 @@ void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
     }
 
     RenderText(updateTextType);
-
-    // If there is text changed, callback is called.
-    if(mTextChanged)
-    {
-      EmitTextChangedSignal();
-    }
   }
 
   // The text-editor emits signals when the input style changes. These changes of style are
index 7fc3eca..c0d7903 100644 (file)
@@ -1407,6 +1407,12 @@ void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
     ResizeActor(mActiveLayer, contentSize);
   }
 
+  // If there is text changed, callback is called.
+  if(mTextChanged)
+  {
+    EmitTextChangedSignal();
+  }
+
   const Text::Controller::UpdateTextType updateTextType = mController->Relayout(contentSize, layoutDirection);
 
   if((Text::Controller::NONE_UPDATED != updateTextType) ||
@@ -1426,12 +1432,6 @@ void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
     }
 
     RenderText(updateTextType);
-
-    // If there is text changed, callback is called.
-    if(mTextChanged)
-    {
-      EmitTextChangedSignal();
-    }
   }
 
   // The text-field emits signals when the input style changes. These changes of style are
index 395dfc8..fc8edaf 100644 (file)
@@ -114,6 +114,7 @@ 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
new file mode 100644 (file)
index 0000000..93f1d65
--- /dev/null
@@ -0,0 +1,449 @@
+/*\r
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+/*\r
+ * Copyright (C) 2017 The Android Open Source Project\r
+ *\r
+ * Modified by joogab yun(joogab.yun@samsung.com)\r
+ */\r
+\r
+// CLASS HEADER\r
+#include "focus-finder-impl.h"\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali/devel-api/actors/actor-devel.h>\r
+\r
+// EXTERNAL INCLUDES\r
+#include <dali/integration-api/adaptor-framework/adaptor.h>\r
+#include <dali/integration-api/adaptor-framework/scene-holder.h>\r
+#include <dali/integration-api/debug.h>\r
+#include <dali/public-api/actors/layer.h>\r
+#include <math.h>\r
+\r
+namespace\r
+{\r
+static int MajorAxisDistanceRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    {\r
+      return source.left - dest.right;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      return dest.left - source.right;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    {\r
+      return source.top - dest.bottom;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      return dest.top - source.bottom;\r
+    }\r
+    default:\r
+    {\r
+      return 0;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+ * @return The distance from the edge furthest in the given direction\r
+ *   of source to the edge nearest in the given direction of dest.\r
+ *   If the dest is not in the direction from source, return 0.\r
+ */\r
+static int MajorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+  return std::max(0, MajorAxisDistanceRaw(direction, source, dest));\r
+}\r
+\r
+static int MajorAxisDistanceToFarEdgeRaw(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    {\r
+      return source.left - dest.left;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      return dest.right - source.right;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    {\r
+      return source.top - dest.top;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      return dest.bottom - source.bottom;\r
+    }\r
+    default:\r
+    {\r
+      return 0;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+ * @return The distance along the major axis w.r.t the direction from the\r
+ *   edge of source to the far edge of dest.\r
+ *   If the dest is not in the direction from source, return 1\r
+ */\r
+static int MajorAxisDistanceToFarEdge(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+  return std::max(1, MajorAxisDistanceToFarEdgeRaw(direction, source, dest));\r
+}\r
+\r
+/**\r
+ * Find the distance on the minor axis w.r.t the direction to the nearest\r
+ * edge of the destination rectangle.\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param source The source rect.\r
+ * @param dest The destination rect.\r
+ * @return The distance.\r
+ */\r
+static int MinorAxisDistance(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> dest)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      // the distance between the center verticals\r
+      return std::abs(\r
+        (((source.top + source.bottom) * 0.5f) -\r
+         (((dest.top + dest.bottom) * 0.5f))));\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      // the distance between the center horizontals\r
+      return std::abs(\r
+        (((source.left + source.right) * 0.5f) -\r
+         (((dest.left + dest.right) * 0.5f))));\r
+    }\r
+    default:\r
+    {\r
+      return 0;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+ * Calculate distance given major and minor axis distances.\r
+ * @param majorAxisDistance The majorAxisDistance\r
+ * @param minorAxisDistance The minorAxisDistance\r
+ * @return The distance\r
+ */\r
+static int GetWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance)\r
+{\r
+  return 13 * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance;\r
+}\r
+\r
+/**\r
+ * Convert x,y,width,height coordinates into left, right, bottom, top coordinates.\r
+ * @param[in,out] rect The rect\r
+ */\r
+static void ConvertCoordinate(Dali::Rect<float>& rect)\r
+{\r
+  // convert x, y, width, height -> left, right, bottom, top\r
+  float left   = rect.x;\r
+  float right  = rect.x + rect.width;\r
+  float bottom = rect.y + rect.height;\r
+  float top    = rect.y;\r
+\r
+  rect.left   = left;\r
+  rect.right  = right;\r
+  rect.bottom = bottom;\r
+  rect.top    = top;\r
+}\r
+\r
+/**\r
+ * Is destRect a candidate for the next focus given the direction?\r
+ * @param srcRect The source rect.\r
+ * @param destRect The dest rect.\r
+ * @param direction The direction (up, down, left, right)\r
+ * @return Whether destRect is a candidate.\r
+ */\r
+static bool IsCandidate(Dali::Rect<float> srcRect, Dali::Rect<float> destRect, Dali::Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    {\r
+      return (srcRect.right > destRect.right || srcRect.left >= destRect.right) && srcRect.left > destRect.left;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      return (srcRect.left < destRect.left || srcRect.right <= destRect.left) && srcRect.right < destRect.right;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    {\r
+      return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom) && srcRect.top > destRect.top;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top) && srcRect.bottom < destRect.bottom;\r
+    }\r
+    default:\r
+    {\r
+      return false;\r
+    }\r
+  }\r
+  return false;\r
+}\r
+\r
+/**\r
+ * Is dest in a given direction from src?\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param src The source rect\r
+ * @param dest The dest rect\r
+ */\r
+static bool IsToDirectionOf(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> src, Dali::Rect<float> dest)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    {\r
+      return src.left >= dest.right;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      return src.right <= dest.left;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    {\r
+      return src.top >= dest.bottom;\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      return src.bottom <= dest.top;\r
+    }\r
+    default:\r
+    {\r
+      return false;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+ * Do the given direction's axis of rect1 and rect2 overlap?\r
+ * @param direction the direction (up, down, left, right)\r
+ * @param rect1 The first rect\r
+ * @param rect2 The second rect\r
+ * @return whether the beams overlap\r
+ */\r
+static bool BeamsOverlap(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> rect1, Dali::Rect<float> rect2)\r
+{\r
+  switch(direction)\r
+  {\r
+    case Dali::Toolkit::Control::KeyboardFocus::LEFT:\r
+    case Dali::Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);\r
+    }\r
+    case Dali::Toolkit::Control::KeyboardFocus::UP:\r
+    case Dali::Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);\r
+    }\r
+    default:\r
+    {\r
+      return false;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+ * One rectangle may be another candidate than another by virtue of being exclusively in the beam of the source rect.\r
+ * @param direction The direction (up, down, left, right)\r
+ * @param source The source rect\r
+ * @param rect1 The first rect\r
+ * @param rect2 The second rect\r
+ * @return Whether rect1 is a better candidate than rect2 by virtue of it being in src's beam\r
+ */\r
+static bool BeamBeats(Dali::Toolkit::Control::KeyboardFocus::Direction direction, Dali::Rect<float> source, Dali::Rect<float> rect1, Dali::Rect<float> rect2)\r
+{\r
+  const bool rect1InSrcBeam = BeamsOverlap(direction, source, rect1);\r
+  const bool rect2InSrcBeam = BeamsOverlap(direction, source, rect2);\r
+  // if rect1 isn't exclusively in the src beam, it doesn't win\r
+  if(rect2InSrcBeam || !rect1InSrcBeam)\r
+  {\r
+    return false;\r
+  }\r
+  // we know rect1 is in the beam, and rect2 is not\r
+  // if rect1 is to the direction of, and rect2 is not, rect1 wins.\r
+  // for example, for direction left, if rect1 is to the left of the source\r
+  // and rect2 is below, then we always prefer the in beam rect1, since rect2\r
+  // could be reached by going down.\r
+  if(!IsToDirectionOf(direction, source, rect2))\r
+  {\r
+    return true;\r
+  }\r
+  // for horizontal directions, being exclusively in beam always wins\r
+  if((direction == Dali::Toolkit::Control::KeyboardFocus::LEFT || direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT))\r
+  {\r
+    return true;\r
+  }\r
+  // for vertical directions, beams only beat up to a point:\r
+  // now, as long as rect2 isn't completely closer, rect1 wins\r
+  // e.g for direction down, completely closer means for rect2's top\r
+  // edge to be closer to the source's top edge than rect1's bottom edge.\r
+  return (MajorAxisDistance(direction, source, rect1) < MajorAxisDistanceToFarEdge(direction, source, rect2));\r
+}\r
+\r
+} // unnamed namespace\r
+\r
+namespace Dali\r
+{\r
+namespace Toolkit\r
+{\r
+namespace Internal\r
+{\r
+FocusFinder::FocusFinder()\r
+{\r
+}\r
+\r
+FocusFinder::~FocusFinder()\r
+{\r
+}\r
+\r
+Actor FocusFinder::GetNearestFocusableActor(Actor& focusedActor, Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+  Actor nearestActor;\r
+  if(!focusedActor)\r
+  {\r
+    return nearestActor;\r
+  }\r
+\r
+  Rect<float> focusedRect = DevelActor::CalculateScreenExtents(focusedActor);\r
+\r
+  // initialize the best candidate to something impossible\r
+  // (so the first plausible actor will become the best choice)\r
+  Rect<float> bestCandidateRect = focusedRect;\r
+  switch(direction)\r
+  {\r
+    case Toolkit::Control::KeyboardFocus::LEFT:\r
+    {\r
+      bestCandidateRect.x += 1;\r
+      break;\r
+    }\r
+    case Toolkit::Control::KeyboardFocus::RIGHT:\r
+    {\r
+      bestCandidateRect.x -= 1;\r
+      break;\r
+    }\r
+    case Toolkit::Control::KeyboardFocus::UP:\r
+    {\r
+      bestCandidateRect.y += 1;\r
+      break;\r
+    }\r
+    case Toolkit::Control::KeyboardFocus::DOWN:\r
+    {\r
+      bestCandidateRect.y -= 1;\r
+      break;\r
+    }\r
+    default:\r
+    {\r
+      break;\r
+    }\r
+  }\r
+\r
+  ConvertCoordinate(bestCandidateRect);\r
+\r
+  ConvertCoordinate(focusedRect);\r
+\r
+  Integration::SceneHolder window = Integration::SceneHolder::Get(focusedActor);\r
+  if(window)\r
+  {\r
+    Actor rootActor = window.GetRootLayer();\r
+    nearestActor    = FindNextFocus(rootActor, focusedActor, focusedRect, bestCandidateRect, direction);\r
+  }\r
+  return nearestActor;\r
+}\r
+\r
+Actor FocusFinder::FindNextFocus(Actor& actor, Actor& focusedActor, Rect<float>& focusedRect, Rect<float>& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction)\r
+{\r
+  Actor nearestActor;\r
+  if(actor)\r
+  {\r
+    // Recursively children\r
+    const auto childCount = actor.GetChildCount();\r
+    for(auto i = 0u; i < childCount; ++i)\r
+    {\r
+      Dali::Actor child = actor.GetChildAt(i);\r
+      if(child && child != focusedActor && child.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))\r
+      {\r
+        Rect<float> candidateRect = DevelActor::CalculateScreenExtents(child);\r
+\r
+        // convert x, y, width, height -> left, right, bottom, top\r
+        ConvertCoordinate(candidateRect);\r
+\r
+        if(IsBetterCandidate(direction, focusedRect, candidateRect, bestCandidateRect))\r
+        {\r
+          bestCandidateRect = candidateRect;\r
+          nearestActor      = child;\r
+        }\r
+      }\r
+      Actor nextActor = FindNextFocus(child, focusedActor, focusedRect, bestCandidateRect, direction);\r
+      if(nextActor)\r
+      {\r
+        nearestActor = nextActor;\r
+      }\r
+    }\r
+  }\r
+  return nearestActor;\r
+}\r
+\r
+bool FocusFinder::IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect<float>& focusedRect, Rect<float>& candidateRect, Rect<float>& bestCandidateRect) const\r
+{\r
+  // to be a better candidate, need to at least be a candidate in the first place\r
+  if(!IsCandidate(focusedRect, candidateRect, direction))\r
+  {\r
+    return false;\r
+  }\r
+  // we know that candidateRect is a candidate.. if bestCandidateRect is not a candidate,\r
+  // candidateRect is better\r
+  if(!IsCandidate(focusedRect, bestCandidateRect, direction))\r
+  {\r
+    return true;\r
+  }\r
+  // if candidateRect is better by beam, it wins\r
+  if(BeamBeats(direction, focusedRect, candidateRect, bestCandidateRect))\r
+  {\r
+    return true;\r
+  }\r
+  // if bestCandidateRect is better, then candidateRect cant' be :)\r
+  if(BeamBeats(direction, focusedRect, bestCandidateRect, candidateRect))\r
+  {\r
+    return false;\r
+  }\r
+\r
+  // otherwise, do fudge-tastic comparison of the major and minor axis\r
+  return (GetWeightedDistanceFor(\r
+            MajorAxisDistance(direction, focusedRect, candidateRect),\r
+            MinorAxisDistance(direction, focusedRect, candidateRect)) < GetWeightedDistanceFor(MajorAxisDistance(direction, focusedRect, bestCandidateRect),\r
+                                                                                               MinorAxisDistance(direction, focusedRect, bestCandidateRect)));\r
+}\r
+\r
+} // namespace Internal\r
+\r
+} // namespace Toolkit\r
+\r
+} // namespace Dali\r
diff --git a/dali-toolkit/internal/focus-manager/focus-finder-impl.h b/dali-toolkit/internal/focus-manager/focus-finder-impl.h
new file mode 100644 (file)
index 0000000..012b1aa
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H\r
+#define DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H\r
+\r
+/*\r
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+// EXTERNAL INCLUDES\r
+#include <dali/public-api/object/base-object.h>\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali-toolkit/devel-api/focus-manager/focus-finder.h>\r
+\r
+namespace Dali\r
+{\r
+namespace Integration\r
+{\r
+class SceneHolder;\r
+\r
+} // namespace Integration\r
+\r
+namespace Toolkit\r
+{\r
+namespace Internal\r
+{\r
+class FocusFinder;\r
+\r
+/**\r
+ * @copydoc Toolkit::FocusFinder\r
+ */\r
+class FocusFinder : public Dali::BaseObject\r
+{\r
+public:\r
+  /**\r
+   * Construct a new FocusFinder.\r
+   */\r
+  FocusFinder();\r
+\r
+  /**\r
+   * @copydoc Toolkit::GetNearestFocusableActor\r
+   */\r
+  Actor GetNearestFocusableActor(Actor& focusedActor, Toolkit::Control::KeyboardFocus::Direction direction);\r
+\r
+protected:\r
+  /**\r
+   * Destructor\r
+   */\r
+  virtual ~FocusFinder();\r
+\r
+private:\r
+  /**\r
+   * Find the next actor to take focus in root's descendants, starting from the actor.\r
+   * @param[in] actor The root actor.\r
+   * @param[in] focusedActor The current focused actor.\r
+   * @param[in] focusedRect The rect of current focused actor.\r
+   * @param[in] bestCandidateRect The current best candidate.\r
+   * @param[in] direction The direction.\r
+   * @return nearest Actor.\r
+   */\r
+  Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect<float>& focusedRect, Rect<float>& bestCandidateRect, Toolkit::Control::KeyboardFocus::Direction direction);\r
+\r
+  /**\r
+   * Is rect1 a better candidate than rect2 for a focus search in a particular\r
+   * direction from a source rect?  This is the core routine that determines\r
+   * the order of focus searching.\r
+   * @param direction The direction (up, down, left, right)\r
+   * @param candidateRect The candidate rectangle\r
+   * @param bestCandidateRect The current best candidate.\r
+   * @return Whether the candidate is the new best.\r
+   */\r
+  bool IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rect<float>& focusedRect, Rect<float>& candidateRect, Rect<float>& bestCandidateRect) const;\r
+\r
+private:\r
+  // Undefined\r
+  FocusFinder(const FocusFinder&);\r
+\r
+  FocusFinder& operator=(const FocusFinder& rhs);\r
+};\r
+\r
+} // namespace Internal\r
+\r
+inline Internal::FocusFinder& GetImpl(Dali::Toolkit::FocusFinder& obj)\r
+{\r
+  DALI_ASSERT_ALWAYS(obj);\r
+\r
+  Dali::BaseObject& handle = obj.GetBaseObject();\r
+\r
+  return static_cast<Internal::FocusFinder&>(handle);\r
+}\r
+\r
+inline const Internal::FocusFinder& GetImpl(const Dali::Toolkit::FocusFinder& obj)\r
+{\r
+  DALI_ASSERT_ALWAYS(obj);\r
+\r
+  const Dali::BaseObject& handle = obj.GetBaseObject();\r
+\r
+  return static_cast<const Internal::FocusFinder&>(handle);\r
+}\r
+\r
+} // namespace Toolkit\r
+\r
+} // namespace Dali\r
+\r
+#endif // DALI_TOOLKIT_INTERNAL_FOCUS_FINDER_H\r
index 0d7bbfd..b06edea 100644 (file)
@@ -36,6 +36,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
 #include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/devel-api/focus-manager/focus-finder.h>
 #include <dali-toolkit/devel-api/styling/style-manager-devel.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/control.h>
@@ -495,6 +496,11 @@ bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocus::Direction
         nextFocusableActor                  = mPreFocusChangeSignal.Emit(currentFocusActor, Actor(), direction);
         mIsWaitingKeyboardFocusChangeCommit = false;
       }
+      else
+      {
+        // We should find it among the actors nearby.
+        nextFocusableActor = Toolkit::FocusFinder::Get().GetNearestFocusableActor(currentFocusActor, direction);
+      }
     }
 
     if(nextFocusableActor && nextFocusableActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
index 0d34d55..a449997 100644 (file)
@@ -568,7 +568,8 @@ void TextureManager::Remove(const TextureManager::TextureId textureId, TextureUp
       {
         if(element.mObserver == observer)
         {
-          mLoadQueue.Erase(&element);
+          // Do not erase the item. We will clear it later in ProcessQueuedTextures().
+          element.mObserver = nullptr;
           break;
         }
       }
index c3e32cb..6d52641 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 25;
+const unsigned int TOOLKIT_MICRO_VERSION = 27;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 94d8e11..391ddf9 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.25
+Version:    2.0.27
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT