Merge "Add support for text geometry" into devel/master
authorBowon Ryu <bowon.ryu@samsung.com>
Mon, 13 Dec 2021 03:49:30 +0000 (03:49 +0000)
committerGerrit Code Review <gerrit@review>
Mon, 13 Dec 2021 03:49:30 +0000 (03:49 +0000)
25 files changed:
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
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/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/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

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..b2a7c5f 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;
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 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 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 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..e599922
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * 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 < 0 && endIndex < 0) || (startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count()))
+    return;
+
+  if(startIndex < 0)
+    startIndex = 0;
+
+  if(endIndex < 0)
+    endIndex = 0;
+
+  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.