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
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
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
*/
DALI_TOOLKIT_API Rect<float> 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
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);
Rect<float> 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
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);
Rect<float> 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
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);
Rect<float> 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
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<Vector2> sizeList;
Rect<float> 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.
#include <dali-toolkit/internal/text/cursor-helper-functions.h>
#include <dali-toolkit/internal/text/line-run.h>
#include <dali-toolkit/internal/text/visual-model-impl.h>
+#include <algorithm>
using namespace Dali;
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<LineRun>& 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<LineRun>::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
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.