Add GetCharacterBoundingRectangle API. 72/282872/21
authorsarajammal <s.al-jammal@partner.samsung.com>
Wed, 12 Oct 2022 11:01:34 +0000 (14:01 +0300)
committerSara AlJammal <s.al-jammal@partner.samsung.com>
Mon, 7 Nov 2022 12:29:53 +0000 (12:29 +0000)
Calculate the character size and position:
- X, the left starting point of the character.
- Y, the left top point of the character.
- Width.
- Height.

Function prototype: GetCharacterBoundingRectangle(ModelPtr model, uint32_t charIndex);

Add new API to TextGeometry.

Reference patches:
https://review.tizen.org/gerrit/c/platform/core/uifw/dali-toolkit/+/266891

Change-Id: Ifcdcca5ae3af52da79af899b26d3a273713e012d

14 files changed:
automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp
dali-toolkit/devel-api/text/text-geometry-devel.cpp
dali-toolkit/devel-api/text/text-geometry-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/text/controller/text-controller.cpp
dali-toolkit/internal/text/controller/text-controller.h
dali-toolkit/internal/text/line-run.h
dali-toolkit/internal/text/text-geometry.cpp
dali-toolkit/internal/text/text-geometry.h

index 34f8d1e..76a8b46 100644 (file)
@@ -328,4 +328,202 @@ int UtcDaliTextGeometryWithEllipsisEndGetLineBoundingRectangleLabel(void)
   END_TEST;
 }
 
+int UtcDaliTextGeometryGetCharacterBoundingRectangleLabel(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryGetCharacterBoundingRectangleLabel");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  application.GetScene().Add(label);
+
+  float lineSpacing = -20.f;
+
+  label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> characterGeometry = TextGeometry::GetCharacterBoundingRectangle(label, 1);
+
+  std::cout << " characterGeometry "<< characterGeometry <<std::endl;
+
+  Rect<> expectedCharacterGeometry = {13.4844f, 16.0f, 18.7031f, 16.0f};
+  std::cout<< " expectedCharacterGeometry " << characterGeometry <<std::endl;
+
+  std:: cout << "if" << (characterGeometry == expectedCharacterGeometry) <<std::endl;
+  TestTextGeometryUtils::CheckRectGeometryResult(characterGeometry, expectedCharacterGeometry);
+
+  END_TEST;
+}
+
+int UtcDaliTextGeometryGetCharacterBoundingRectangleEditor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryGetCharacterBoundingRectangleEditor");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK(editor);
+
+  application.GetScene().Add(editor);
+
+  editor.SetProperty(Actor::Property::SIZE, Vector2(160.0f, 250.f));
+  editor.SetProperty(TextEditor::Property::POINT_SIZE, 10.f);
+  editor.SetProperty(TextEditor::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> characterGeometry = TextGeometry::GetCharacterBoundingRectangle(editor, 1);
+
+  Rect<> expectedCharacterGeometry = {13.4844f, 16.0f, 18.7031f, 16.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(characterGeometry, expectedCharacterGeometry);
+
+  END_TEST;
+}
+
+int UtcDaliTextGeometryGetCharacterBoundingRectangleField(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryGetCharacterBoundingRectangleField");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK(field);
+
+  application.GetScene().Add(field);
+
+  field.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  field.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 350.f));
+  field.SetProperty(TextField::Property::POINT_SIZE, 10.f);
+  field.SetProperty(TextField::Property::TEXT, "مرحبا بالعالم");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> characterGeometry = TextGeometry::GetCharacterBoundingRectangle(field, 0);
+
+  Rect<> expectedCharacterGeometry = {142.844f, 10.0f, 16.375f, 11.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(characterGeometry, expectedCharacterGeometry);
+
+  END_TEST;
+}
+// char tc
+
+int UtcDaliTextGeometryEmptyTextGetCharacterBoundingRectangleLabel(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryEmptyTextGetCharacterBoundingRectangleLabel");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  application.GetScene().Add(label);
+
+  label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(TextLabel::Property::TEXT, "");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> charGeometry = TextGeometry::GetCharacterBoundingRectangle(label, 0);
+
+  Rect<> expectedCharGeometry = {0.0f, 0.0f, 0.0f, 0.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(charGeometry, expectedCharGeometry);
+
+  END_TEST;
+}
+
+int UtcDaliTextGeometryLineSpacingPositiveGetCharacterBoundingRectangleLabel(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryLineSpacingPositiveGetCharacterBoundingRectangleLabel");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  application.GetScene().Add(label);
+
+  float lineSpacing = 20.f;
 
+  label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> charGeometry = TextGeometry::GetCharacterBoundingRectangle(label, 1);
+
+  Rect<> expectedCharGeometry = {13.4844f, 16.0f, 18.7031f, 16.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(charGeometry, expectedCharGeometry);
+
+  END_TEST;
+}
+
+int UtcDaliTextGeometryWithVerticalLineAlignmentTopGetCharacterBoundingRectangleLabel(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryWithVerticalLineAlignmentTopGetCharacterBoundingRectangleLabel");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  application.GetScene().Add(label);
+
+  label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "TOP");
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> charGeometry = TextGeometry::GetCharacterBoundingRectangle(label, 1);
+
+  Rect<> expectedCharGeometry = {13.4844f, 16.0f, 18.7031f, 16.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(charGeometry, expectedCharGeometry);
+
+  END_TEST;
+}
+
+int UtcDaliTextGeometryWithVerticalLineAlignmentBottomGetCharacterBoundingRectangleLabel(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextGeometryWithVerticalLineAlignmentBottomGetCharacterBoundingRectangleLabel");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  application.GetScene().Add(label);
+
+  label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "BOTTOM");
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+
+  application.SendNotification();
+  application.Render();
+
+  Rect<> charGeometry = TextGeometry::GetCharacterBoundingRectangle(label, 1);
+
+  Rect<> expectedCharGeometry = {13.4844f, 16.0f, 18.7031f, 16.0f};
+
+  TestTextGeometryUtils::CheckRectGeometryResult(charGeometry, expectedCharGeometry);
+
+  END_TEST;
+}
\ No newline at end of file
index 1afc773..962484e 100644 (file)
@@ -47,6 +47,21 @@ Rect<float> GetLineBoundingRectangle(TextField field, const uint32_t lineIndex)
   return GetImpl(field).GetLineBoundingRectangle(lineIndex);
 }
 
+Rect<float> GetCharacterBoundingRectangle(TextLabel label, const uint32_t charIndex)
+{
+  return GetImpl(label).GetCharacterBoundingRectangle(charIndex);
+}
+
+Rect<float> GetCharacterBoundingRectangle(TextEditor editor, const uint32_t charIndex)
+{
+  return GetImpl(editor).GetCharacterBoundingRectangle(charIndex);
+}
+
+Rect<float> GetCharacterBoundingRectangle(TextField field, const uint32_t charIndex)
+{
+  return GetImpl(field).GetCharacterBoundingRectangle(charIndex);
+}
+
 } //namespace TextGeometry
 
 } // namespace Text
index b63f337..014a7ce 100644 (file)
@@ -62,6 +62,37 @@ namespace TextGeometry
    */
    DALI_TOOLKIT_API Rect<float> GetLineBoundingRectangle(TextField field, const uint32_t lineIndex);
 
+  /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] label text model containing text info.
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+   DALI_TOOLKIT_API Rect<float> GetCharacterBoundingRectangle(TextLabel label, const uint32_t charIndex);
+
+  /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] field text model containing text info.
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+   DALI_TOOLKIT_API Rect<float> GetCharacterBoundingRectangle(TextField field, const uint32_t charIndex);
+
+  /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] editor text model containing text info.
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+   DALI_TOOLKIT_API Rect<float> GetCharacterBoundingRectangle(TextEditor editor, const uint32_t charIndex);
+
+
 } // namespace TextGeometry
 
 } // namespace Text
index 890b22c..dae56ef 100644 (file)
@@ -369,6 +369,11 @@ Rect<float> TextEditor::GetLineBoundingRectangle(const uint32_t lineIndex) const
   return mController->GetLineBoundingRectangle(lineIndex);
 }
 
+Rect<float> TextEditor::GetCharacterBoundingRectangle(const uint32_t charIndex) const
+{
+  return mController->GetCharacterBoundingRectangle(charIndex);
+}
+
 string TextEditor::GetSelectedText() const
 {
   string selectedText = "";
index 0ad18a9..71d11c4 100644 (file)
@@ -367,6 +367,15 @@ public:
   Rect<float> GetLineBoundingRectangle(const uint32_t lineIndex) const;
 
   /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+  Rect<float> GetCharacterBoundingRectangle(const uint32_t charIndex) const;
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const override;
index 76d68df..6345f15 100644 (file)
@@ -1210,6 +1210,11 @@ Rect<float> TextField::GetLineBoundingRectangle(const uint32_t lineIndex) const
   return mController->GetLineBoundingRectangle(lineIndex);
 }
 
+Rect<float> TextField::GetCharacterBoundingRectangle(const uint32_t charIndex) const
+{
+  return mController->GetCharacterBoundingRectangle(charIndex);
+}
+
 std::string TextField::TextFieldAccessible::GetName() const
 {
   if(IsHiddenInput())
index 8e8a01a..ce96286 100644 (file)
@@ -376,6 +376,15 @@ public:
    */
   Rect<float> GetLineBoundingRectangle(const uint32_t lineIndex) const;
 
+  /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+  Rect<float> GetCharacterBoundingRectangle(const uint32_t charIndex) const;
+
 private: // Implementation
   /**
    * @copydoc Dali::Toolkit::Text::Controller::(InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent)
index cec3d0c..cb60705 100644 (file)
@@ -1241,6 +1241,11 @@ Rect<float> TextLabel::GetLineBoundingRectangle(const uint32_t lineIndex) const
   return mController->GetLineBoundingRectangle(lineIndex);
 }
 
+Rect<float> TextLabel::GetCharacterBoundingRectangle(const uint32_t charIndex) const
+{
+  return mController->GetCharacterBoundingRectangle(charIndex);
+}
+
 std::string TextLabel::TextLabelAccessible::GetNameRaw() const
 {
   return GetWholeText();
index 000b6f0..b935c84 100644 (file)
@@ -134,6 +134,15 @@ public:
    */
   Rect<float> GetLineBoundingRectangle(const uint32_t lineIndex) const;
 
+  /**
+   * @brief Get the character bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+  Rect<float> GetCharacterBoundingRectangle(const uint32_t charIndex) const;
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
index 5362804..9f2384d 100644 (file)
@@ -1420,6 +1420,11 @@ Rect<float> Controller::GetLineBoundingRectangle(const uint32_t lineIndex)
   return GetLineBoundingRect(mImpl->mModel, lineIndex);
 }
 
+Rect<float> Controller::GetCharacterBoundingRectangle(const uint32_t charIndex)
+{
+  return GetCharacterBoundingRect(mImpl->mModel, charIndex);
+}
+
 Rect<> Controller::GetTextBoundingRectangle(CharacterIndex startIndex, CharacterIndex endIndex)
 {
   Vector<Vector2> sizeList;
index 25185ad..511c550 100644 (file)
@@ -1742,6 +1742,15 @@ public: // Queries & retrieves.
   Rect<float> GetLineBoundingRectangle(const uint32_t lineIndex);
 
   /**
+   * @brief Get the char bounding rectangle.
+   * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+   *
+   * @param[in] charIndex character index to which we want to calculate the geometry for.
+   * @return bounding rectangle.
+   */
+  Rect<float> GetCharacterBoundingRectangle(const uint32_t charIndex);
+
+  /**
    * @brief Gets the bounding box of a specific text range.
    *
    * @param[in] startIndex start index of the text requested to get bounding box to.
index ac2e29d..5d5b28b 100644 (file)
@@ -58,6 +58,7 @@ float GetLineHeight(const LineRun lineRun, bool isLastLine);
 
 /**
  * @brief Get the line width for the specified line run.
+ * The width in the geometry = glyph.advance, not glyph.width.
  *
  * @param[in] lineRun The line runs to get the width for.
  * @return the width of the line.
index 09d2c92..01ba584 100644 (file)
@@ -333,6 +333,73 @@ Rect<float> GetLineBoundingRect(ModelPtr textModel, const uint32_t lineIndex)
   return {lineX, lineY, lineWidth, lineHeight};
 }
 
+float GetCharacterLeft(const GlyphInfo& glyph, const Vector2& characterPosition)
+{
+  return characterPosition.x - glyph.xBearing;
+}
+
+float GetCharacterTop(const float& yPosition)
+{
+  return (-1 * yPosition);
+}
+
+float GetCharacterHeight(const GlyphInfo& glyph)
+{
+  return glyph.height;
+}
+
+float GetCharacterWidth(const GlyphInfo& glyph)
+{
+  return glyph.advance;
+}
+
+Rect<> GetCharacterBoundingRect(ModelPtr textModel, const uint32_t charIndex)
+{
+  if(textModel->mVisualModel == nullptr)
+  {
+    return {0, 0, 0, 0};
+  }
+
+  VisualModelPtr&  visualModel  = textModel->mVisualModel;
+  LogicalModelPtr& logicalModel = textModel->mLogicalModel;
+
+  if(charIndex >= logicalModel->mText.Count() || visualModel->mLines.Empty())
+  {
+    return {0, 0, 0, 0};
+  }
+
+  const Vector<Vector2>&   glyphPositions = visualModel->mGlyphPositions;
+  const Vector<GlyphInfo>& glyphs         = visualModel->mGlyphs;
+  const Vector<LineRun>&   lines          = visualModel->mLines;
+
+  //For each character, the index of the first glyph.
+  const GlyphIndex glyphIndex = visualModel->mCharactersToGlyph[charIndex]; //took its glyphs
+
+  const Vector2&   characterPosition = glyphPositions[glyphIndex];
+  const GlyphInfo& glyphInfo    = glyphs[glyphIndex];
+
+  // GetLineOfCharacter function returns 0 if the lines are empty
+  const int      lineIndex = visualModel->GetLineOfCharacter(charIndex);
+  const LineRun& lineRun   = lines[lineIndex];
+
+  //Calculate the left: x position of the glyph + alignmentOffset of the line +  mScrollPosition.x.
+  float characterX = lineRun.alignmentOffset + GetCharacterLeft(glyphInfo, characterPosition) + textModel->mScrollPosition.x;
+
+  //Calculate the Top(characterY): position.Y + previouse lines height + mScrollPosition.y.
+  bool isFirstLine = lineIndex == 0;
+
+  //If the line is the first line of the text; its top = 0.
+  float lineY = (isFirstLine ? 0 : GetLineTop(lines, lineRun));
+
+  float characterY = lineY + GetCharacterTop(characterPosition.y) + textModel->mScrollPosition.y;
+
+  //The rectangle contains the width and height:
+  float characterWidth  = GetCharacterWidth(glyphInfo);
+  float characterHeight = GetCharacterHeight(glyphInfo);
+
+  return {characterX, characterY, characterWidth, characterHeight};
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index c783d9b..6dae02a 100644 (file)
@@ -70,6 +70,52 @@ float GetLineLeft(const LineRun& lineRun);
   */
 float GetLineTop(const Vector<LineRun>& lines, const LineRun& lineRun);
 
+/**
+  * @brief Get the rendered size and position of a specific character in a rectangle.
+  * If the text is not yet rendered or the index > text.Count(); a rect of {0, 0, 0, 0} is returned.
+  * The Width  of the character = glyph.advance
+  * The Height of the character = glyph.height
+  * The Left (X) of the character = characterPosition.x - glyph.xBearing; Regardless of the language direction RTL or LTR, the x point is the left x-point of the glyph. 
+  * The Top  (Y) of the character = characterPosition.y.
+  * @param[in] textModel text model containing text info.
+  * @param[in] charIndex character index to which we want to calculate the geometry for.
+  * @return Rect {x, y, width, height}.
+  */
+Rect<> GetCharacterBoundingRect(ModelPtr textModel, const uint32_t charIndex);
+
+/**
+  * @brief Get the left point of the character (x).
+  *
+  * @param[in] glyph the requested character glyph.
+  * @param[in] characterPosition the position of the requested character.
+  * @return x point of the character.
+  */
+float GetCharacterLeft(const GlyphInfo& glyph, const Vector2& characterPosition);
+
+/**
+  * @brief Get the top point of the character (y).
+  *
+  * @param[in] characterPosition the position of the requested character.
+  * @return y point of the character.
+  */
+float GetCharacterTop(const Vector2& characterPosition);
+
+/**
+  * @brief Get the height of the character.
+  *
+  * @param[in] glyph the requested character glyph.
+  * @return height of the character.
+  */
+float GetCharacterHeight(const GlyphInfo& glyph);
+
+/**
+  * @brief Get the left point of the character.
+  *
+  * @param[in] glyph the requested character glyph.
+  * @return width of the character.
+  */
+float GetCharacterWidth(const GlyphInfo& glyph);
+
 } // namespace Text
 
 } // namespace Toolkit