From e4025736c4d5e85ab7408f9d6932cc7b3e9c9fef Mon Sep 17 00:00:00 2001 From: sarajammal Date: Mon, 21 Nov 2022 19:28:54 +0300 Subject: [PATCH] Add GetCharacterIndexAtPosition API Retrieve the character index based on a given visual X and Y values. Function prototype: int GetCharacterIndexAtPosition(ModelPtr textModel, float visualX, float visualY); Add new API to TextGeometry. Change-Id: Idea36f92053cd475e48e15c3bbdd57c79311f4c3 --- .../src/dali-toolkit/utc-Dali-TextGeometry.cpp | 157 +++++++++++++++++++++ .../devel-api/text/text-geometry-devel.cpp | 15 ++ dali-toolkit/devel-api/text/text-geometry-devel.h | 33 +++++ .../controls/text-controls/text-editor-impl.cpp | 5 + .../controls/text-controls/text-editor-impl.h | 10 ++ .../controls/text-controls/text-field-impl.cpp | 5 + .../controls/text-controls/text-field-impl.h | 10 ++ .../controls/text-controls/text-label-impl.cpp | 5 + .../controls/text-controls/text-label-impl.h | 10 ++ .../internal/text/controller/text-controller.cpp | 5 + .../internal/text/controller/text-controller.h | 10 ++ dali-toolkit/internal/text/text-geometry.cpp | 121 ++++++++++++++++ dali-toolkit/internal/text/text-geometry.h | 11 ++ 13 files changed, 397 insertions(+) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp index 76a8b46..634d85c 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp @@ -526,4 +526,161 @@ int UtcDaliTextGeometryWithVerticalLineAlignmentBottomGetCharacterBoundingRectan TestTextGeometryUtils::CheckRectGeometryResult(charGeometry, expectedCharGeometry); END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionTextLabel(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionTextLabel"); + + 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, "sara سارة"); + + application.SendNotification(); + application.Render(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(label, 1, 1); + + int expectedCharacterIndex = 0; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionTextEditor(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionTextEditor"); + + 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, "Hello everyone."); + + application.SendNotification(); + application.Render(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(editor, 2, 2); + + int expectedCharacterIndex = 0; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionTextField(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionTextField"); + + 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(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(field, 18.6, 6); + + int expectedCharacterIndex = 12; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionTextLabelEmptyText(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionTextLabelEmptyText"); + + 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(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(label, 1, 0); + + int expectedCharacterIndex = -1; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionLastCharacter(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionLastCharacter"); + + 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, "Hello"); + + application.SendNotification(); + application.Render(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(label, 57, 2); + + int expectedCharacterIndex = 4; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliTextGeometryGetCharacterIndexAtPositionXExceedsTextWidth(void) +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTextGeometryGetCharacterIndexAtPositionXExceedsTextWidth"); + + 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, "Hello"); + + application.SendNotification(); + application.Render(); + + int characterIndex = TextGeometry::GetCharacterIndexAtPosition(label, 150, 1); + + int expectedCharacterIndex = -1; + + DALI_TEST_EQUALS(characterIndex, expectedCharacterIndex, TEST_LOCATION); + + END_TEST; } \ No newline at end of file diff --git a/dali-toolkit/devel-api/text/text-geometry-devel.cpp b/dali-toolkit/devel-api/text/text-geometry-devel.cpp index 962484e..aaecc89 100644 --- a/dali-toolkit/devel-api/text/text-geometry-devel.cpp +++ b/dali-toolkit/devel-api/text/text-geometry-devel.cpp @@ -62,6 +62,21 @@ Rect GetCharacterBoundingRectangle(TextField field, const uint32_t charIn return GetImpl(field).GetCharacterBoundingRectangle(charIndex); } +int GetCharacterIndexAtPosition(TextLabel label, float visualX, float visualY) +{ + return GetImpl(label).GetCharacterIndexAtPosition(visualX, visualY); +} + +int GetCharacterIndexAtPosition(TextField field, float visualX, float visualY) +{ + return GetImpl(field).GetCharacterIndexAtPosition(visualX, visualY); +} + +int GetCharacterIndexAtPosition(TextEditor editor, float visualX, float visualY) +{ + return GetImpl(editor).GetCharacterIndexAtPosition(visualX, visualY); +} + } //namespace TextGeometry } // namespace Text diff --git a/dali-toolkit/devel-api/text/text-geometry-devel.h b/dali-toolkit/devel-api/text/text-geometry-devel.h index 014a7ce..8872cfe 100644 --- a/dali-toolkit/devel-api/text/text-geometry-devel.h +++ b/dali-toolkit/devel-api/text/text-geometry-devel.h @@ -92,6 +92,39 @@ namespace TextGeometry */ DALI_TOOLKIT_API Rect GetCharacterBoundingRectangle(TextEditor editor, const uint32_t charIndex); + /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] label text model containing text info. + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + DALI_TOOLKIT_API int GetCharacterIndexAtPosition(TextLabel label, float visualX, float visualY); + + /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] field text model containing text info. + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + DALI_TOOLKIT_API int GetCharacterIndexAtPosition(TextField field, float visualX, float visualY); + + /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] editor text model containing text info. + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + DALI_TOOLKIT_API int GetCharacterIndexAtPosition(TextEditor editor, float visualX, float visualY); + } // namespace TextGeometry diff --git a/dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp index a27f45d..ea5f43c 100644 --- a/dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp @@ -374,6 +374,11 @@ Rect TextEditor::GetCharacterBoundingRectangle(const uint32_t charIndex) return mController->GetCharacterBoundingRectangle(charIndex); } +int TextEditor::GetCharacterIndexAtPosition(float visualX, float visualY) const +{ + return mController->GetCharacterIndexAtPosition(visualX, visualY); +} + void TextEditor::SetSpannedText(const Text::Spanned& spannedText) { mController->SetSpannedText(spannedText); diff --git a/dali-toolkit/internal/controls/text-controls/text-editor-impl.h b/dali-toolkit/internal/controls/text-controls/text-editor-impl.h index a6cd50d..5591439 100644 --- a/dali-toolkit/internal/controls/text-controls/text-editor-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-editor-impl.h @@ -377,6 +377,16 @@ public: Rect GetCharacterBoundingRectangle(const uint32_t charIndex) const; /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + int GetCharacterIndexAtPosition(float visualX, float visualY) const; + + /** * @brief Set the @p spannedText into current textEditor * the spanned text contains content (text) and format (spans with ranges) * the text is copied into text-controller and the spans are applied on ranges diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp index 3d53aa1..e7a8f4b 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp @@ -1215,6 +1215,11 @@ Rect TextField::GetCharacterBoundingRectangle(const uint32_t charIndex) c return mController->GetCharacterBoundingRectangle(charIndex); } +int TextField::GetCharacterIndexAtPosition(float visualX, float visualY) const +{ + return mController->GetCharacterIndexAtPosition(visualX, visualY); +} + void TextField::SetSpannedText(const Text::Spanned& spannedText) { mController->SetSpannedText(spannedText); diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.h b/dali-toolkit/internal/controls/text-controls/text-field-impl.h index c9ef431..43bde7a 100644 --- a/dali-toolkit/internal/controls/text-controls/text-field-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.h @@ -386,6 +386,16 @@ public: Rect GetCharacterBoundingRectangle(const uint32_t charIndex) const; /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + int GetCharacterIndexAtPosition(float visualX, float visualY) const; + + /** * @brief Set the @p spannedText into current textField * the spanned text contains content (text) and format (spans with ranges) * the text is copied into text-controller and the spans are applied on ranges diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp index 570e370..fb41c56 100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp @@ -1246,6 +1246,11 @@ Rect TextLabel::GetCharacterBoundingRectangle(const uint32_t charIndex) c return mController->GetCharacterBoundingRectangle(charIndex); } +int TextLabel::GetCharacterIndexAtPosition(float visualX, float visualY) const +{ + return mController->GetCharacterIndexAtPosition(visualX, visualY); +} + void TextLabel::SetSpannedText(const Text::Spanned& spannedText) { mController->SetSpannedText(spannedText); diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.h b/dali-toolkit/internal/controls/text-controls/text-label-impl.h index edf0a20..6e34534 100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.h +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.h @@ -144,6 +144,16 @@ public: Rect GetCharacterBoundingRectangle(const uint32_t charIndex) const; /** + * @brief Get the character index. + * If the text is not yet rendered or the textis empty, -1 is returned. + * + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + int GetCharacterIndexAtPosition(float visualX, float visualY) const; + + /** * @brief Set the @p spannedText into current textLabel * the spanned text contains content (text) and format (spans with ranges) * the text is copied into text-controller and the spans are applied on ranges diff --git a/dali-toolkit/internal/text/controller/text-controller.cpp b/dali-toolkit/internal/text/controller/text-controller.cpp index 2589266..e8d6337 100644 --- a/dali-toolkit/internal/text/controller/text-controller.cpp +++ b/dali-toolkit/internal/text/controller/text-controller.cpp @@ -1431,6 +1431,11 @@ Rect Controller::GetCharacterBoundingRectangle(const uint32_t charIndex) return GetCharacterBoundingRect(mImpl->mModel, charIndex); } +int Controller::GetCharacterIndexAtPosition(float visualX, float visualY) +{ + return GetCharIndexAtPosition(mImpl->mModel, visualX, visualY); +} + Rect<> Controller::GetTextBoundingRectangle(CharacterIndex startIndex, CharacterIndex endIndex) { Vector sizeList; diff --git a/dali-toolkit/internal/text/controller/text-controller.h b/dali-toolkit/internal/text/controller/text-controller.h index 85ebfaa..27b8491 100644 --- a/dali-toolkit/internal/text/controller/text-controller.h +++ b/dali-toolkit/internal/text/controller/text-controller.h @@ -1761,6 +1761,16 @@ public: // Queries & retrieves. Rect GetCharacterBoundingRectangle(const uint32_t charIndex); /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ + int GetCharacterIndexAtPosition(float visualX, float visualY); + + /** * @brief Gets the bounding box of a specific text range. * * @param[in] startIndex start index of the text requested to get bounding box to. diff --git a/dali-toolkit/internal/text/text-geometry.cpp b/dali-toolkit/internal/text/text-geometry.cpp index 01ba584..8992c6d 100644 --- a/dali-toolkit/internal/text/text-geometry.cpp +++ b/dali-toolkit/internal/text/text-geometry.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace Dali; @@ -400,6 +401,126 @@ Rect<> GetCharacterBoundingRect(ModelPtr textModel, const uint32_t charIndex) return {characterX, characterY, characterWidth, characterHeight}; } +int GetCharIndexAtPosition(ModelPtr textModel, float visualX, float visualY) +{ + if(textModel == nullptr) + { + return -1; + } + + VisualModelPtr& visualModel = textModel->mVisualModel; + + const int totalNumberOfGlyphs = visualModel->mGlyphs.Count(); + const int totalNumberOfLines = visualModel->mLines.Count(); + + if((0 == totalNumberOfGlyphs) || + (0 == totalNumberOfLines)) + { + return -1; + } + + //The top point of the view = 0. + if(visualY < 0) + { + return -1; + } + + const Vector& lines = visualModel->mLines; + + float lineTop = 0.f; + int lineIndex = -1; + int high = totalNumberOfLines; + int low = -1; + int guess; + + // Searching for the correct line. + while(high - low > 1) + { + guess = (high + low) / 2; + Vector::ConstIterator it = lines.Begin() + guess; + + lineTop = GetLineTop(lines, *it); + + if(lineTop > visualY) + { + high = guess; + } + else + { + low = guess; + } + } + + if(low < 0) + { + lineIndex = 0; + } + else + { + lineIndex = low; + } + + bool isLastLine = lineIndex + 1 == totalNumberOfLines; + + if(isLastLine) + { + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex); + float lineHeight = GetLineHeight(line, isLastLine); + + // If the visualY is placed after the last line. + if(visualY > lineTop + lineHeight) + { + return -1; + } + } + + // Check if a line is not found; return -1. + if(lineIndex == -1) + { + return -1; + } + + // Start searching for the visualX + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex); + + visualX -= line.alignmentOffset; + + // Positions of the glyphs + const Vector2* const positionsBuffer = visualModel->mGlyphPositions.Begin(); + + const CharacterIndex startCharacter = line.characterRun.characterIndex; + const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1; + + CharacterIndex characterIndexAtPosition = -1; + CharacterIndex characterIndex = startCharacter; + float characterPosition; + float rightMostCharacterPosition; + + for(; characterIndex != endCharacter; characterIndex++) + { + characterPosition = positionsBuffer[characterIndex].x; + rightMostCharacterPosition = positionsBuffer[characterIndex+1].x; + + if(visualX < rightMostCharacterPosition && visualX >= characterPosition) + { + characterIndexAtPosition = characterIndex; + break; + } + } + + if(characterIndex == endCharacter) + { + // If the visualX is within the last character position or it comes after the last character; we return the last character. + rightMostCharacterPosition = positionsBuffer[endCharacter].x + GetCharacterWidth(visualModel->mGlyphs[endCharacter]); + + if(visualX >= positionsBuffer[endCharacter].x && visualX < rightMostCharacterPosition) + { + characterIndexAtPosition = endCharacter; + } + } + + return characterIndexAtPosition; +} } // namespace Text } // namespace Toolkit diff --git a/dali-toolkit/internal/text/text-geometry.h b/dali-toolkit/internal/text/text-geometry.h index 6dae02a..1d410bf 100644 --- a/dali-toolkit/internal/text/text-geometry.h +++ b/dali-toolkit/internal/text/text-geometry.h @@ -84,6 +84,17 @@ float GetLineTop(const Vector& lines, const LineRun& lineRun); Rect<> GetCharacterBoundingRect(ModelPtr textModel, const uint32_t charIndex); /** + * @brief Get the character index. + * If the text is not yet rendered or the text is empty, -1 is returned. + * + * @param[in] textModel text model containing text info. + * @param[in] visualX visual x position. + * @param[in] visualY visual y position. + * @return character index. + */ +int GetCharIndexAtPosition(ModelPtr textModel, float visualX, float visualY); + +/** * @brief Get the left point of the character (x). * * @param[in] glyph the requested character glyph. -- 2.7.4