[dali_2.1.3] Merge branch 'devel/master' 29/268229/1
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 17 Dec 2021 06:59:29 +0000 (06:59 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 17 Dec 2021 06:59:29 +0000 (06:59 +0000)
Change-Id: Ib87f60dcdec914230ef334ade4fe593f9b96bd8d

61 files changed:
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h
automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Cursor.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Hyphen-Wrapping.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-VisualModel.cpp
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/test-text-geometry-utils.h [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/controls/text-controls/text-field-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-field-devel.h
dali-toolkit/devel-api/controls/text-controls/text-label-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-label-devel.h
dali-toolkit/devel-api/focus-manager/focus-finder.cpp
dali-toolkit/devel-api/focus-manager/focus-finder.h
dali-toolkit/internal/controls/buttons/button-impl.cpp
dali-toolkit/internal/controls/popup/popup-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.h
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp
dali-toolkit/internal/text/cursor-helper-functions.cpp
dali-toolkit/internal/text/cursor-helper-functions.h
dali-toolkit/internal/text/glyph-metrics-helper.cpp
dali-toolkit/internal/text/glyph-metrics-helper.h
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-relayouter.cpp
dali-toolkit/internal/text/text-controller-relayouter.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-geometry.cpp [new file with mode: 0644]
dali-toolkit/internal/text/text-geometry.h [new file with mode: 0644]
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/image-atlas-manager.cpp
dali-toolkit/internal/visuals/image-atlas-manager.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/internal/visuals/visual-base-impl.cpp
dali-toolkit/internal/visuals/visual-base-impl.h
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

index b654d52..2747582 100644 (file)
@@ -80,6 +80,15 @@ namespace Accessibility
           gMoveOutedCalled = true;
           return wr->newReplyMessage(m);
       };
+
+      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Activate", MethodType::Method}] =
+      [wr](const MessagePtr &m) -> MessagePtr {
+          return wr->newReplyMessage(m);
+      };
+      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Deactivate", MethodType::Method}] =
+      [wr](const MessagePtr &m) -> MessagePtr {
+          return wr->newReplyMessage(m);
+      };
     }
     auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
     wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", b);
index 701977d..cc0288c 100755 (executable)
@@ -104,7 +104,8 @@ void CreateTextModel( const std::string& text,
                       bool markupProcessorEnabled,
                       LineWrap::Mode wrapMode,
                       bool ellipsisEnabled,
-                      DevelText::EllipsisPosition::Type ellipsisPosition)
+                      DevelText::EllipsisPosition::Type ellipsisPosition,
+                      float lineSpacing)
 {
   textModel = Model::New(); ///< Pointer to the text's model.
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
@@ -327,6 +328,7 @@ void CreateTextModel( const std::string& text,
   Layout::Engine layoutEngine;
   layoutEngine.SetMetrics( metrics );
   layoutEngine.SetLayout( Layout::Engine::MULTI_LINE_BOX );
+  layoutEngine.SetDefaultLineSpacing(lineSpacing);
 
   // Set the layout parameters.
   textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN;
index 0b9f592..389d72d 100644 (file)
@@ -59,6 +59,7 @@ struct LayoutOptions
  * @param[in] wrapMode Line wrap mode.
  * @param[in] ellipsisEnabled Whether the ellipsis layout option is enabled.
  * @param[in] ellipsisPosition Where is the location the text elide.
+ * @param[in] lineSpacing The height of the line in points.
  */
 void CreateTextModel( const std::string& text,
                       const Size& textArea,
@@ -70,7 +71,8 @@ void CreateTextModel( const std::string& text,
                       bool markupProcessorEnabled,
                       LineWrap::Mode wrapMode,
                       bool ellipsisEnabled,
-                      DevelText::EllipsisPosition::Type ellipsisPosition);
+                      DevelText::EllipsisPosition::Type ellipsisPosition,
+                      float lineSpacing);
 
 /**
  * @brief Configures the text @p controller similarly to the one configured by the text-label.
index 84ecd2d..9f1d5da 100644 (file)
@@ -114,7 +114,8 @@ bool SetBidirectionalInfoTest( const SetBidirectionalInfoData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -202,7 +203,8 @@ bool GetMirroredTextTest( const GetMirroredTextData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -281,7 +283,8 @@ bool GetCharactersDirectionTest( const GetCharactersDirectionData& data )
                    data.markupProcessorEnabled,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
index a95b707..8b15ad3 100755 (executable)
@@ -121,7 +121,8 @@ bool CreateParagraphTest( const CreateParagraphData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -186,7 +187,8 @@ bool FindParagraphTest( const FindParagraphData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -240,7 +242,8 @@ bool FetchBidirectionalLineInfoTest( const FetchBidirectionalLineInfoData& data
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -289,7 +292,8 @@ bool GetLogicalCharacterIndexTest( const GetLogicalCharacterIndexData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -355,7 +359,8 @@ bool GetLogicalCursorIndexTest( const GetLogicalCursorIndexData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
index 768e58b..920a826 100755 (executable)
@@ -16,8 +16,8 @@
  */
 
 #include <iostream>
-
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
@@ -46,6 +46,8 @@ using namespace Text;
 
 namespace
 {
+  const std::string DEFAULT_FONT_DIR( "/resources/fonts" );
+  const unsigned int DEFAULT_FONT_SIZE = 1152u;
 
 struct GetClosestLineData
 {
@@ -92,6 +94,15 @@ struct FindSelectionIndicesData
   CharacterIndex* noTextHitIndex;                 ///< The expected character index when there is no hit.
 };
 
+struct PrimaryCursorHeightData
+{
+  std::string     description;                    ///< Description of the test.
+  std::string     text;                           ///< Input text.
+  unsigned int    numberOfTests;                  ///< The number of tests.
+  CharacterIndex* logicalIndex;                   ///< The logical cursor index for each test.
+  float*          heights;                        ///< The expected primary cursor height for each test.
+};
+
 bool GetClosestLineTest( const GetClosestLineData& data )
 {
   std::cout << "  testing : " << data.description << std::endl;
@@ -114,7 +125,8 @@ bool GetClosestLineTest( const GetClosestLineData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -163,7 +175,8 @@ bool GetClosestCursorIndexTest( const GetClosestCursorIndexData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -216,7 +229,8 @@ bool GetCursorPositionTest( const GetCursorPositionData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -233,6 +247,7 @@ bool GetCursorPositionTest( const GetCursorPositionData& data )
     parameters.logical = data.logicalIndex[index];
 
     GetCursorPosition( parameters,
+                       0.f,// Since this test case is not testing the primary cursor height, the default font line height can be set to 0.f.
                        cursorInfo );
 
     if( floor(cursorInfo.primaryPosition.x) != data.visualX[index] )
@@ -243,7 +258,7 @@ bool GetCursorPositionTest( const GetCursorPositionData& data )
     if( floor(cursorInfo.primaryPosition.y) != data.visualY[index] )
     {
       std::cout << "  test " << index << " failed. Different 'y' cursor position : " << cursorInfo.primaryPosition.y << ", expected : " << data.visualY[index] << std::endl;
-       return false;
+      return false;
     }
   }
 
@@ -272,7 +287,8 @@ bool FindSelectionIndicesTest( const FindSelectionIndicesData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -315,12 +331,102 @@ bool FindSelectionIndicesTest( const FindSelectionIndicesData& data )
   return true;
 }
 
+bool PrimaryCursorHeightTest( const PrimaryCursorHeightData& data )
+{
+  std::cout << "  testing : " << data.description << std::endl;
+
+  // 1) Create the model.
+  ModelPtr textModel;
+  MetricsPtr metrics;
+  Size textArea(400.f, 600.f);
+  Size layoutSize;
+
+  Vector<FontDescriptionRun> fontDescriptionRuns;
+
+  const std::string fontFamily( "DejaVuSans" );
+
+  // Set a known font description
+  FontDescriptionRun fontDescriptionRun1;
+  fontDescriptionRun1.characterRun.characterIndex = 0u;
+  fontDescriptionRun1.characterRun.numberOfCharacters = 13u;
+  fontDescriptionRun1.familyLength = fontFamily.size();
+  fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength];
+  memcpy( fontDescriptionRun1.familyName, fontFamily.c_str(), fontDescriptionRun1.familyLength );
+  fontDescriptionRun1.familyDefined = true;
+  fontDescriptionRun1.weightDefined = false;
+  fontDescriptionRun1.widthDefined = false;
+  fontDescriptionRun1.slantDefined = false;
+  fontDescriptionRun1.sizeDefined = true;
+  fontDescriptionRun1.size = 768u;//Font size = 12.0f (768/64 = 12)
+
+  fontDescriptionRuns.PushBack( fontDescriptionRun1 );
+
+  LayoutOptions options;
+  CreateTextModel( data.text,
+                   textArea,
+                   fontDescriptionRuns,
+                   options,
+                   layoutSize,
+                   textModel,
+                   metrics,
+                   false,
+                   LineWrap::WORD,
+                   false,
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   50.f );
+
+  LogicalModelPtr logicalModel = textModel->mLogicalModel;
+  VisualModelPtr visualModel = textModel->mVisualModel;
+
+  GetCursorPositionParameters parameters;
+  parameters.visualModel = visualModel;
+  parameters.logicalModel = logicalModel;
+  parameters.metrics = metrics;
+  parameters.isMultiline = true;
+
+  for( unsigned int index = 0; index < data.numberOfTests; ++index )
+  {
+    CursorInfo cursorInfo;
+    parameters.logical = data.logicalIndex[index];
+
+    // Load some fonts.
+    TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+    fontClient.SetDpi( 93u, 93u );
+
+    char* pathNamePtr = get_current_dir_name();
+    const std::string pathName( pathNamePtr );
+    free( pathNamePtr );
+
+    FontId fontID = fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/dejavu/DejaVuSans.ttf" );
+
+    Text::FontMetrics fontMetrics;
+    MetricsPtr mMetrics = Metrics::New(fontClient);
+    mMetrics->GetFontMetrics(fontID, fontMetrics);
+    float defaultFontLineHeight = (fontMetrics.ascender - fontMetrics.descender);
+
+    GetCursorPosition( parameters,
+                       defaultFontLineHeight,
+                       cursorInfo );
+
+    if( floor(cursorInfo.primaryCursorHeight) != data.heights[index] )
+    {
+      std::cout << "  test " << index << " failed. Different primaryCursorHeight : " << cursorInfo.primaryCursorHeight << ", expected : " << data.heights[index] << std::endl;
+      return false;
+    }
+  }
+
+  return true;
+}
+
 } // namespace
 
 //////////////////////////////////////////////////////////
 //
 // UtcDaliGetClosestLine
 // UtcDaliGetClosestCursorIndex
+// UtcDaliGetCursorPosition
+// UtcDaliFindSelectionIndices
+// UtcDaliPrimaryCursorHeight
 //
 //////////////////////////////////////////////////////////
 
@@ -805,3 +911,35 @@ int UtcDaliFindSelectionIndices(void)
   tet_result(TET_PASS);
   END_TEST;
 }
+
+int UtcDaliPrimaryCursorHeight(void)
+{
+  tet_infoline(" UtcDaliPrimaryCursorHeight");
+
+  float heights[] = { 19.f };
+  CharacterIndex logicalIndex[] = { 1u };
+
+  struct PrimaryCursorHeightData data[] =
+  {
+    {
+      "Testing primary cursor height when line spacing is used.",
+      "Hello World",
+      1u,
+      logicalIndex,
+      heights,
+    }
+  };
+  const unsigned int numberOfTests = 1u;
+
+  for( unsigned int index = 0; index < numberOfTests; ++index )
+  {
+    ToolkitTestApplication application;
+    if( !PrimaryCursorHeightTest( data[index] ) )
+    {
+      tet_result(TET_FAIL);
+    }
+  }
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
index 402cc40..4f1d0f5 100755 (executable)
@@ -93,7 +93,8 @@ bool LayoutTextTest( const LayoutTextData& data )
                    false,
                    data.wrapMode,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   Vector<LineRun>& lines = textModel->mVisualModel->mLines;
 
index 281dbba..b50ab83 100755 (executable)
@@ -112,7 +112,8 @@ bool LayoutTextTest( const LayoutTextData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -370,7 +371,8 @@ bool AlignTest( const AlignData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
index 272db31..59a0dfd 100755 (executable)
@@ -144,7 +144,8 @@ bool ShapeInfoTest( const ShapeInfoData& data )
                    data.markupProcessorEnabled,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
index 04c51af..1553f97 100644 (file)
@@ -71,7 +71,8 @@ public:
   TestObserver()
   : mCompleteType( CompleteType::NOT_COMPLETED ),
     mLoaded(false),
-    mObserverCalled(false)
+    mObserverCalled(false),
+    mTextureSet()
   {
   }
 
@@ -81,6 +82,7 @@ public:
     mCompleteType = CompleteType::UPLOAD_COMPLETE;
     mLoaded = loadSuccess;
     mObserverCalled = true;
+    mTextureSet = textureSet;
   }
 
   virtual void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override
@@ -93,6 +95,7 @@ public:
   CompleteType mCompleteType;
   bool mLoaded;
   bool mObserverCalled;
+  TextureSet mTextureSet;
 };
 
 
@@ -590,3 +593,305 @@ int UtcTextureManagerUseInvalidMask(void)
 
   END_TEST;
 }
+
+int UtcTextureManagerSynchronousLoadingFail(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerSynchronousLoadingFail" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  std::string filename("dummy");
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(!textureSet);  // texture loading fail.
+  DALI_TEST_CHECK(textureId == TextureManager::INVALID_TEXTURE_ID); // invalid texture id is returned.
+
+  END_TEST;
+}
+
+int UtcTextureManagerCachingSynchronousLoading(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerCachingSynchronousLoading" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(textureSet);  // texture is loaded.
+
+  // observer isn't called in synchronous loading.
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image asynchronously.
+  TestObserver asyncObserver;
+  auto asyncTextureId( TextureManager::INVALID_TEXTURE_ID );
+  loadingStatus = false;
+  TextureSet asyncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncTextureSet);  // Cached texture.
+
+  // observer is directly called because textureSet is retrieved by cache.
+  DALI_TEST_EQUALS(asyncObserver.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver.mObserverCalled, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcTextureManagerAsyncSyncAsync(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerAsyncSyncAsync" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image asynchronously.
+  TestObserver asyncObserver1;
+  auto asyncTextureId1( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus1 = false;
+  TextureSet asyncTextureSet1 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId1,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus1,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver1,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus1, true, TEST_LOCATION); // texture is loading now.
+  DALI_TEST_CHECK(!asyncTextureSet1);  // texture is not loaded yet.
+
+  // observer is still not called.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image synchronously just after asynchronous loading.
+  TestObserver syncObserver;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  bool syncLoadingStatus = false;
+  TextureSet syncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    syncLoadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &syncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId1, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(syncLoadingStatus, false, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_CHECK(syncTextureSet);  // texture is loaded.
+
+  // syncObserver isn't called in synchronous loading.
+  DALI_TEST_EQUALS(syncObserver.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(syncObserver.mObserverCalled, false, TEST_LOCATION);
+
+  // asyncObserver1 is still not called too.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+
+  // load image asynchronously.
+  TestObserver asyncObserver2;
+  auto asyncTextureId2( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus2 = false;
+  TextureSet asyncTextureSet2 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId2,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus2,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver2,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus2, false, TEST_LOCATION); // texture is loaded by previous sync request
+  DALI_TEST_CHECK(asyncTextureSet2); // texture is loaded
+  DALI_TEST_CHECK(asyncTextureSet2 == syncTextureSet);  // check loaded two texture is same.
+
+  // observer is called synchronously because the texture is cached.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, true, TEST_LOCATION);
+
+  asyncObserver2.mLoaded = false;
+  asyncObserver2.mObserverCalled = false;
+
+  application.SendNotification();
+  application.Render();
+
+  // Requested asynchronous loading at first is finished now and async observer is called now.
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncObserver1.mTextureSet == asyncTextureSet2);  // check loaded two texture is same.
+
+  // asyncObserver2 was already called so it isn't called here.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
index 2453771..a88a85d 100644 (file)
@@ -82,7 +82,8 @@ bool SetGlyphsPerCharacterTest( const SetGlyphsPerCharacterData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
@@ -169,7 +170,8 @@ bool SetCharacterToGlyphTest( const SetCharacterToGlyphData& data )
                    false,
                    LineWrap::WORD,
                    false,
-                   Toolkit::DevelText::EllipsisPosition::END );
+                   Toolkit::DevelText::EllipsisPosition::END,
+                   0.f );
 
   LogicalModelPtr logicalModel = textModel->mLogicalModel;
   VisualModelPtr visualModel = textModel->mVisualModel;
index b1aece3..641a9df 100755 (executable)
@@ -135,6 +135,7 @@ SET(TEST_HARNESS_SOURCES
   dali-toolkit-test-utils/test-render-controller.cpp
   dali-toolkit-test-utils/test-trace-call-stack.cpp
   dali-toolkit-test-utils/test-native-image.cpp
+  test-text-geometry-utils.cpp
 )
 
 PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
diff --git a/automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp b/automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp
new file mode 100644 (file)
index 0000000..b2f0411
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 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.
+ *
+ */
+
+#include "test-text-geometry-utils.h"
+
+namespace TestTextGeometryUtils
+{
+
+void CheckGeometryResult(Vector<Vector2> positionsList, Vector<Vector2> sizeList, Vector<Vector2> expectedPositions, Vector<Vector2> expectedSizes)
+{
+  unsigned int expectedCount = expectedSizes.Size();
+
+  for(unsigned int i = 0; i < expectedCount; i++)
+  {
+    DALI_TEST_EQUALS((int)positionsList[i].x, (int)expectedPositions[i].x, TEST_LOCATION);
+    DALI_TEST_EQUALS((int)positionsList[i].y, (int)expectedPositions[i].y, TEST_LOCATION);
+
+    DALI_TEST_EQUALS((int)sizeList[i].x, (int)expectedSizes[i].x, TEST_LOCATION);
+    DALI_TEST_EQUALS((int)sizeList[i].y, (int)expectedSizes[i].y, TEST_LOCATION);
+  }
+}
+
+}
\ No newline at end of file
diff --git a/automated-tests/src/dali-toolkit/test-text-geometry-utils.h b/automated-tests/src/dali-toolkit/test-text-geometry-utils.h
new file mode 100644 (file)
index 0000000..4aa2a33
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef  TOOLKIT_TEXT_GEOMETRY_UTILS_H
+#define  TOOLKIT_TEXT_GEOMETRY_UTILS_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+
+
+
+namespace TestTextGeometryUtils
+{
+void CheckGeometryResult(Vector<Vector2> positionsList, Vector<Vector2> sizeList, Vector<Vector2> expectedPositions, Vector<Vector2> expectedSizes);
+}
+
+#endif // TOOLKIT_TEXT_GEOMETRY_UTILS_H
index 02dff2e..4e9890c 100644 (file)
@@ -29,6 +29,7 @@
 #include <dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -4455,6 +4456,258 @@ int utcDaliTextEditorCursorPositionChangedSignal(void)
   END_TEST;
 }
 
+int utcDaliTextEditorGeometryEllipsisStart(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisStart");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(37, 0));
+  expectedSizes.PushBack(Vector2(20, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryEllipsisMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisMiddle");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::MIDDLE );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-1, 0));
+  expectedSizes.PushBack(Vector2(25, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryEllipsisEnd(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisEnd");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-1, 0));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(25, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryRTL");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 4;
+  unsigned int startIndex = 3;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(24, 0));
+  expectedSizes.PushBack(Vector2(33, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 50));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(61, 75));
+  expectedSizes.PushBack(Vector2(37, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryGlyphMiddle");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 150.f, 200.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( TextEditor::Property::TEXT, "لا تحتوي على لا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(6, 0));
+  expectedSizes.PushBack(Vector2(124, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int utcDaliTextEditorSelectionClearedSignal(void)
 {
   ToolkitTestApplication application;
@@ -4492,7 +4745,7 @@ int utcDaliTextEditorSelectionClearedSignal(void)
   application.Render();
 
   // Move to second line of the text & Select some text in the right of the current cursor position
-  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) ); 
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
   application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
 
   // remove selection
@@ -4589,6 +4842,53 @@ int utcDaliTextEditorSelectionClearedSignal(void)
   END_TEST;
 }
 
+int utcDaliTextEditorSelectionWithSecondaryCursor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorSelectionWithSecondaryCursor");
+
+  // Checks if the actor is created.
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::ENABLE_MARKUP, true );
+  editor.SetProperty( TextEditor::Property::TEXT, "اللغة العربية\nمرحبا بالجميع\nالسلام عليكم <span font-size='12' font-family='DejaVu Sans' >Hello world</span>" );
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 12.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  editor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 50.f );
+  editor.SetProperty( DevelTextEditor::Property::MATCH_SYSTEM_LANGUAGE_DIRECTION, false );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text editor
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Select the last Arabic word (RTL) & some the space before the English (LTR) letters.
+  DevelTextEditor::SelectText( editor, 35, 41 );// This will activate the alternative cursor position and thus 'cursorInfo.isSecondaryCursor' will be true.
+
+  application.SendNotification();
+  application.Render();
+
+  std::string selectedText = editor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "عليكم ", selectedText, TEST_LOCATION );
+
+  END_TEST;
+}
+
 int utcDaliTextEditorSelectionChangedSignal(void)
 {
   ToolkitTestApplication application;
@@ -4720,4 +5020,212 @@ int utcDaliTextEditorSelectionChangedSignal(void)
   DALI_TEST_EQUALS(oldSelectionEnd, 23, TEST_LOCATION);
 
   END_TEST;
+}
+
+
+int utcDaliTextEditorInsertCharacterAfterInitWithResizePolicyNaturalSize(void)
+{
+
+  //This is to test a crash when used Resize Policy equals USE_NATURAL_SIZE
+  //DaliException on vector: "Iterator not inside vector"
+
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorInsertCharacterAfterInitWithResizePolicyNaturalSize");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  //Set multilines text
+  editor.SetProperty(Dali::Toolkit::TextEditor::Property::TEXT, "Hello \n World");
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  //Set ResizePolicy to NaturalSize
+  editor.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::USE_NATURAL_SIZE);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Create a tap event to touch the text editor.
+  TestGenerateTap( application, 5.0f, 5.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Set currsor and add character (in first line)
+  editor.SetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 5);
+  application.ProcessEvent( GenerateKey( "d", "", "d", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "d", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "d", "", "d", KEY_D_CODE, 0, 0, Integration::KeyEvent::UP, "d", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Check the changed text and cursor position
+  DALI_TEST_EQUALS( editor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "Hellod \n World", TEST_LOCATION );
+  DALI_TEST_EQUALS( editor.GetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION ).Get<int>(), 6, TEST_LOCATION );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int utcDaliTextEditorRemoveCharacterAfterInitWithResizePolicyNaturalSize(void)
+{
+
+  //This is to test a crash when used Resize Policy equals USE_NATURAL_SIZE
+  //DaliException on vector: "Iterator not inside vector"
+
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorRemoveCharacterAfterInitWithResizePolicyNaturalSize");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  //Set multilines text
+  editor.SetProperty(Dali::Toolkit::TextEditor::Property::TEXT, "Hello \n World");
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  //Set ResizePolicy to NaturalSize
+  editor.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::USE_NATURAL_SIZE);
+
+  // Set currsor
+  editor.SetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 5);
+  application.SendNotification();
+  application.Render();
+
+  // Set focus and remove character
+  editor.SetKeyInputFocus();
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_BACKSPACE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Check the changed text and cursor position
+  DALI_TEST_EQUALS( editor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "Hell \n World", TEST_LOCATION );
+  DALI_TEST_EQUALS( editor.GetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION ).Get<int>(), 4, TEST_LOCATION );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int utcDaliTextEditorCutSelectedTextAfterInitWithResizePolicyNaturalSize(void)
+{
+
+  //This is to test a crash when used Resize Policy equals USE_NATURAL_SIZE
+  //DaliException on vector: "Iterator not inside vector"
+
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorCutSelectedTextAfterInitWithResizePolicyNaturalSize");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  //Set multilines text
+  editor.SetProperty(Dali::Toolkit::TextEditor::Property::TEXT, "Hello \n World");
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  //Set ResizePolicy to NaturalSize
+  editor.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::USE_NATURAL_SIZE);
+
+  //Select text at initialization (before the first render)
+  DevelTextEditor::SelectText( editor ,3, 5 );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Cut text
+  application.ProcessEvent( GenerateKey( "", "", "", Dali::DevelKey::DALI_KEY_CONTROL_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "x", "x", "x", KEY_X_CODE, KEY_CONTROL_MODIFIER, 0, Integration::KeyEvent::DOWN, "x", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Check the changed text and cursor position
+  DALI_TEST_EQUALS( editor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "Hel \n World", TEST_LOCATION );
+  DALI_TEST_EQUALS( editor.GetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION ).Get<int>(), 3, TEST_LOCATION );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+
+int utcDaliTextEditorDoubleEnterAfterInitWithResizePolicyNaturalSize(void)
+{
+
+  //This is to test a crash when used Resize Policy equals USE_NATURAL_SIZE
+  //DaliException on vector: "Iterator not inside vector"
+
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorDoubleEnterAfterInitWithResizePolicyNaturalSize");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  //Set multilines text
+  editor.SetProperty(Dali::Toolkit::TextEditor::Property::TEXT, "Hello \n World");
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  //Set ResizePolicy to NaturalSize
+  editor.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::USE_NATURAL_SIZE);
+
+  // Set currsor
+  editor.SetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 5);
+  application.SendNotification();
+  application.Render();
+
+  // Set focus and double enter (new line)
+  editor.SetKeyInputFocus();
+  application.ProcessEvent(GenerateKey("Enter", "", "\n", 13, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+  application.ProcessEvent(GenerateKey("Enter", "", "\n", 13, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //Check the changed text and cursor position
+  DALI_TEST_EQUALS( editor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "Hello\n\n \n World", TEST_LOCATION );
+  DALI_TEST_EQUALS( editor.GetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION ).Get<int>(), 7, TEST_LOCATION );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
 }
\ No newline at end of file
index e8bf4b6..d1a04e9 100644 (file)
@@ -31,6 +31,7 @@
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include "toolkit-clipboard.h"
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -4391,6 +4392,190 @@ int utcDaliTextFieldCursorPositionChangedSignal(void)
   END_TEST;
 }
 
+int utcDaliTextFieldGeometryEllipsisStart(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryEllipsisStart");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 250.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START );
+  field.SetProperty( TextField::Property::TEXT, "Hello World" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 10;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(14, 0));
+  expectedSizes.PushBack(Vector2(106, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryEllipsisEnd(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryEllipsisEnd");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 250.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END );
+  field.SetProperty( TextField::Property::TEXT, "Hello World" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 10;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-2, 0));
+  expectedSizes.PushBack(Vector2(122, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryRTL");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 300.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( TextField::Property::TEXT, "السطر الاخير" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 7;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(38, 0));
+  expectedSizes.PushBack(Vector2(73, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryGlyphMiddle");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 150.f, 200.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( TextField::Property::TEXT, "لا تحتوي على لا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(6, 0));
+  expectedSizes.PushBack(Vector2(124, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int utcDaliTextFieldSelectionClearedSignal(void)
 {
   ToolkitTestApplication application;
index b0f9764..c060743 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-toolkit/devel-api/text/bitmap-font.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include <dali-toolkit/devel-api/text/text-utils-devel.h>
+#include "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -2017,6 +2018,106 @@ int utcDaliTextLabelGetHeightForWidthChangeLineCountWhenTextChanged(void)
   END_TEST;
 }
 
+int utcDaliTextLabelGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextLabelGeometryRTL");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK( label );
+
+  application.GetScene().Add( label );
+
+  label.SetProperty( TextLabel::Property::POINT_SIZE, 7.f );
+  label.SetProperty( Actor::Property::SIZE, Vector2( 150.f, 100.f ) );
+  label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true );
+  label.SetProperty( TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty( TextLabel::Property::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 4;
+  unsigned int startIndex = 3;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextLabel::GetTextPosition(label, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextLabel::GetTextSize(label, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(24, 0));
+  expectedSizes.PushBack(Vector2(33, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 50));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(73, 75));
+  expectedSizes.PushBack(Vector2(37, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextLabelGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextLabelGeometryGlyphMiddle");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK( label );
+
+  application.GetScene().Add( label );
+
+  label.SetProperty( TextLabel::Property::POINT_SIZE, 7.f );
+  label.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+  label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true );
+  label.SetProperty( TextLabel::Property::TEXT, "لا تحتوي على لا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextLabel::GetTextPosition(label, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextLabel::GetTextSize(label, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(12, 0));
+  expectedSizes.PushBack(Vector2(118, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextlabelEllipsisPositionProperty(void)
 {
   ToolkitTestApplication application;
index d1a2e56..387275d 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-toolkit/devel-api/visual-factory/transition-data.h>
 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/text-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/animated-gradient-visual-properties-devel.h>
@@ -5446,4 +5447,667 @@ int UtcDaliVisualGetVisualProperty07(void)
 #endif
 
   END_TEST;
+}
+
+int UtcDaliVisualUpdateProperty(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdateProperty: Test update property by DoAction. Standard case" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+  propertyMap[Visual::Property::MIX_COLOR] = Color::BLUE;
+  propertyMap[DevelVisual::Property::VISUAL_FITTING_MODE] = DevelVisual::FIT_WIDTH;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  Property::Map originalMap;
+  imageVisual.CreatePropertyMap( originalMap );
+
+  float                    targetOpacity            = 0.5f;
+  Vector3                  targetMixColor           = Vector3(1.0f, 0.4f, 0.2f);
+  bool                     targetPreMultipliedAlpha = originalMap[Visual::Property::PREMULTIPLIED_ALPHA].Get<bool>() ^ true;
+  DevelVisual::FittingMode targetVisualFittingMode  = DevelVisual::CENTER;
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[Visual::Property::OPACITY]                  = targetOpacity;
+  targetPropertyMap[ImageVisual::Property::URL]                 = "foobar";
+  targetPropertyMap[Visual::Property::MIX_COLOR]                = targetMixColor;
+  targetPropertyMap[Visual::Property::PREMULTIPLIED_ALPHA]      = targetPreMultipliedAlpha;
+  targetPropertyMap[DevelVisual::Property::VISUAL_FITTING_MODE] = targetVisualFittingMode;
+
+  // Update Properties
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* colorValue = resultMap.Find(Visual::Property::MIX_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(colorValue);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetMixColor.r, targetMixColor.g, targetMixColor.b, targetOpacity), TEST_LOCATION);
+
+  Property::Value* urlValue = resultMap.Find(ImageVisual::Property::URL, Property::STRING);
+  DALI_TEST_CHECK(urlValue);
+  // NOTE : ImageVisual URL must NOT changed.
+  DALI_TEST_EQUALS(urlValue->Get< std::string >(), TEST_IMAGE_FILE_NAME, TEST_LOCATION);
+
+  Property::Value* preMultipliedValue = resultMap.Find(Visual::Property::PREMULTIPLIED_ALPHA, Property::BOOLEAN);
+  DALI_TEST_CHECK(preMultipliedValue);
+  DALI_TEST_EQUALS(preMultipliedValue->Get< bool >(), targetPreMultipliedAlpha, TEST_LOCATION);
+
+  Property::Value* visualFittingModeValue = resultMap.Find(DevelVisual::Property::VISUAL_FITTING_MODE, Property::STRING);
+  DALI_TEST_CHECK(visualFittingModeValue);
+  DALI_TEST_EQUALS(visualFittingModeValue->Get< std::string >(), "CENTER", TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader01: Test update property by DoAction. Change the shader case" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Vector4 targetCornerRadius = Vector4(1.0f, 12.0f, 2.0f, 21.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS] = targetCornerRadius;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::RELATIVE;
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  Property::Value* cornerRadiusPolicyValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::RELATIVE), TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader changed
+  DALI_TEST_CHECK( callStack.FindMethod("CreateShader") );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  float   targetBorderlineWidth  = 10.0f;
+  Vector4 targetBorderlineColor  = Vector4(1.0f, 0.2f, 0.1f, 0.5f);
+  float   targetBorderlineOffset = -0.3f;
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS]        = Vector4::ZERO;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::ABSOLUTE;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH]     = targetBorderlineWidth;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_COLOR]     = targetBorderlineColor;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_OFFSET]    = targetBorderlineOffset;
+
+  // Update Properties with Borderline
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  cornerRadiusValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  cornerRadiusPolicyValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::ABSOLUTE), TEST_LOCATION);
+
+  Property::Value* borderlineWidthValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), targetBorderlineWidth, TEST_LOCATION);
+
+  Property::Value* borderlineColorValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), targetBorderlineColor, TEST_LOCATION);
+
+  Property::Value* borderlineOffsetValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    // Note : mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    // Note : mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader changed
+  DALI_TEST_CHECK( callStack.FindMethod("CreateShader") );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Property::Map targetPropertyMap3;
+  targetPropertyMap3[DevelVisual::Property::CORNER_RADIUS]        = Vector4::ZERO;
+  targetPropertyMap3[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::ABSOLUTE;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_WIDTH]     = 0.0f;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_COLOR]     = Vector4::ZERO;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_OFFSET]    = 0.0f;
+
+  // Update Properties into zero
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap3);
+
+  Property::Map resultMap3;
+  imageVisual.CreatePropertyMap( resultMap3 );
+
+  // Test property values: they should be updated
+  cornerRadiusValue = resultMap3.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  cornerRadiusPolicyValue = resultMap3.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::ABSOLUTE), TEST_LOCATION);
+
+  borderlineWidthValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  borderlineColorValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  borderlineOffsetValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : mAlwaysUsingBorderline and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : mAlwaysUsingBorderline and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader not changed
+  DALI_TEST_CHECK( !callStack.FindMethod("CreateShader") );
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader02: Test update property by DoAction. Fake update" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  Vector4 targetCornerRadius = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS] = targetCornerRadius;
+
+  callStack.Reset();
+  callStack.Enable(true);
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    // Note : corner radius is zero. so we don't change shader!
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    // Note : corner radius is zero. so we don't change shader!
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader doesn't changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  float   targetBorderlineWidth = 0.0f;
+  Vector4 targetBorderlineColor = Vector4(1.0f, 1.0f, 0.0f, 0.0f);
+  float   targetBorderlineOffset = -1.0f;
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH]  = targetBorderlineWidth;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_COLOR]  = targetBorderlineColor;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_OFFSET] = targetBorderlineOffset;
+
+  // Update Properties with Borderline
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  Property::Value* borderlineWidthValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), targetBorderlineWidth, TEST_LOCATION);
+
+  Property::Value* borderlineColorValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), targetBorderlineColor, TEST_LOCATION);
+
+  Property::Value* borderlineOffsetValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : borderline width is zero. so we don't change shader!
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : borderline width is zero. so we don't change shader!
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader doesn't changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader03: Test update property by DoAction. Blur Radius" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::COLOR;
+  propertyMap[ColorVisual::Property::MIX_COLOR] = Color::BLUE;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  float   targetBlurRadius   = 15.0f;
+  Vector4 targetCornerRadius = Vector4(1.0f, 0.1f, 1.1f, 0.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelColorVisual::Property::BLUR_RADIUS] = targetBlurRadius;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS]    = targetCornerRadius;
+  targetPropertyMap[DevelVisual::Property::BORDERLINE_WIDTH] = 10.0f; // Don't care. just dummy
+
+  callStack.Reset();
+  callStack.Enable(true);
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* blurRadiusValue = resultMap.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get<float>(), targetBlurRadius, TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader changed
+  DALI_TEST_CHECK( (callStack.FindMethod("CreateShader")) );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelColorVisual::Property::BLUR_RADIUS] = 0.0f;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS]    = Vector4::ZERO;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH] = 15.0f; // Don't care. just dummy
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  blurRadiusValue = resultMap2.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  cornerRadiusValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : mAlwaysUsingBlurRadius and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : mAlwaysUsingBlurRadius and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader not changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+
+  END_TEST;
 }
\ No newline at end of file
index f3f073b..a7b4ae4 100644 (file)
@@ -80,6 +80,16 @@ void ScrollBy(TextEditor textEditor, Vector2 scroll)
   GetImpl(textEditor).ScrollBy(scroll);
 }
 
+Vector<Vector2> GetTextSize(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textEditor).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textEditor).GetTextPosition(startIndex, endIndex);
+}
+
 string CopyText(TextEditor textEditor)
 {
   return GetImpl(textEditor).CopyText();
index 177be64..74c19df 100644 (file)
@@ -451,6 +451,30 @@ DALI_TOOLKIT_API void SelectText(TextEditor textEditor, const uint32_t start, co
 DALI_TOOLKIT_API void ScrollBy(TextEditor textEditor, Vector2 scroll);
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Copy and return the selected text of TextEditor.
  *
  * @param[in] textEditor The instance of TextEditor.
index e5abdfa..fce7819 100644 (file)
@@ -70,6 +70,16 @@ void SelectText(TextField textField, const uint32_t start, const uint32_t end)
   GetImpl(textField).SelectText(start, end);
 }
 
+Vector<Vector2> GetTextSize(TextField textField, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textField).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextField textField, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textField).GetTextPosition(startIndex, endIndex);
+}
+
 string CopyText(TextField textField)
 {
   return GetImpl(textField).CopyText();
index 10d79e8..7498110 100644 (file)
@@ -336,6 +336,30 @@ using SelectionClearedSignalType = Signal<void(TextField)>;
 DALI_TOOLKIT_API SelectionClearedSignalType& SelectionClearedSignal(TextField textField);
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textField The instance of TextField.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextField textField, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textField The instance of TextField.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextField textField, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Select the whole text of TextField.
  *
  * @param[in] textField The instance of TextField.
index c117152..5e6bc02 100644 (file)
@@ -35,6 +35,16 @@ TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel)
   return GetImpl(textLabel).TextFitChangedSignal();
 }
 
+Vector<Vector2> GetTextSize(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textLabel).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textLabel).GetTextPosition(startIndex, endIndex);
+}
+
 } // namespace DevelTextLabel
 
 } // namespace Toolkit
index 6bcbdd0..17a808f 100644 (file)
@@ -167,6 +167,30 @@ enum Type
 } // namespace Property
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Anchor clicked signal type.
  *
  * @note Signal
index ddd0360..c52bf57 100644 (file)
@@ -354,9 +354,9 @@ Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect<float>& focusedRect,
   {\r
     // Recursively children\r
     const auto childCount = actor.GetChildCount();\r
-    for(auto i = 0u; i < childCount; ++i)\r
+    for(auto i = childCount; i > 0u; --i)\r
     {\r
-      Dali::Actor child = actor.GetChildAt(i);\r
+      Dali::Actor child = actor.GetChildAt(i-1);\r
       if(child && child != focusedActor && IsFocusable(child))\r
       {\r
         Rect<float> candidateRect = DevelActor::CalculateScreenExtents(child);\r
@@ -382,10 +382,10 @@ Actor FindNextFocus(Actor& actor, Actor& focusedActor, Rect<float>& focusedRect,
 \r
 } // unnamed namespace\r
 \r
-Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction)\r
+Actor GetNearestFocusableActor(Actor rootActor, Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction)\r
 {\r
   Actor nearestActor;\r
-  if(!focusedActor)\r
+  if(!focusedActor || !rootActor)\r
   {\r
     return nearestActor;\r
   }\r
@@ -426,13 +426,7 @@ Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFoc
   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
+  nearestActor = FindNextFocus(rootActor, focusedActor, focusedRect, bestCandidateRect, direction);\r
   return nearestActor;\r
 }\r
 \r
index 47f313d..69708b3 100644 (file)
@@ -27,13 +27,15 @@ namespace Toolkit
 {\r
 namespace FocusFinder\r
 {\r
+\r
 /**\r
  * Get the nearest focusable actor.\r
+ * @param [in] rootActor The root actor.\r
  * @param [in] focusedActor The current focused actor.\r
  * @param [in] direction The direction.\r
  * @return The nearest focusable actor, or an empty handle if none exists.\r
  */\r
-DALI_TOOLKIT_API Actor GetNearestFocusableActor(Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction);\r
+DALI_TOOLKIT_API Actor GetNearestFocusableActor(Actor rootActor, Actor focusedActor, Toolkit::Control::KeyboardFocus::Direction direction);\r
 \r
 } // namespace FocusFinder\r
 \r
index 70e9c76..7ff5875 100644 (file)
@@ -111,7 +111,7 @@ const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
 
 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof(ALIGNMENT_STRING_TABLE) / sizeof(ALIGNMENT_STRING_TABLE[0]);
 
-const Property::Index VISUAL_INDEX_FOR_STATE[][Button::STATE_COUNT] =
+const Property::Index VISUAL_INDEX_FOR_STATE[Button::STATE_COUNT][Button::VISUAL_STATE_COUNT] =
   {
     {Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::UNSELECTED_VISUAL},
     {Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::SELECTED_VISUAL},
index cc65de9..9abcfe0 100644 (file)
@@ -726,25 +726,16 @@ void Popup::SetDisplayState(Toolkit::Popup::DisplayState displayState)
     return;
   }
 
-  auto* accessible = Dali::Accessibility::Accessible::Get(Self());
-  if(display)
-  {
-    Dali::Accessibility::Bridge::GetCurrentBridge()->AddPopup(accessible);
-    accessible->EmitStateChanged(Dali::Accessibility::State::SHOWING, 1, 0);
-  }
-  else
-  {
-    accessible->EmitStateChanged(Dali::Accessibility::State::SHOWING, 0, 0);
-    Dali::Accessibility::Bridge::GetCurrentBridge()->RemovePopup(accessible);
-  }
-
   // Convert the bool state to the actual display state to use.
-  mDisplayState = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING;
+  mDisplayState    = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING;
+  auto* accessible = Dali::Accessibility::Accessible::Get(Self());
 
   if(display)
   {
     // Update the state to indicate the current intent.
     mDisplayState = Toolkit::Popup::SHOWING;
+    Dali::Accessibility::Bridge::GetCurrentBridge()->RegisterDefaultLabel(accessible);
+    accessible->EmitShowing(true);
 
     // We want the popup to have key input focus when it is displayed
     SetKeyInputFocus();
@@ -798,8 +789,9 @@ void Popup::SetDisplayState(Toolkit::Popup::DisplayState displayState)
   else // Not visible.
   {
     mDisplayState = Toolkit::Popup::HIDING;
+    Dali::Accessibility::Bridge::GetCurrentBridge()->UnregisterDefaultLabel(accessible);
     ClearKeyInputFocus();
-
+    accessible->EmitShowing(false);
     // Restore the keyboard focus when popup is hidden.
     if(mPreviousFocusedActor && mPreviousFocusedActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
     {
index 44c1176..5e9e7d9 100644 (file)
@@ -341,6 +341,16 @@ float TextEditor::GetVerticalScrollPosition()
   return 0;
 }
 
+Vector<Vector2> TextEditor::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextEditor::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 string TextEditor::GetSelectedText() const
 {
   string selectedText = "";
index e4b5fa5..9d41735 100644 (file)
@@ -317,6 +317,28 @@ public:
   float GetVerticalScrollPosition();
 
   /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const override;
index 2f4ef60..9cefc6c 100644 (file)
@@ -204,8 +204,8 @@ Toolkit::TextField::InputStyle::Mask ConvertInputStyle(Text::InputStyle::Mask in
 bool IsHiddenInput(Toolkit::TextField textField)
 {
   Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
-  auto mode  = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE);
-  if (mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
+  auto          mode                = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE);
+  if(mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
   {
     return true;
   }
@@ -215,8 +215,8 @@ bool IsHiddenInput(Toolkit::TextField textField)
 char GetSubstituteCharacter(Toolkit::TextField textField)
 {
   Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
-  auto substChar = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER);
-  if (substChar)
+  auto          substChar           = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER);
+  if(substChar)
   {
     return static_cast<char>(substChar->Get<int>());
   }
@@ -1128,10 +1128,20 @@ TextField::~TextField()
   }
 }
 
+Vector<Vector2> TextField::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextField::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 std::string TextField::AccessibleImpl::GetName()
 {
   auto self = Toolkit::TextField::DownCast(Self());
-  if (IsHiddenInput(self))
+  if(IsHiddenInput(self))
   {
     return {};
   }
@@ -1193,8 +1203,8 @@ bool TextField::AccessibleImpl::SetCursorOffset(size_t offset)
 Dali::Accessibility::Range TextField::AccessibleImpl::GetTextAtOffset(
   size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self     = Toolkit::TextField::DownCast(Self());
-  auto range    = Dali::Accessibility::Range{};
+  auto self  = Toolkit::TextField::DownCast(Self());
+  auto range = Dali::Accessibility::Range{};
 
   if(IsHiddenInput(self))
   {
@@ -1297,14 +1307,14 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetRangeOfSelection(size_t
     return {};
   }
 
-  auto self = Toolkit::TextField::DownCast(Self());
+  auto self       = Toolkit::TextField::DownCast(Self());
   auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
-  auto indices = controller->GetSelectionIndexes();
+  auto indices    = controller->GetSelectionIndexes();
 
   auto startOffset = static_cast<size_t>(indices.first);
-  auto endOffset = static_cast<size_t>(indices.second);
+  auto endOffset   = static_cast<size_t>(indices.second);
 
-  if (IsHiddenInput(self))
+  if(IsHiddenInput(self))
   {
     return {startOffset, endOffset, std::string(endOffset - startOffset, GetSubstituteCharacter(self))};
   }
index 7e18a82..1f97263 100644 (file)
@@ -325,6 +325,28 @@ public:
    */
   void AnchorClicked(const std::string& href) override;
 
+  /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
 private: // Implementation
   /**
    * @copydoc Dali::Toolkit::Text::Controller::(InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent)
index 22990d9..9e15b34 100644 (file)
@@ -30,6 +30,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/text/property-string-parser.h>
 #include <dali-toolkit/internal/text/rendering/text-backend.h>
@@ -37,7 +38,6 @@
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
 #include <dali-toolkit/internal/text/text-view.h>
-#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/public-api/text/text-enumerations.h>
 
 #include <dali-toolkit/devel-api/controls/control-devel.h>
@@ -729,10 +729,10 @@ Property::Value TextLabel::GetProperty(BaseObject* object, Property::Index index
       }
       case Toolkit::DevelTextLabel::Property::TEXT_FIT:
       {
-        const bool  enabled  = impl.mController->IsTextFitEnabled();
-        const float minSize  = impl.mController->GetTextFitMinSize();
-        const float maxSize  = impl.mController->GetTextFitMaxSize();
-        const float stepSize = impl.mController->GetTextFitStepSize();
+        const bool  enabled   = impl.mController->IsTextFitEnabled();
+        const float minSize   = impl.mController->GetTextFitMinSize();
+        const float maxSize   = impl.mController->GetTextFitMaxSize();
+        const float stepSize  = impl.mController->GetTextFitStepSize();
         const float pointSize = impl.mController->GetTextFitPointSize();
 
         Property::Map map;
@@ -1136,6 +1136,16 @@ TextLabel::~TextLabel()
 {
 }
 
+Vector<Vector2> TextLabel::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextLabel::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 std::string TextLabel::AccessibleImpl::GetNameRaw()
 {
   auto self = Toolkit::TextLabel::DownCast(Self());
index 5c9a6b1..d776c32 100644 (file)
@@ -98,6 +98,28 @@ public:
    */
   Text::ControllerPtr GetTextController();
 
+  /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
@@ -209,7 +231,7 @@ private: // Data
   std::vector<Toolkit::TextAnchor> mAnchorActors;
 
   // Signals
-  Toolkit::DevelTextLabel::AnchorClickedSignalType mAnchorClickedSignal;
+  Toolkit::DevelTextLabel::AnchorClickedSignalType  mAnchorClickedSignal;
   Toolkit::DevelTextLabel::TextFitChangedSignalType mTextFitChangedSignal;
 
   int  mRenderingBackend;
index 8438261..0ad41cd 100644 (file)
@@ -208,6 +208,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/xhtml-entities.cpp
    ${toolkit_src_dir}/drag-drop-detector/drag-and-drop-detector-impl.cpp
    ${toolkit_src_dir}/text/emoji-helper.cpp
+   ${toolkit_src_dir}/text/text-geometry.cpp
 )
 
 SET( SOURCES ${SOURCES}
index 745da15..8c737d2 100644 (file)
@@ -517,7 +517,11 @@ bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocus::Direction
       else if(mEnableDefaultAlgorithm)
       {
         // We should find it among the actors nearby.
-        nextFocusableActor = Toolkit::FocusFinder::GetNearestFocusableActor(currentFocusActor, direction);
+        Integration::SceneHolder window = Integration::SceneHolder::Get(currentFocusActor);
+        if(window)
+        {
+          nextFocusableActor = Toolkit::FocusFinder::GetNearestFocusableActor(window.GetRootLayer(), currentFocusActor, direction);
+        }
       }
     }
 
index c2d35d9..7b08c47 100644 (file)
@@ -458,6 +458,7 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
 }
 
 void GetCursorPosition(GetCursorPositionParameters& parameters,
+                       float                        defaultFontLineHeight,
                        CursorInfo&                  cursorInfo)
 {
   const LineRun* const modelLines = parameters.visualModel->mLines.Begin();
@@ -479,6 +480,15 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
   const LineIndex lineIndex = parameters.visualModel->GetLineOfCharacter(characterOfLine);
   const LineRun&  line      = *(modelLines + lineIndex);
 
+  const GlyphIndex* const charactersToGlyphBuffer  = parameters.visualModel->mCharactersToGlyph.Begin();
+  const Length* const     glyphsPerCharacterBuffer = parameters.visualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const  glyphInfoBuffer          = parameters.visualModel->mGlyphs.Begin();
+  CharacterIndex          index;
+  GlyphMetrics            glyphMetrics;
+  MetricsPtr&             metrics = parameters.metrics;
+  GlyphIndex glyphIndex = 0u;
+  Length numberOfGlyphs = 0u;
+
   if(isLastNewParagraph)
   {
     // The cursor is in a new line with no characters. Place the cursor in that line.
@@ -493,8 +503,14 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
 
     cursorInfo.lineHeight = GetLineHeight(newLine);
 
+    const Length totalNumberOfCharacters = parameters.logicalModel->mText.Count();
+    index                                = totalNumberOfCharacters - 1;
+
+    GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+
     // Set the primary cursor's height.
-    cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
+    // The primary cursor height will take the font height of the last character and if there are no characters, it'll take the default font line height.
+    cursorInfo.primaryCursorHeight = (totalNumberOfCharacters > 0) ? (cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight) : defaultFontLineHeight;
 
     // Set the primary cursor's position.
     cursorInfo.primaryPosition.x = (LTR == line.direction) ? newLine.alignmentOffset : parameters.visualModel->mControlSize.width - newLine.alignmentOffset;
@@ -541,7 +557,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
 
     // Calculate the primary cursor.
 
-    CharacterIndex index = characterIndex;
+    index = characterIndex;
     if(cursorInfo.isSecondaryCursor)
     {
       // If there is a secondary position, the primary cursor may be in a different place than the logical index.
@@ -574,26 +590,14 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
       }
     }
 
-    const GlyphIndex* const     charactersToGlyphBuffer  = parameters.visualModel->mCharactersToGlyph.Begin();
-    const Length* const         glyphsPerCharacterBuffer = parameters.visualModel->mGlyphsPerCharacter.Begin();
     const Length* const         charactersPerGlyphBuffer = parameters.visualModel->mCharactersPerGlyph.Begin();
     const CharacterIndex* const glyphsToCharactersBuffer = parameters.visualModel->mGlyphsToCharacters.Begin();
     const Vector2* const        glyphPositionsBuffer     = parameters.visualModel->mGlyphPositions.Begin();
-    const GlyphInfo* const      glyphInfoBuffer          = parameters.visualModel->mGlyphs.Begin();
 
-    // Convert the cursor position into the glyph position.
-    const GlyphIndex primaryGlyphIndex         = *(charactersToGlyphBuffer + index);
-    const Length     primaryNumberOfGlyphs     = *(glyphsPerCharacterBuffer + index);
+    GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+    const GlyphIndex primaryGlyphIndex         = glyphIndex;
     const Length     primaryNumberOfCharacters = *(charactersPerGlyphBuffer + primaryGlyphIndex);
 
-    // Get the metrics for the group of glyphs.
-    GlyphMetrics glyphMetrics;
-    GetGlyphsMetrics(primaryGlyphIndex,
-                     primaryNumberOfGlyphs,
-                     glyphMetrics,
-                     glyphInfoBuffer,
-                     parameters.metrics);
-
     // Whether to add the glyph's advance to the cursor position.
     // i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
     //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
@@ -676,16 +680,9 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
         index = (isRightToLeftParagraph == isCurrentRightToLeft) ? nextCharacterIndex : characterIndex;
       }
 
-      const GlyphIndex secondaryGlyphIndex     = *(charactersToGlyphBuffer + index);
-      const Length     secondaryNumberOfGlyphs = *(glyphsPerCharacterBuffer + index);
-
-      const Vector2& secondaryPosition = *(glyphPositionsBuffer + secondaryGlyphIndex);
-
-      GetGlyphsMetrics(secondaryGlyphIndex,
-                       secondaryNumberOfGlyphs,
-                       glyphMetrics,
-                       glyphInfoBuffer,
-                       parameters.metrics);
+      GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+      const GlyphIndex secondaryGlyphIndex = glyphIndex;
+      const Vector2&   secondaryPosition   = *(glyphPositionsBuffer + secondaryGlyphIndex);
 
       // Set the secondary cursor's position.
 
index dc005a3..4414743 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali-toolkit/internal/text/logical-model-impl.h>
 #include <dali-toolkit/internal/text/metrics.h>
 #include <dali-toolkit/internal/text/visual-model-impl.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
 
 namespace Dali
 {
@@ -142,9 +143,11 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
  * if there is a valid alternative cursor, its position and height.
  *
  * @param[in] parameters Parameters used to calculate the cursor's position.
+ * @param[in] defaultFontLineHeight The default font line height.
  * @param[out] cursorInfo The line's height, the cursor's height, the cursor's position and whether there is an alternative cursor.
  */
 void GetCursorPosition(GetCursorPositionParameters& parameters,
+                       float                        defaultFontLineHeight,
                        CursorInfo&                  cursorInfo);
 
 /**
index 1f27a84..f87555e 100644 (file)
@@ -100,6 +100,20 @@ void GetGlyphsMetrics(GlyphIndex             glyphIndex,
   glyphMetrics.width += (firstGlyph.isItalicRequired && !isItalicFont) ? TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE * firstGlyph.height : 0.f;
 }
 
+void GetGlyphMetricsFromCharacterIndex(CharacterIndex index, const GlyphInfo* const glyphInfoBuffer, const GlyphIndex* const charactersToGlyphBuffer, const Length* const glyphsPerCharacterBuffer, MetricsPtr& metrics, GlyphMetrics& glyphMetrics, GlyphIndex& glyphIndex, Length& numberOfGlyphs)
+{
+  //Takes the character index, obtains the glyph index (and the number of Glyphs) from it and finally gets the glyph metrics.
+  glyphIndex     = *(charactersToGlyphBuffer + index);
+  numberOfGlyphs = *(glyphsPerCharacterBuffer + index);
+
+  // Get the metrics for the group of glyphs.
+  GetGlyphsMetrics(glyphIndex,
+                   numberOfGlyphs,
+                   glyphMetrics,
+                   glyphInfoBuffer,
+                   metrics);
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index 05ba882..dac57f0 100644 (file)
@@ -83,6 +83,28 @@ void GetGlyphsMetrics(GlyphIndex             glyphIndex,
                       const GlyphInfo* const glyphsBuffer,
                       MetricsPtr&            metrics);
 
+/**
+ * @brief Takes the character index, obtains the glyph index (and the number of Glyphs) from it and finally gets the glyph metrics.
+ *
+ * @param[in] index The character index.
+ * @param[in] glyphInfoBuffer The glyphs buffer.
+ * @param[in] charactersToGlyphBuffer A vector containing the glyph index for each character.
+ * @param[in] glyphsPerCharacterBuffer A vector containing the number of glyphs in each character.
+ * @param[in] metrics Used to access metrics from FontClient.
+ * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
+ * @param[out] glyphIndex The glyph index obtained from the character index.
+ * @param[out] numberOfGlyphs The number of glyphs in the character of which the index was passed to the function.
+ *
+ */
+void GetGlyphMetricsFromCharacterIndex(CharacterIndex          index,
+                                       const GlyphInfo* const  glyphInfoBuffer,
+                                       const GlyphIndex* const charactersToGlyphBuffer,
+                                       const Length* const     glyphsPerCharacterBuffer,
+                                       MetricsPtr&             metrics,
+                                       GlyphMetrics&           glyphMetrics,
+                                       GlyphIndex&             glyphIndex,
+                                       Length&                 numberOfGlyphs);
+
 } // namespace Text
 
 } // namespace Toolkit
index 35ad959..e0603f8 100644 (file)
 #include <dali-toolkit/internal/text/text-controller-impl.h>
 
 // EXTERNAL INCLUDES
-#include <cmath>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/actors/layer.h>
-#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <cmath>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/character-set-conversion.h>
@@ -54,10 +54,8 @@ const std::string EMPTY_STRING("");
 
 namespace Dali::Toolkit::Text
 {
-
 namespace
 {
-
 void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
 {
   // Sets the default text's color.
@@ -342,7 +340,7 @@ void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState
         decorator->StopCursorBlink();
         decorator->SetHandleActive(GRAB_HANDLE, false);
         if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
-            decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
+           decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
         {
           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
@@ -692,7 +690,6 @@ bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
   if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
   {
     mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
-    mRecalculateNaturalSize = true;
 
     RelayoutForNewLineSize();
     return true;
@@ -705,7 +702,6 @@ bool Controller::Impl::SetDefaultLineSize(float lineSize)
   if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
   {
     mLayoutEngine.SetDefaultLineSize(lineSize);
-    mRecalculateNaturalSize = true;
 
     RelayoutForNewLineSize();
     return true;
@@ -1199,7 +1195,10 @@ void Controller::Impl::GetCursorPosition(CharacterIndex logical,
   parameters.logical      = logical;
   parameters.isMultiline  = isMultiLine;
 
+  float defaultFontLineHeight = GetDefaultFontLineHeight();
+
   Text::GetCursorPosition(parameters,
+                          defaultFontLineHeight,
                           cursorInfo);
 
   // Adds Outline offset.
@@ -1470,8 +1469,13 @@ void Controller::Impl::RelayoutForNewLineSize()
   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
   mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
 
+  mTextUpdateInfo.mFullRelayoutNeeded = true;
+
+  // Need to recalculate natural size
+  mRecalculateNaturalSize = true;
+
   //remove selection
-  if(mEventData && mEventData->mState == EventData::SELECTING)
+  if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
   {
     ChangeState(EventData::EDITING);
   }
@@ -1493,7 +1497,7 @@ void Controller::Impl::ProcessInputStyleChangedSignals()
       // Emit the input style changed signal for each mask
       std::for_each(mEventData->mInputStyleChangedQueue.begin(),
                     mEventData->mInputStyleChangedQueue.end(),
-                    [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); } );
+                    [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
     }
 
     mEventData->mInputStyleChangedQueue.Clear();
@@ -1724,7 +1728,7 @@ void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
   {
     // Set the alignment.
     mModel->mVerticalAlignment = alignment;
-    mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+    mOperationsPending         = static_cast<OperationsMask>(mOperationsPending | ALIGN);
     RequestRelayout();
   }
 }
@@ -1804,7 +1808,6 @@ void Controller::Impl::ClearStyleData()
   mModel->mLogicalModel->ClearFontDescriptionRuns();
 }
 
-
 void Controller::Impl::ResetScrollPosition()
 {
   if(mEventData)
index ce40c60..34e7053 100644 (file)
@@ -49,7 +49,7 @@ namespace Toolkit
 {
 namespace Text
 {
-Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask, bool restoreLinesAndGlyphPositions)
+Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->CalculateLayoutSizeOnRequiredControllerSize\n");
   Size calculatedLayoutSize;
@@ -58,17 +58,6 @@ Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Control
   ModelPtr&         model       = impl.mModel;
   VisualModelPtr&   visualModel = model->mVisualModel;
 
-  // Store the pending operations mask so that it can be restored later on with no modifications made on it
-  // while getting the natural size were reflected on the original mask.
-  OperationsMask operationsPendingBackUp = static_cast<OperationsMask>(impl.mOperationsPending);
-
-  // This is a hotfix for side effect on Scrolling, LineWrap and Invalid position of cursor in TextEditor after calling CalculateLayoutSizeOnRequiredControllerSize.
-  // The number of lines and glyph-positions inside visualModel have been changed by calling DoRelayout with requestedControllerSize.
-  // Store the mLines and mGlyphPositions from visualModel so that they can be restored later on with no modifications made on them.
-  //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel, and then blah, blah, etc.
-  Vector<LineRun> linesBackup          = visualModel->mLines;
-  Vector<Vector2> glyphPositionsBackup = visualModel->mGlyphPositions;
-
   // Operations that can be done only once until the text changes.
   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
                                                                         GET_SCRIPTS |
@@ -89,39 +78,59 @@ Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Control
   textUpdateInfo.mParagraphCharacterIndex     = 0u;
   textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
 
-  // Make sure the model is up-to-date before layouting
-  impl.UpdateModel(onlyOnceOperations);
+  // This is to keep Index to the first character to be updated.
+  // Then restore it after calling Clear method.
+  auto updateInfoCharIndexBackup = textUpdateInfo.mCharacterIndex;
 
   // Get a reference to the pending operations member
   OperationsMask& operationsPending = impl.mOperationsPending;
 
   // Layout the text for the new width.
-  operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask);
+  // Apply the pending operations, requested operations and the only once operations.
+  // Then remove onlyOnceOperations
+  operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask | onlyOnceOperations);
+
+  // Make sure the model is up-to-date before layouting
+  impl.UpdateModel(static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE));
 
   // Store the actual control's size to restore later.
   const Size actualControlSize = visualModel->mControlSize;
 
   DoRelayout(impl,
              requestedControllerSize,
-             static_cast<OperationsMask>(onlyOnceOperations |
-                                         requestedOperationsMask),
+             static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE),
              calculatedLayoutSize);
 
   // Clear the update info. This info will be set the next time the text is updated.
   textUpdateInfo.Clear();
-  textUpdateInfo.mClearAll = true;
+
+  //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel,
+  //TODO: then calculate GlyphPositions. Lines, Size, Layout for Natural-Size
+  //TODO: and utilize the values in OperationsPending and TextUpdateInfo without changing the original one.
+  //TODO: Also it will improve performance because there is no need todo FullRelyout on the next need for layouting.
+
+  // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
+  // By this no need to take backup and restore it.
+  textUpdateInfo.mFullRelayoutNeeded = true;
+
+  // Restore mCharacterIndex. Because "Clear" set it to the maximum integer.
+  // The "CalculateTextUpdateIndices" does not work proprely because the mCharacterIndex will be greater than mPreviousNumberOfCharacters.
+  // Which apply an assumption to update only the last  paragraph. That could cause many of out of index crashes.
+  textUpdateInfo.mCharacterIndex = updateInfoCharIndexBackup;
+
+  // Do not do again the only once operations.
+  operationsPending = static_cast<OperationsMask>(operationsPending & ~onlyOnceOperations);
+
+  // Do the size related operations again.
+
+  const OperationsMask sizeOperations = static_cast<OperationsMask>(LAYOUT |
+                                                                    ALIGN |
+                                                                    REORDER);
+
+  operationsPending = static_cast<OperationsMask>(operationsPending | sizeOperations);
 
   // Restore the actual control's size.
   visualModel->mControlSize = actualControlSize;
-  // Restore the previously backed-up pending operations' mask without the only once operations.
-  impl.mOperationsPending = static_cast<OperationsMask>(operationsPendingBackUp & ~onlyOnceOperations);
-
-  // Restore the previously backed-up mLines and mGlyphPositions from visualModel.
-  if(restoreLinesAndGlyphPositions)
-  {
-    visualModel->mLines          = linesBackup;
-    visualModel->mGlyphPositions = glyphPositionsBackup;
-  }
 
   return calculatedLayoutSize;
 }
@@ -146,7 +155,7 @@ Vector3 Controller::Relayouter::GetNaturalSize(Controller& controller)
     OperationsMask requestedOperationsMask  = static_cast<OperationsMask>(LAYOUT | REORDER);
     Size           sizeMaxWidthAndMaxHeight = Size(MAX_FLOAT, MAX_FLOAT);
 
-    naturalSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeMaxWidthAndMaxHeight, requestedOperationsMask, true);
+    naturalSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeMaxWidthAndMaxHeight, requestedOperationsMask);
 
     // Stores the natural size to avoid recalculate it again
     // unless the text/style changes.
@@ -295,15 +304,7 @@ float Controller::Relayouter::GetHeightForWidth(Controller& controller, float wi
     OperationsMask requestedOperationsMask        = static_cast<OperationsMask>(LAYOUT);
     Size           sizeRequestedWidthAndMaxHeight = Size(width, MAX_FLOAT);
 
-    // Skip restore, because if GetHeightForWidth called before rendering and layouting then visualModel->mControlSize will be zero which will make LineCount zero.
-    // The implementation of Get LineCount property depends on calling GetHeightForWidth then read mLines.Count() from visualModel direct.
-    // If the LineCount property is requested before rendering and layouting then the value will be zero, which is incorrect.
-    // So we will not restore the previously backed-up mLines and mGlyphPositions from visualModel in such case.
-    // Another case to skip restore is when the requested width equals the Control's width which means the caller need to update the old values.
-    // For example, when the text is changed.
-    bool restoreLinesAndGlyphPositions = (visualModel->mControlSize.width > 0 && visualModel->mControlSize.height > 0) && (visualModel->mControlSize.width != width);
-
-    layoutSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeRequestedWidthAndMaxHeight, requestedOperationsMask, restoreLinesAndGlyphPositions);
+    layoutSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeRequestedWidthAndMaxHeight, requestedOperationsMask);
 
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height);
   }
index decca97..b605545 100644 (file)
@@ -110,12 +110,10 @@ struct Controller::Relayouter
   * @param[in] controller The controller to calcualte size on it.
   * @param[in] requestedControllerSize The requested size of controller to calcualte layout size on it.
   * @param[in] requestedOperationsMask The requested operations-mask to calcualte layout size according to it.
-  * @param[in] restoreLinesAndGlyphPositions whether to restore lines and glyph-positions to status before requesting calculation on size.
   *
   * @return The calculated layout-size.
   */
-  static Size CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask, bool restoreLinesAndGlyphPositions);
-
+  static Size CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask);
 };
 
 } // namespace Text
index 757d54a..be985d1 100644 (file)
@@ -37,6 +37,7 @@
 #include <dali-toolkit/internal/text/text-controller-relayouter.h>
 #include <dali-toolkit/internal/text/text-controller-text-updater.h>
 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
+#include <dali-toolkit/internal/text/text-geometry.h>
 
 namespace
 {
@@ -116,7 +117,6 @@ void UpdateCursorPosition(Dali::Toolkit::Text::EventData* eventData)
 
 namespace Dali::Toolkit::Text
 {
-
 void Controller::EnableTextInput(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
 {
   if(!decorator)
@@ -1234,6 +1234,24 @@ void Controller::RequestRelayout()
   mImpl->RequestRelayout();
 }
 
+Vector<Vector2> Controller::GetTextSize(CharacterIndex startIndex, CharacterIndex endIndex)
+{
+  Vector<Vector2> sizesList;
+  Vector<Vector2> positionsList;
+
+  GetTextGeometry(mImpl->mModel, startIndex, endIndex, sizesList, positionsList);
+  return sizesList;
+}
+
+Vector<Vector2> Controller::GetTextPosition(CharacterIndex startIndex, CharacterIndex endIndex)
+{
+  Vector<Vector2> sizesList;
+  Vector<Vector2> positionsList;
+
+  GetTextGeometry(mImpl->mModel, startIndex, endIndex, sizesList, positionsList);
+  return positionsList;
+}
+
 bool Controller::IsInputStyleChangedSignalsQueueEmpty()
 {
   return mImpl->IsInputStyleChangedSignalsQueueEmpty();
index a582110..b16186b 100644 (file)
@@ -23,9 +23,9 @@
 #include <dali/public-api/events/gesture.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
-#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
 #include <dali-toolkit/internal/text/hidden-text.h>
@@ -1516,6 +1516,28 @@ public: // Queries & retrieves.
   Dali::LayoutDirection::Type GetLayoutDirection(Dali::Actor& actor) const;
 
   /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(CharacterIndex startIndex, CharacterIndex endIndex);
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(CharacterIndex startIndex, CharacterIndex endIndex);
+
+  /**
    * @brief Sets the layout direction changed.
    */
   void ChangedLayoutDirection();
diff --git a/dali-toolkit/internal/text/text-geometry.cpp b/dali-toolkit/internal/text/text-geometry.cpp
new file mode 100644 (file)
index 0000000..9fa5ad1
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/text-geometry.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+
+using namespace Dali;
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, GlyphIndex& lastGlyphOfLine, Length numberOfLines)
+{
+  if(index == lastGlyphOfLine)
+  {
+    ++lineIndex;
+    if(lineIndex < numberOfLines)
+    {
+      ++lineRun;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void UpdateLineInfo(const LineRun* lineRun, float& currentLineOffset, float& currentLineHeight, GlyphIndex& lastGlyphOfLine)
+{
+  lastGlyphOfLine   = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+  currentLineOffset = currentLineOffset + currentLineHeight;
+  currentLineHeight = GetLineHeight(*lineRun);
+}
+
+void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector<Vector2>& sizesList, Vector<Vector2>& positionsList)
+{
+  VisualModelPtr&  visualModel  = textModel->mVisualModel;
+  LogicalModelPtr& logicalModel = textModel->mLogicalModel;
+
+  const GlyphIndex* const         charactersToGlyphBuffer        = visualModel->mCharactersToGlyph.Begin();
+  const Length* const             glyphsPerCharacterBuffer       = visualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const          glyphsBuffer                   = visualModel->mGlyphs.Begin();
+  const Vector2* const            positionsBuffer                = visualModel->mGlyphPositions.Begin();
+  const Length* const             charactersPerGlyphBuffer       = visualModel->mCharactersPerGlyph.Begin();
+  const CharacterIndex* const     glyphToCharacterBuffer         = visualModel->mGlyphsToCharacters.Begin();
+  const CharacterDirection* const modelCharacterDirectionsBuffer = (0u != logicalModel->mCharacterDirections.Count()) ? logicalModel->mCharacterDirections.Begin() : NULL;
+
+  if(startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count())
+    return;
+
+  if(startIndex >= logicalModel->mText.Count())
+    startIndex = logicalModel->mText.Count() - 1;
+
+  if(endIndex >= logicalModel->mText.Count())
+    endIndex = logicalModel->mText.Count() - 1;
+
+  if(startIndex > endIndex)
+  {
+    std::swap(startIndex, endIndex);
+  }
+
+  LineRun*   lineRun    = visualModel->mLines.Begin();
+  GlyphIndex glyphStart = *(charactersToGlyphBuffer + startIndex);
+
+  //if glyph not in the first line (in some ellipsis cases)
+  if(glyphStart < lineRun->glyphRun.glyphIndex)
+  {
+    glyphStart = lineRun->glyphRun.glyphIndex;
+    startIndex = *(glyphToCharacterBuffer + glyphStart);
+
+    if(startIndex > endIndex)
+    {
+      std::swap(startIndex, endIndex);
+    }
+  }
+
+  const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + endIndex);
+  GlyphIndex   glyphEnd       = *(charactersToGlyphBuffer + endIndex) + ((numberOfGlyphs > 0) ? numberOfGlyphs - 1u : 0u);
+  LineIndex    lineIndex      = visualModel->GetLineOfCharacter(startIndex);
+  Length       numberOfLines  = visualModel->GetTotalNumberOfLines();
+
+  LineIndex firstLineIndex = lineIndex;
+  Size      textInLineSize;
+  Vector2   textInLinePosition;
+
+  lineRun += firstLineIndex;
+
+  //get the first line and its vertical offset
+  float      currentLineOffset = CalculateLineOffset(visualModel->mLines, firstLineIndex);
+  float      currentLineHeight = GetLineHeight(*lineRun);
+  GlyphIndex lastGlyphOfLine   = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1;
+
+  // Check if the first/last glyph is a ligature that needs be splitted like English fi or Arabic ﻻ.
+  const Length numberOfCharactersStart = *(charactersPerGlyphBuffer + glyphStart);
+  const Length numberOfCharactersEnd   = *(charactersPerGlyphBuffer + glyphEnd);
+
+  bool splitStartGlyph = (numberOfCharactersStart > 1u) && HasLigatureMustBreak(logicalModel->GetScript(startIndex));
+  bool splitEndGlyph   = (glyphStart != glyphEnd) && (numberOfCharactersEnd > 1u) && HasLigatureMustBreak(logicalModel->GetScript(endIndex));
+
+  Vector2            currentSize;
+  Vector2            currentPosition;
+  Vector2            blockSize;
+  Vector2            blockPos;
+  CharacterDirection isCurrentRightToLeft;
+
+  CharacterDirection                      isPrevoiusRightToLeft           = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + startIndex) : false);
+  const bool                              isEllipsisEnabled               = textModel->mElideEnabled;
+  const GlyphIndex                        startIndexOfGlyphs              = textModel->GetStartIndexOfElidedGlyphs();
+  const GlyphIndex                        endIndexOfGlyphs                = textModel->GetEndIndexOfElidedGlyphs();
+  const GlyphIndex                        firstMiddleIndexOfElidedGlyphs  = textModel->GetFirstMiddleIndexOfElidedGlyphs();
+  const GlyphIndex                        secondMiddleIndexOfElidedGlyphs = textModel->GetSecondMiddleIndexOfElidedGlyphs();
+  const DevelText::EllipsisPosition::Type ellipsisPosition                = textModel->GetEllipsisPosition();
+
+  for(GlyphIndex index = glyphStart; index <= glyphEnd; ++index)
+  {
+    if(isEllipsisEnabled)
+    {
+      if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
+      {
+        if(index >= firstMiddleIndexOfElidedGlyphs &&
+           index < secondMiddleIndexOfElidedGlyphs)
+        {
+          if((index - 1 == firstMiddleIndexOfElidedGlyphs) && (firstMiddleIndexOfElidedGlyphs != 0))
+          {
+            sizesList.PushBack(blockSize);
+            positionsList.PushBack(blockPos);
+          }
+
+          if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+          {
+            UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+          }
+          // Ignore any glyph that was removed
+          continue;
+        }
+      }
+      else
+      {
+        if((ellipsisPosition == DevelText::EllipsisPosition::END) && (index >= endIndexOfGlyphs))
+        {
+          //skip remaining elided glyphs
+          break;
+        }
+        else if((ellipsisPosition == DevelText::EllipsisPosition::START) && (index <= startIndexOfGlyphs))
+        {
+          if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+          {
+            UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+          }
+
+          continue;
+        }
+      }
+    }
+
+    const GlyphInfo& glyph    = *(glyphsBuffer + index);
+    const Vector2&   position = *(positionsBuffer + index);
+
+    // If NULL, means all of the characters is left to right.
+    isCurrentRightToLeft = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + *(glyphToCharacterBuffer + index)) : false);
+
+    if(splitStartGlyph && (index == glyphStart))
+    {
+      // If the first glyph is a ligature that needs to be splitted, we may need only to add part of the glyph.
+      const float          glyphAdvance       = glyph.advance / static_cast<float>(numberOfCharactersStart);
+      const CharacterIndex interGlyphIndex    = startIndex - *(glyphToCharacterBuffer + glyphStart);
+      const Length         numberOfCharacters = (glyphStart == glyphEnd) ? (endIndex - startIndex) + 1 : (numberOfCharactersStart - interGlyphIndex);
+
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + glyphAdvance * static_cast<float>(isCurrentRightToLeft ? (numberOfCharactersStart - interGlyphIndex - numberOfCharacters) : interGlyphIndex);
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = static_cast<float>(numberOfCharacters) * glyphAdvance;
+      currentSize.y     = currentLineHeight;
+      splitStartGlyph   = false;
+    }
+    else if(splitEndGlyph && (index == glyphEnd))
+    {
+      const float          glyphAdvance       = glyph.advance / static_cast<float>(numberOfCharactersEnd);
+      const CharacterIndex interGlyphIndex    = endIndex - *(glyphToCharacterBuffer + glyphEnd);
+      const Length         numberOfCharacters = numberOfCharactersEnd - interGlyphIndex - 1;
+
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + (isCurrentRightToLeft ? (glyphAdvance * static_cast<float>(numberOfCharacters)) : 0.f);
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = static_cast<float>(interGlyphIndex + 1) * glyphAdvance;
+      currentSize.y     = currentLineHeight;
+      splitEndGlyph     = false;
+    }
+    else
+    {
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x;
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = glyph.advance;
+      currentSize.y     = currentLineHeight;
+
+      // if there is next line to retrieve.
+      if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+      {
+        UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+      }
+    }
+
+    if((index == glyphStart) || (isEllipsisEnabled && (((ellipsisPosition == DevelText::EllipsisPosition::MIDDLE) && (index == secondMiddleIndexOfElidedGlyphs)) || ((ellipsisPosition == DevelText::EllipsisPosition::START) && (index - 1 == startIndexOfGlyphs)))))
+    {
+      blockPos  = currentPosition;
+      blockSize = currentSize;
+    }
+    else if((isPrevoiusRightToLeft != isCurrentRightToLeft) || (blockPos.y != currentPosition.y)) //new direction or new line
+    {
+      sizesList.PushBack(blockSize);
+      positionsList.PushBack(blockPos);
+
+      blockPos  = currentPosition;
+      blockSize = currentSize;
+    }
+    else
+    {
+      if(isCurrentRightToLeft)
+      {
+        blockPos.x -= currentSize.x;
+      }
+
+      blockSize.x += currentSize.x;
+    }
+
+    isPrevoiusRightToLeft = isCurrentRightToLeft;
+  }
+
+  //add last block
+  sizesList.PushBack(blockSize);
+  positionsList.PushBack(blockPos);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/text-geometry.h b/dali-toolkit/internal/text/text-geometry.h
new file mode 100644 (file)
index 0000000..fb118f0
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef DALI_TOOLKIT_TEXT_GEOMETRY_H
+#define DALI_TOOLKIT_TEXT_GEOMETRY_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/text-model.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+   * @brief Get the rendered size & position of a specific text range.
+   * if the requested text is at multilines, multiple sizes/positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] textModel text model containing text info.
+   * @param[in] startIndex start index of the text requested to get position/size for.
+   * @param[in] endIndex end index(included) of the text requested to get position/size for.
+   * @param[in] sizesList list of sizes for the reuested text.
+   * @param[in] positionsList list of positions for the requested text
+   */
+void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector<Vector2>& sizesList, Vector<Vector2>& positionsList);
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_GEOMETRY_H
index 5fa3843..9c57494 100644 (file)
@@ -204,6 +204,11 @@ void VisualModel::GetGlyphPositions(Vector2*   glyphPositions,
   memcpy(glyphPositions, mGlyphPositions.Begin() + glyphIndex, numberOfGlyphs * sizeof(Vector2));
 }
 
+Length VisualModel::GetTotalNumberOfLines() const
+{
+  return mLines.Size();
+}
+
 void VisualModel::GetNumberOfLines(GlyphIndex glyphIndex,
                                    Length     numberOfGlyphs,
                                    LineIndex& firstLine,
index 359b695..ac9b12d 100644 (file)
@@ -120,6 +120,13 @@ public:
   // Line interface.
 
   /**
+   * @brief Retrieves the total number of lines.
+   *
+   * @return The number of lines.
+   */
+  Length GetTotalNumberOfLines() const;
+
+  /**
    * @brief Retrieves the number of lines and the index to the first line where the given range of glyphs is laid out.
    *
    * @param[in] glyphIndex Index to the first glyph.
index 1d21356..ad8b5ac 100644 (file)
@@ -133,7 +133,7 @@ public: // from Visual
   /**
    * @copydoc Visual::Base::OnDoAction
    */
-  void OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes) override;
+  void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
 
 protected:
   /**
index 73b0931..31e0c55 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/integration-api/debug.h>
 
 //INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
@@ -113,6 +114,27 @@ void ColorVisual::DoSetProperties(const Property::Map& propertyMap)
     {
       DALI_LOG_ERROR("ColorVisual:DoSetProperties:: BLUR_RADIUS property has incorrect type: %d\n", blurRadiusValue->GetType());
     }
+
+    if(mBlurRadiusIndex != Property::INVALID_INDEX)
+    {
+      mImpl->mRenderer.SetProperty(mBlurRadiusIndex, mBlurRadius);
+    }
+    else if(DALI_UNLIKELY(mImpl->mRenderer && (!EqualsZero(mBlurRadius) || mAlwaysUsingBlurRadius)))
+    {
+      // Unusual case. SetProperty called after OnInitialize().
+      // Assume that DoAction call UPDATE_PROPERTY.
+      // We must regist properies into renderer, and update shader.
+
+      // BlurRadius added by this action. Regist property to renderer.
+      mBlurRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelColorVisual::Property::BLUR_RADIUS, BLUR_RADIUS_NAME, mBlurRadius);
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+      // Change the shader must not be occured many times. we always have to use blur feature.
+      mAlwaysUsingBlurRadius = true;
+
+      // Change shader
+      UpdateShader();
+    }
   }
 }
 
index a8de5a8..577a433 100644 (file)
@@ -46,6 +46,23 @@ ImageAtlasManager::~ImageAtlasManager()
 {
 }
 
+bool ImageAtlasManager::CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const
+{
+  ImageDimensions dimensions = size;
+  ImageDimensions zero;
+  if(size == zero)
+  {
+    dimensions = Dali::GetClosestImageSize(url.GetUrl());
+  }
+
+  // big image, atlasing is not applied
+  if(static_cast<uint32_t>(dimensions.GetWidth()) * static_cast<uint32_t>(dimensions.GetHeight()) > MAX_ITEM_AREA || dimensions.GetWidth() > DEFAULT_ATLAS_SIZE || dimensions.GetHeight() > DEFAULT_ATLAS_SIZE)
+  {
+    return false;
+  }
+  return true;
+}
+
 TextureSet ImageAtlasManager::Add(Vector4&             textureRect,
                                   const VisualUrl&     url,
                                   ImageDimensions&     size,
index b8f5a11..d2630c1 100644 (file)
@@ -52,6 +52,15 @@ public:
   ImageAtlasManager();
 
   /**
+   * @brief Check whether the image of url could be Atlas or not.
+   *
+   * @param [in] url The URL of the resource image file to use.
+   * @param [in] size The width and height to fit the loaded image to.
+   * @return True if the image could be Atlas.
+   */
+  bool CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const;
+
+  /**
    * @brief Add an image to the atlas.
    *
    * @note To make the atlasing efficient, an valid size should be provided.
index 650c508..7676bf8 100644 (file)
@@ -477,7 +477,7 @@ void ImageVisual::GetNaturalSize(Vector2& naturalSize)
     }
 
     auto textureSet = mImpl->mRenderer.GetTextures();
-    if(textureSet)
+    if(textureSet && textureSet.GetTextureCount())
     {
       auto texture = textureSet.GetTexture(0);
       if(texture)
@@ -795,11 +795,11 @@ void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
   }
 }
 
-void ImageVisual::OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes)
+void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
 {
   // Check if action is valid for this visual type and perform action if possible
 
-  switch(actionName)
+  switch(actionId)
   {
     case DevelImageVisual::Action::RELOAD:
     {
index c0a3f75..55fb59b 100644 (file)
@@ -171,7 +171,7 @@ public: // from Visual
   /**
    * @copydoc Visual::Base::OnDoAction
    */
-  void OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes) override;
+  void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
 
 protected:
   /**
index 093954e..49867c4 100644 (file)
@@ -122,8 +122,10 @@ TextureManager::MaskingData::MaskingData()
 }
 
 TextureManager::TextureManager()
-: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
-  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
+: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]()
+                     { return AsyncLoadingHelper(*this); }),
+  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]()
+                      { return AsyncLoadingHelper(*this); }),
   mExternalTextures(),
   mLifecycleObservers(),
   mLoadQueue(),
@@ -173,7 +175,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(
   else
   {
     auto preMultiply                    = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex);
+    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false);
     TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
     if(loadState == TextureManager::LoadState::UPLOADED)
     {
@@ -220,7 +222,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
   }
 
   return pixelBuffer;
@@ -251,113 +253,100 @@ TextureSet TextureManager::LoadTexture(
       }
     }
   }
-  else if(synchronousLoading)
+  else
   {
-    PixelData data;
-    if(url.IsValid())
+    // For Atlas
+    if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize))
     {
-      Devel::PixelBuffer pixelBuffer;
-      if(url.IsBufferResource())
-      {
-        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
-        if(encodedImageBuffer)
-        {
-          pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-        }
-      }
-      else
-      {
-        pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-      }
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
+
       if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
       {
-        Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
+        Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
         if(maskPixelBuffer)
         {
           pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
         }
       }
+
+      PixelData data;
       if(pixelBuffer)
       {
         PreMultiply(pixelBuffer, preMultiplyOnLoad);
         data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+
+        if(data)
+        {
+          textureSet = imageAtlasManager->Add(textureRect, data);
+          if(textureSet)
+          {
+            textureRectSize.SetWidth(data.GetWidth());
+            textureRectSize.SetHeight(data.GetHeight());
+          }
+        }
+        else
+        {
+          DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
+        }
       }
-    }
-    if(!data)
-    {
-      DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous loading is failed\n");
-    }
-    else
-    {
-      if(atlasingStatus) // attempt atlasing
-      {
-        textureSet = imageAtlasManager->Add(textureRect, data);
-      }
-      if(!textureSet) // big image, no atlasing or atlasing failed
-      {
-        atlasingStatus  = false;
-        Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), data.GetWidth(), data.GetHeight());
-        texture.Upload(data);
-        textureSet = TextureSet::New();
-        textureSet.SetTexture(0u, texture);
-      }
-      else
+      if(!textureSet)
       {
-        textureRectSize.SetWidth(data.GetWidth());
-        textureRectSize.SetHeight(data.GetHeight());
+        atlasingStatus = false;
       }
     }
-  }
-  else
-  {
-    loadingStatus = true;
-    if(atlasingStatus)
-    {
-      textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
-    }
-    if(!textureSet) // big image, no atlasing or atlasing failed
+
+    if(!textureSet)
     {
-      atlasingStatus = false;
-      if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+      loadingStatus = true;
+      if(atlasingStatus)
       {
-        textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad);
+        textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
       }
-      else
+      if(!textureSet) // big image, no atlasing or atlasing failed
       {
-        maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl);
-        textureId              = RequestLoad(url,
-                                maskInfo->mAlphaMaskId,
-                                maskInfo->mContentScaleFactor,
-                                desiredSize,
-                                fittingMode,
-                                samplingMode,
-                                TextureManager::NO_ATLAS,
-                                maskInfo->mCropToMask,
-                                textureObserver,
-                                orientationCorrection,
-                                reloadPolicy,
-                                preMultiplyOnLoad);
-      }
+        atlasingStatus = false;
+        if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+        {
+          textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+        }
+        else
+        {
+          maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading);
+          textureId              = RequestLoad(url,
+                                  maskInfo->mAlphaMaskId,
+                                  maskInfo->mContentScaleFactor,
+                                  desiredSize,
+                                  fittingMode,
+                                  samplingMode,
+                                  TextureManager::NO_ATLAS,
+                                  maskInfo->mCropToMask,
+                                  textureObserver,
+                                  orientationCorrection,
+                                  reloadPolicy,
+                                  preMultiplyOnLoad,
+                                  synchronousLoading);
+        }
+
+        TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
+        if(loadState == TextureManager::LoadState::UPLOADED)
+        {
+          // UploadComplete has already been called - keep the same texture set
+          textureSet = GetTextureSet(textureId);
+        }
 
-      TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
-      if(loadState == TextureManager::LoadState::UPLOADED)
+        // If we are loading the texture, or waiting for the ready signal handler to complete, inform
+        // caller that they need to wait.
+        loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
+                         loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
+                         loadState == TextureManager::LoadState::MASK_APPLYING ||
+                         loadState == TextureManager::LoadState::MASK_APPLIED ||
+                         loadState == TextureManager::LoadState::NOT_STARTED ||
+                         mQueueLoadFlag);
+      }
+      else
       {
-        // UploadComplete has already been called - keep the same texture set
-        textureSet = GetTextureSet(textureId);
+        textureRectSize = desiredSize;
       }
-
-      // If we are loading the texture, or waiting for the ready signal handler to complete, inform
-      // caller that they need to wait.
-      loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
-                       loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
-                       loadState == TextureManager::LoadState::MASK_APPLYING ||
-                       loadState == TextureManager::LoadState::MASK_APPLIED ||
-                       loadState == TextureManager::LoadState::NOT_STARTED ||
-                       mQueueLoadFlag);
-    }
-    else
-    {
-      textureRectSize = desiredSize;
     }
   }
 
@@ -368,6 +357,11 @@ TextureSet TextureManager::LoadTexture(
     textureSet.SetSampler(0u, sampler);
   }
 
+  if(synchronousLoading)
+  {
+    loadingStatus = false;
+  }
+
   return textureSet;
 }
 
@@ -380,9 +374,10 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                            synchronousLoading)
 {
-  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -397,16 +392,17 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                            synchronousLoading)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
-TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl)
+TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, bool synchronousLoading)
 {
   // Use the normal load procedure to get the alpha mask.
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
@@ -424,7 +420,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureManager::ReloadPolicy    reloadPolicy,
   TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
   Dali::AnimatedImageLoading      animatedImageLoading,
-  uint32_t                        frameIndex)
+  uint32_t                        frameIndex,
+  bool                            synchronousLoading)
 {
   // First check if the requested Texture is cached.
   bool isAnimatedImage = (animatedImageLoading) ? true : false;
@@ -457,37 +454,30 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
   }
 
-  // Check if the requested Texture exist in Encoded Buffer
-  // This mean, that buffer is not cached, and need to be decoded.
-  if(textureId == INVALID_TEXTURE_ID && VisualUrl::BUFFER == url.GetProtocolType())
+  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
   {
-    std::string location = url.GetLocation();
-    if(location.size() > 0u)
+    if(VisualUrl::BUFFER == url.GetProtocolType())
     {
-      TextureId                 targetId           = std::stoi(location);
-      const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
-      if(encodedImageBuffer)
+      std::string location = url.GetLocation();
+      if(location.size() > 0u)
       {
-        textureId = targetId;
-
-        // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
-        UseExternalResource(url.GetUrl());
-
-        // Insert this buffer at mTextureInfoContainer.
-        // This buffer will decode at ImageLoaderThread.
-        bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
-        mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
-        cacheIndex = mTextureInfoContainer.size() - 1u;
+        TextureId                 targetId           = std::stoi(location);
+        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
+        if(encodedImageBuffer)
+        {
+          textureId = targetId;
 
-        DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New buffered texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+          // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
+          UseExternalResource(url.GetUrl());
+        }
       }
     }
-  }
 
-  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
-  {
-    // We need a new Texture.
-    textureId        = GenerateUniqueTextureId();
+    if(textureId == INVALID_TEXTURE_ID)
+    {
+      textureId = GenerateUniqueTextureId();
+    }
+
     bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
     mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
     cacheIndex = mTextureInfoContainer.size() - 1u;
@@ -518,48 +508,101 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
 
-  // Check if we should add the observer.
-  // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
-  switch(textureInfo.loadState)
+  if(!synchronousLoading)
   {
-    case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
-    case TextureManager::LoadState::NOT_STARTED:
-    {
-      LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
-      break;
-    }
-    case TextureManager::LoadState::LOADING:
-    case TextureManager::LoadState::WAITING_FOR_MASK:
-    case TextureManager::LoadState::MASK_APPLYING:
-    case TextureManager::LoadState::MASK_APPLIED:
+    // Check if we should add the observer.
+    // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
+    switch(textureInfo.loadState)
     {
-      ObserveTexture(textureInfo, observer);
-      break;
-    }
-    case TextureManager::LoadState::UPLOADED:
-    {
-      if(observer)
+      case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
+      case TextureManager::LoadState::NOT_STARTED:
+      {
+        LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
+        break;
+      }
+      case TextureManager::LoadState::LOADING:
+      case TextureManager::LoadState::WAITING_FOR_MASK:
+      case TextureManager::LoadState::MASK_APPLYING:
+      case TextureManager::LoadState::MASK_APPLIED:
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::UPLOADED:
+      {
+        if(observer)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
+      }
+      case TextureManager::LoadState::CANCELLED:
+      {
+        // A cancelled texture hasn't finished loading yet. Treat as a loading texture
+        // (it's ref count has already been incremented, above)
+        textureInfo.loadState = TextureManager::LoadState::LOADING;
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::LOAD_FINISHED:
+      {
+        // Loading has already completed.
+        if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
       }
-      break;
     }
-    case TextureManager::LoadState::CANCELLED:
+  }
+  else
+  {
+    // If the image is already finished to load, use cached texture.
+    // We don't need to consider Observer becaouse this is synchronous loading.
+    if(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
+       textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)
     {
-      // A cancelled texture hasn't finished loading yet. Treat as a loading texture
-      // (it's ref count has already been incremented, above)
-      textureInfo.loadState = TextureManager::LoadState::LOADING;
-      ObserveTexture(textureInfo, observer);
-      break;
+      return textureId;
     }
-    case TextureManager::LoadState::LOAD_FINISHED:
+    else
     {
-      // Loading has already completed.
-      if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);;
+
+      if(!pixelBuffer)
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        // If pixelBuffer loading is failed in synchronously, call Remove() method.
+        Remove(textureId, nullptr);
+        return INVALID_TEXTURE_ID;
+      }
+
+      if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+      {
+        textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
+        textureInfo.loadState   = LoadState::LOAD_FINISHED;
+      }
+      else // For the image loading.
+      {
+        if(maskTextureId != INVALID_TEXTURE_ID)
+        {
+          int maskCacheIndex = GetCacheIndexFromId(maskTextureId);
+          if(maskCacheIndex != INVALID_CACHE_INDEX)
+          {
+            Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
+            if(maskPixelBuffer)
+            {
+              pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+            }
+          }
+          else
+          {
+            DALI_LOG_ERROR("Mask image is not stored in cache.\n");
+          }
+        }
+        PreMultiply(pixelBuffer, preMultiplyOnLoad);
+
+        // Upload texture
+        UploadTexture(pixelBuffer, textureInfo);
       }
-      break;
     }
   }
 
@@ -687,6 +730,28 @@ TextureManager::LoadState TextureManager::GetTextureStateInternal(TextureId text
   return loadState;
 }
 
+Devel::PixelBuffer TextureManager::LoadImageSynchronously(const VisualUrl&         url,
+                                                          const ImageDimensions    desiredSize,
+                                                          FittingMode::Type        fittingMode,
+                                                          Dali::SamplingMode::Type samplingMode,
+                                                          bool                     orientationCorrection)
+{
+  Devel::PixelBuffer pixelBuffer;
+  if(url.IsBufferResource())
+  {
+    const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
+    if(encodedImageBuffer)
+    {
+      pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+    }
+  }
+  else
+  {
+    pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+  }
+  return pixelBuffer;
+}
+
 TextureSet TextureManager::GetTextureSet(TextureId textureId)
 {
   TextureSet textureSet; // empty handle
@@ -759,7 +824,7 @@ std::string TextureManager::AddExternalEncodedImageBuffer(const EncodedImageBuff
     {
       // If same buffer added, increase reference count and return.
       elem.referenceCount++;
-      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));;
+      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
     }
   }
   TextureManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
@@ -807,7 +872,7 @@ EncodedImageBuffer TextureManager::RemoveExternalEncodedImageBuffer(const std::s
       std::string location = VisualUrl::GetLocation(url);
       if(location.size() > 0u)
       {
-        TextureId id = std::stoi(location);
+        TextureId  id  = std::stoi(location);
         const auto end = mEncodedBufferTextures.end();
         for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
         {
@@ -1049,7 +1114,10 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
       // If there is a mask texture ID associated with this texture, then apply the mask
       // if it's already loaded. If it hasn't, and the mask is still loading,
       // wait for the mask to finish loading.
-      if(textureInfo.maskTextureId != INVALID_TEXTURE_ID)
+      // note, If the texture is already uploaded synchronously during loading,
+      // we don't need to apply mask.
+      if(textureInfo.loadState != LoadState::UPLOADED &&
+         textureInfo.maskTextureId != INVALID_TEXTURE_ID)
       {
         if(textureInfo.loadState == LoadState::MASK_APPLYING)
         {
@@ -1153,7 +1221,7 @@ void TextureManager::ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId
 
 void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo)
 {
-  if(textureInfo.useAtlas != USE_ATLAS)
+  if(textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != USE_ATLAS)
   {
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId);
 
index 48eea78..7730d28 100644 (file)
@@ -75,9 +75,10 @@ public:
    */
   enum class StorageType : uint8_t
   {
-    KEEP_PIXEL_BUFFER,
-    RETURN_PIXEL_BUFFER,
-    UPLOAD_TO_TEXTURE
+    KEEP_PIXEL_BUFFER,    ///< Keep loaded pixel buffer inside of texture manager without making texture. This could be used for inside pixel process like mask image.
+    RETURN_PIXEL_BUFFER,  ///< Return loaded pixel buffer without making texture.
+                          ///  Because a pixel buffer cannot be used multiple texture, this pixel buffer only cached during loading, and is removed after loading is finished.
+    UPLOAD_TO_TEXTURE     ///< Loaded image will be uploaded to texture and the texture will be returned.
   };
 
   /**
@@ -177,7 +178,6 @@ public:
    *
    * @return                          The texture set containing the frame of animated image, or empty if still loading.
    */
-
   TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading,
                                       uint32_t                   frameIndex,
                                       Dali::SamplingMode::Type   samplingMode,
@@ -206,7 +206,6 @@ public:
    *
    * @return                          The pixel buffer containing the image, or empty if still loading.
    */
-
   Devel::PixelBuffer LoadPixelBuffer(const VisualUrl&                url,
                                      Dali::ImageDimensions           desiredSize,
                                      Dali::FittingMode::Type         fittingMode,
@@ -253,7 +252,6 @@ public:
    *
    * @return                          The texture set containing the image, or empty if still loading.
    */
-
   TextureSet LoadTexture(const VisualUrl&             url,
                          Dali::ImageDimensions        desiredSize,
                          Dali::FittingMode::Type      fittingMode,
@@ -293,6 +291,7 @@ public:
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in,out] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if the image has no alpha channel
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -303,7 +302,8 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * @brief Requests an image load of the given URL, when the texture has
@@ -335,6 +335,7 @@ public:
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if the
    *                                  image has no alpha channel
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -348,13 +349,15 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * Requests a masking image to be loaded. This mask is not uploaded to GL,
    * instead, it is stored in CPU memory, and can be used for CPU blending.
    */
-  TextureId RequestMaskLoad(const VisualUrl& maskUrl);
+  TextureId RequestMaskLoad(const VisualUrl& maskUrl,
+                            bool             synchronousLoading = false);
 
   /**
    * @brief Remove a Texture from the TextureManager.
@@ -492,6 +495,7 @@ private:
    *                                  there is no alpha
    * @param[in] animatedImageLoading  The AnimatedImageLoading to load animated image
    * @param[in] frameIndex            The frame index of a frame to be loaded frame
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoadInternal(
@@ -509,7 +513,8 @@ private:
     TextureManager::ReloadPolicy reloadPolicy,
     MultiplyOnLoad&              preMultiplyOnLoad,
     Dali::AnimatedImageLoading   animatedImageLoading,
-    uint32_t                     frameIndex);
+    uint32_t                     frameIndex,
+    bool                         synchronousLoading);
 
   /**
    * @brief Get the current state of a texture
@@ -519,6 +524,23 @@ private:
    */
   LoadState GetTextureStateInternal(TextureId textureId);
 
+  /**
+   * @brief Load a new image synchronously.
+   * @param[in] url                   The URL of the image to load
+   * @param[in] desiredSize           The size the image is likely to appear at.
+   *                                  This can be set to 0,0 for automatic
+   * @param[in] fittingMode           The FittingMode to use
+   * @param[in] samplingMode          The SamplingMode to use
+   * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image,
+   *                                  e.g., from portrait to landscape
+   * @return PixelBuffer of loaded image.
+   */
+  Devel::PixelBuffer LoadImageSynchronously(const VisualUrl&         url,
+                                            const ImageDimensions    desiredSize,
+                                            FittingMode::Type        fittingMode,
+                                            Dali::SamplingMode::Type samplingMode,
+                                            bool                     orientationCorrection);
+
   typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
 
   // Structs:
index f0a7af4..2841655 100644 (file)
@@ -168,6 +168,7 @@ void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
 
 void Visual::Base::SetProperties(const Property::Map& propertyMap)
 {
+  bool needUpdateShader = false;
   for(size_t i = 0; i < propertyMap.Count(); ++i)
   {
     const KeyValuePair&    pair  = propertyMap.GetKeyValue(i);
@@ -300,6 +301,26 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
         {
           mImpl->mRenderer.SetProperty(mImpl->mBorderlineWidthIndex, mImpl->mBorderlineWidth);
         }
+        else if(DALI_UNLIKELY(mImpl->mRenderer && IsBorderlineRequired()))
+        {
+          // Unusual case. SetProperty called after OnInitialize().
+          // Assume that DoAction call UPDATE_PROPERTY.
+          // We must regist properies into renderer, and update shader.
+
+          // Borderline added by this action. Regist property to renderer.
+          mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
+          mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
+          mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
+
+          // Make Blend mode ON_WITHOUT_CULL for transparent mix color.
+          mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
+
+          // Change the shader must not be occured many times. we always have to use borderline feature.
+          mImpl->mAlwaysUsingBorderline = true;
+
+          // Change shader
+          needUpdateShader = true;
+        }
         break;
       }
       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
@@ -358,6 +379,28 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
         {
           mImpl->mRenderer.SetProperty(mImpl->mCornerRadiusIndex, mImpl->mCornerRadius);
         }
+        else if(DALI_UNLIKELY(mImpl->mRenderer && IsRoundedCornerRequired()))
+        {
+          // Unusual case. SetProperty called after OnInitialize().
+          // Assume that DoAction call UPDATE_PROPERTY.
+          // We must regist properies into renderer, and update shader.
+
+          // CornerRadius added by this action. Regist property to renderer.
+          mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
+          mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
+
+          // Change the shader must not be occured many times. we always have to use corner radius feature.
+          mImpl->mAlwaysUsingCornerRadius = true;
+
+          if(!IsBorderlineRequired())
+          {
+            // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
+            mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+          }
+
+          // Change shader
+          needUpdateShader = true;
+        }
         break;
       }
       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
@@ -371,6 +414,15 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
             {
               mImpl->mCornerRadiusPolicy = policy;
+              if(DALI_UNLIKELY(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX))
+              {
+                // Unusual case. SetProperty called after OnInitialize().
+                // Assume that DoAction call UPDATE_PROPERTY.
+                // We must update properies result into renderer
+                // Note : mImpl->mCornerRadiusIndex is not INVALID_INDEX.
+                // So CornerRadiusPolicy property is already registed.
+                mImpl->mRenderer.SetProperty(mImpl->mRenderer.GetPropertyIndex(CORNER_RADIUS_POLICY), mImpl->mCornerRadiusPolicy);
+              }
               break;
             }
             default:
@@ -386,6 +438,11 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
   }
 
   DoSetProperties(propertyMap);
+
+  if(DALI_UNLIKELY(needUpdateShader))
+  {
+    UpdateShader();
+  }
 }
 
 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
index d04d3df..b7b72e1 100644 (file)
@@ -92,12 +92,12 @@ public:
   void SetTransformAndSize(const Property::Map& transform, Size controlSize);
 
   /**
-   * @brief Performs an action on the visual with the given action name and attributes.
+   * @brief Performs an action on the visual with the given action id and attributes.
    *
-   * @param[in] actionName The name of the action to perform this API only takes an Index
+   * @param[in] actionId The id of the action to perform this API only takes an Index
    * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
    */
-  void DoAction(const Dali::Property::Index actionName, const Dali::Property::Value attributes);
+  void DoAction(const Dali::Property::Index actionId, const Dali::Property::Value attributes);
 
   /**
    * @copydoc Toolkit::Visual::Base::GetHeightForWidth
index 12a4324..ed05a9a 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 1;
-const unsigned int TOOLKIT_MICRO_VERSION = 2;
+const unsigned int TOOLKIT_MICRO_VERSION = 3;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index c007a73..10bf3ed 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.1.2
+Version:    2.1.3
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT