utc-Dali-ItemView-internal.cpp
utc-Dali-LogicalModel.cpp
utc-Dali-PropertyHelper.cpp
+ utc-Dali-Text-Characters.cpp
utc-Dali-Text-CharacterSetConversion.cpp
utc-Dali-Text-Circular.cpp
utc-Dali-Text-Controller.cpp
--- /dev/null
+/*
+ * Copyright (c) 2022 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 <stdlib.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/internal/text/characters-helper-functions.h>
+#include <toolkit-text-utils.h>
+
+using namespace Dali;
+using namespace Toolkit;
+using namespace Text;
+
+// Tests the following function.
+//
+// CharacterRun RetrieveClusteredCharactersOfCharacterIndex(const VisualModelPtr& visualModel,
+// const LogicalModelPtr& logicalModel,
+// const CharacterIndex& characterIndex)
+
+//////////////////////////////////////////////////////////
+
+namespace
+{
+const std::string DEFAULT_FONT_DIR("/resources/fonts");
+const unsigned int DEFAULT_FONT_SIZE = 1152u;
+
+struct RetrieveClusteredCharactersOfCharacterIndexData
+{
+ std::string description; ///< Description of the test.
+ std::string text; ///< Input text.
+ bool markupProcessorEnabled; //< Enable markup processor to use markup text.
+ unsigned int numberOfTests; ///< The number of tests.
+ CharacterIndex* characterIndex; ///< The character index for each test.
+ CharacterRun* clusteredCharacters; ///< The expected clustered characters run for each test.
+};
+
+bool GetRetrieveClusteredCharactersOfCharacterIndexTest(const RetrieveClusteredCharactersOfCharacterIndexData& data)
+{
+ std::cout << " testing : " << data.description << std::endl;
+
+ // 1) Create the model.
+ ModelPtr textModel;
+ MetricsPtr metrics;
+ Size textArea(400.f, 600.f);
+ Size layoutSize;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ LayoutOptions options;
+ CreateTextModel(data.text,
+ textArea,
+ fontDescriptionRuns,
+ options,
+ layoutSize,
+ textModel,
+ metrics,
+ data.markupProcessorEnabled,
+ LineWrap::WORD,
+ false,
+ Toolkit::DevelText::EllipsisPosition::END,
+ 0.0f, // lineSpacing
+ 0.0f // characterSpacing
+ );
+
+ LogicalModelPtr logicalModel = textModel->mLogicalModel;
+ VisualModelPtr visualModel = textModel->mVisualModel;
+
+ for(unsigned int index = 0; index < data.numberOfTests; ++index)
+ {
+ CharacterRun clusteredCharacters = RetrieveClusteredCharactersOfCharacterIndex(visualModel, logicalModel, data.characterIndex[index]);
+
+ if(clusteredCharacters.characterIndex != data.clusteredCharacters[index].characterIndex)
+ {
+ std::cout << " test " << index << " failed. Different clusteredCharacters.characterIndex : " << clusteredCharacters.characterIndex << ", expected : " << data.clusteredCharacters[index].characterIndex << std::endl;
+ return false;
+ }
+
+ if(clusteredCharacters.numberOfCharacters != data.clusteredCharacters[index].numberOfCharacters)
+ {
+ std::cout << " test " << index << " failed. Different clusteredCharacters.numberOfCharacters : " << clusteredCharacters.numberOfCharacters << ", expected : " << data.clusteredCharacters[index].numberOfCharacters << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+//////////////////////////////////////////////////////////
+//
+// UtcDaliRetrieveClusteredCharactersOfCharacterIndex
+//
+//////////////////////////////////////////////////////////
+
+int UtcDaliRetrieveClusteredCharactersOfCharacterIndex(void)
+{
+ tet_infoline(" UtcDaliRetrieveClusteredCharactersOfCharacterIndex");
+
+ CharacterIndex characterIndex01[] = {0u, 1u, 2u, 10u};
+
+ CharacterRun clusteredCharacters01[] = {
+ {0u, 1u},
+ {1u, 1u},
+ {2u, 1u},
+ {10u, 1u}};
+
+ CharacterIndex characterIndex02[] = {0u, 4u, 6u};
+
+ CharacterRun clusteredCharacters02[] = {
+ {0u, 7u},
+ {0u, 7u},
+ {0u, 7u}};
+
+ CharacterIndex characterIndex03[] = {3u, 9u, 14u};
+
+ CharacterRun clusteredCharacters03[] = {
+ {2u, 7u},
+ {9u, 1u},
+ {11u, 4u}};
+
+ CharacterIndex characterIndex04[] = {0u, 1u, 2u, 10u};
+
+ CharacterRun clusteredCharacters04[] = {
+ {0u, 1u},
+ {1u, 1u},
+ {2u, 1u},
+ {10u, 1u}};
+
+ struct RetrieveClusteredCharactersOfCharacterIndexData data[] =
+ {
+ {"Easy latin script",
+ "Hello world",
+ true,
+ 4u,
+ characterIndex01,
+ clusteredCharacters01},
+
+ {"FamilyManWomanGirlBoy Single Complex Emoji script",
+ "👨‍👩‍👧‍👦",
+ true,
+ 3u,
+ characterIndex02,
+ clusteredCharacters02},
+
+ {"Long text many Emojis with letters",
+ "AB👨‍👩‍👧‍👦AB👩🏻‍🔬B👨‍👩‍👧‍👦AA☪︎B☪️AB",
+ true,
+ 3u,
+ characterIndex03,
+ clusteredCharacters03},
+
+ {"Arabic script",
+ "اهلا و سهلا",
+ true,
+ 4u,
+ characterIndex04,
+ clusteredCharacters04},
+ };
+ const unsigned int numberOfTests = 4u;
+
+ for(unsigned int index = 0; index < numberOfTests; ++index)
+ {
+ ToolkitTestApplication application;
+ if(!GetRetrieveClusteredCharactersOfCharacterIndexTest(data[index]))
+ {
+ tet_result(TET_FAIL);
+ }
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
struct GetClosestCursorIndexData
{
- std::string description; ///< Description of the test.
- std::string text; ///< Input text.
- unsigned int numberOfTests; ///< The number of tests.
- float* visualX; ///< The visual 'x' position for each test.
- float* visualY; ///< The visual 'y' position for each test.
- CharacterHitTest::Mode* mode; ///< The type of hit test.
- CharacterIndex* logicalIndex; ///< The expected logical cursor index for each test.
- bool* isCharacterHit; ///< The expected character hit value for each test.
+ std::string description; ///< Description of the test.
+ std::string text; ///< Input text.
+ unsigned int numberOfTests; ///< The number of tests.
+ float* visualX; ///< The visual 'x' position for each test.
+ float* visualY; ///< The visual 'y' position for each test.
+ CharacterHitTest::Mode* mode; ///< The type of hit test.
+ bool markupProcessorEnabled; //< Enable markup processor to use markup text.
+ CharacterIndex* logicalIndex; ///< The expected logical cursor index for each test.
+ bool* isCharacterHit; ///< The expected character hit value for each test.
};
struct GetCursorPositionData
layoutSize,
textModel,
metrics,
- false,
+ data.markupProcessorEnabled,
LineWrap::WORD,
false,
Toolkit::DevelText::EllipsisPosition::END,
CharacterIndex logicalIndex08[] = {1u};
bool isCharacterHit08[] = {true};
+ float visualX09[] = {9.f};
+ float visualY09[] = {12.f};
+ CharacterHitTest::Mode mode09[] = {CharacterHitTest::TAP};
+ CharacterIndex logicalIndex09[] = {1u};
+ bool isCharacterHit09[] = {true};
+
struct GetClosestCursorIndexData data[] =
{
{"Void text.",
visualX01,
visualY01,
mode01,
+ false,
logicalIndex01,
isCharacterHit01},
+
{"Single line text.",
"Hello world שלום עולם",
7u,
visualX02,
visualY02,
mode02,
+ false,
logicalIndex02,
isCharacterHit02},
+
{"Single line with ligatures",
"different الأربعاء",
4u,
visualX03,
visualY03,
mode03,
+ false,
logicalIndex03,
isCharacterHit03},
+
{"Multiline. Single line paragraphs",
"Hello world\n"
"שלום עולם\n"
visualX04,
visualY04,
mode04,
+ false,
logicalIndex04,
isCharacterHit04},
+
{"Multiline. Single bidirectional paragraph, starts LTR, wrapped lines",
"abcשנבdefגקכghiעיןjklחלךmnoצמםpqrפרףstuדאוvwxה"
"סתyzטזץabcשנבdefגקכghiעיןjklחלךmnoצמםpqrפרףstuד"
visualX05,
visualY05,
mode05,
+ false,
logicalIndex05,
isCharacterHit05},
+
{"Multiline. Single bidirectional paragraph, starts RTL, wrapped lines",
"שנבabcגקכdefעיןghiחלךjklצמםmnoפרףpqrדאוstuהסתv"
"wxטזץyzשנבabcגקכdefעיןghiחלךjklצמםmnoפרףpqrדאוs"
visualX06,
visualY06,
mode06,
+ false,
logicalIndex06,
isCharacterHit06},
+
{"Testing complex characters. Arabic ligatures",
"الأَبْجَدِيَّة العَرَبِيَّة",
1u,
visualX07,
visualY07,
mode07,
+ false,
logicalIndex07,
isCharacterHit07},
+
{"Testing complex characters. Latin ligatures",
"fi ligature",
1u,
visualX08,
visualY08,
mode08,
+ false,
logicalIndex08,
- isCharacterHit08}};
- const unsigned int numberOfTests = 8u;
+ isCharacterHit08},
+
+ {"Testing complex characters. Emoji",
+ "A👨‍👩‍👧‍👦B",
+ 1u,
+ visualX09,
+ visualY09,
+ mode09,
+ true,
+ logicalIndex09,
+ isCharacterHit09}
+
+ };
+ const unsigned int numberOfTests = 9u;
for(unsigned int index = 0; index < numberOfTests; ++index)
{
application.Render();
END_TEST;
+}
+
+//Handle Emoji clustering for cursor handling
+int utcDaliTextEditorClusteredEmojiDeletionBackSpaceKey(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" utcDaliTextEditorClusteredEmojiDeletionBackSpaceKey ");
+ TextEditor textEditor = TextEditor::New();
+ DALI_TEST_CHECK(textEditor);
+
+ application.GetScene().Add(textEditor);
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ textEditor.SetProperty(TextEditor::Property::TEXT, "ABC👨‍👩‍👧‍👦XY");
+ textEditor.SetProperty(Dali::Toolkit::TextEditor::Property::ENABLE_MARKUP, true);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ // Set currsor
+ textEditor.SetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 10);
+ application.SendNotification();
+ application.Render();
+
+ // Set focus and remove Emoji
+ textEditor.SetKeyInputFocus();
+ application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_BACKSPACE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+ //Check the changed text and cursor position
+ DALI_TEST_EQUALS(textEditor.GetProperty(TextEditor::Property::TEXT).Get<std::string>(), "ABCXY", TEST_LOCATION);
+ DALI_TEST_EQUALS(textEditor.GetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 3, TEST_LOCATION);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int utcDaliTextEditorClusteredEmojiDeletionDeleteKey(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" utcDaliTextEditorClusteredEmojiDeletionDeleteKey ");
+ TextEditor textEditor = TextEditor::New();
+ DALI_TEST_CHECK(textEditor);
+
+ application.GetScene().Add(textEditor);
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ textEditor.SetProperty(TextEditor::Property::TEXT, "ABC👨‍👩‍👧‍👦XY");
+ textEditor.SetProperty(Dali::Toolkit::TextEditor::Property::ENABLE_MARKUP, true);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ // Set currsor
+ textEditor.SetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 3);
+ application.SendNotification();
+ application.Render();
+
+ // Set focus and remove Emoji
+ textEditor.SetKeyInputFocus();
+ application.ProcessEvent(GenerateKey("", "", "", Dali::DevelKey::DALI_KEY_DELETE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+ //Check the changed text and cursor position
+ DALI_TEST_EQUALS(textEditor.GetProperty(TextEditor::Property::TEXT).Get<std::string>(), "ABCXY", TEST_LOCATION);
+ DALI_TEST_EQUALS(textEditor.GetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 3, TEST_LOCATION);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
}
\ No newline at end of file
application.Render();
END_TEST;
+}
+
+//Handle Emoji clustering for cursor handling
+int utcDaliTextFieldClusteredEmojiDeletionBackSpaceKey(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" utcDaliTextFieldClusteredEmojiDeletionBackSpaceKey ");
+ TextField textField = TextField::New();
+ DALI_TEST_CHECK(textField);
+
+ application.GetScene().Add(textField);
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ textField.SetProperty(TextField::Property::TEXT, "ABC👨‍👩‍👧‍👦XY");
+ textField.SetProperty(Dali::Toolkit::TextField::Property::ENABLE_MARKUP, true);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ // Set currsor
+ textField.SetProperty(DevelTextField::Property::PRIMARY_CURSOR_POSITION, 10);
+ application.SendNotification();
+ application.Render();
+
+ // Set focus and remove Emoji
+ textField.SetKeyInputFocus();
+ application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_BACKSPACE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+ //Check the changed text and cursor position
+ DALI_TEST_EQUALS(textField.GetProperty(TextField::Property::TEXT).Get<std::string>(), "ABCXY", TEST_LOCATION);
+ DALI_TEST_EQUALS(textField.GetProperty(DevelTextField::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 3, TEST_LOCATION);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int utcDaliTextFieldClusteredEmojiDeletionDeleteKey(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" utcDaliTextFieldClusteredEmojiDeletionDeleteKey ");
+ TextField textField = TextField::New();
+ DALI_TEST_CHECK(textField);
+
+ application.GetScene().Add(textField);
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ textField.SetProperty(TextField::Property::TEXT, "ABC👨‍👩‍👧‍👦XY");
+ textField.SetProperty(Dali::Toolkit::TextField::Property::ENABLE_MARKUP, true);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ // Set currsor
+ textField.SetProperty(DevelTextField::Property::PRIMARY_CURSOR_POSITION, 3);
+ application.SendNotification();
+ application.Render();
+
+ // Set focus and remove Emoji
+ textField.SetKeyInputFocus();
+ application.ProcessEvent(GenerateKey("", "", "", Dali::DevelKey::DALI_KEY_DELETE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+ //Check the changed text and cursor position
+ DALI_TEST_EQUALS(textField.GetProperty(TextField::Property::TEXT).Get<std::string>(), "ABCXY", TEST_LOCATION);
+ DALI_TEST_EQUALS(textField.GetProperty(DevelTextField::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 3, TEST_LOCATION);
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
}
\ No newline at end of file
${toolkit_src_dir}/text/bidirectional-support.cpp
${toolkit_src_dir}/text/bounded-paragraph-helper-functions.cpp
${toolkit_src_dir}/text/character-set-conversion.cpp
+ ${toolkit_src_dir}/text/characters-helper-functions.cpp
${toolkit_src_dir}/text/color-segmentation.cpp
${toolkit_src_dir}/text/cursor-helper-functions.cpp
${toolkit_src_dir}/text/glyph-metrics-helper.cpp
--- /dev/null
+// FILE HEADER
+#include <dali-toolkit/internal/text/characters-helper-functions.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/emoji-helper.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+CharacterRun RetrieveClusteredCharactersOfCharacterIndex(const VisualModelPtr& visualModel,
+ const LogicalModelPtr& logicalModel,
+ const CharacterIndex& characterIndex)
+{
+ // Initialization
+ CharacterRun clusteredCharacters;
+ clusteredCharacters.characterIndex = characterIndex;
+ clusteredCharacters.numberOfCharacters = 1u;
+
+ const GlyphIndex* const charactersToGlyphBuffer = visualModel->mCharactersToGlyph.Begin();
+ const Length* const charactersPerGlyphBuffer = visualModel->mCharactersPerGlyph.Begin();
+ const CharacterIndex* const glyphsToCharacters = visualModel->mGlyphsToCharacters.Begin();
+
+ GlyphIndex glyphIndex = *(charactersToGlyphBuffer + characterIndex);
+ Length actualNumberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
+
+ if(actualNumberOfCharacters > 1u)
+ {
+ const Script script = logicalModel->GetScript(characterIndex);
+ // Prevents to break the Latin ligatures like fi, ff, or Arabic ﻻ, ...
+ // Keep actual index of character as is. Because these characters cannot be clustered.
+
+ if(!HasLigatureMustBreak(script))
+ {
+ clusteredCharacters.numberOfCharacters = actualNumberOfCharacters;
+ clusteredCharacters.characterIndex = *(glyphsToCharacters + glyphIndex); // firstCharacterIndex
+ }
+ }
+ else
+ {
+ while(0u == actualNumberOfCharacters)
+ {
+ ++glyphIndex;
+ actualNumberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
+ }
+
+ clusteredCharacters.characterIndex = *(glyphsToCharacters + glyphIndex); // firstCharacterIndex
+ clusteredCharacters.numberOfCharacters = actualNumberOfCharacters;
+ }
+
+ return clusteredCharacters;
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_TEXT_CHARACTERS_HELPER_FUNCTIONS_H
+#define DALI_TOOLKIT_TEXT_CHARACTERS_HELPER_FUNCTIONS_H
+
+/*
+ * Copyright (c) 2022 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/character-run.h>
+#include <dali-toolkit/internal/text/text-definitions.h>
+#include <dali-toolkit/internal/text/text-model.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief Retrieve the clustered consecutive characters that contains current character index.
+ * Found the first index of clustered characters and number of characters.
+ *
+ * @param[in] visualModel The visual model.
+ * @param[in] logicalModel The logical model.
+ * @param[in] characterIndex The character index.
+ *
+ * @return CharacterRun for the clustered characters contains character Index
+ */
+CharacterRun RetrieveClusteredCharactersOfCharacterIndex(const VisualModelPtr& visualModel,
+ const LogicalModelPtr& logicalModel,
+ const CharacterIndex& characterIndex);
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_CHARACTERS_HELPER_FUNCTIONS_H
\ No newline at end of file
#include <dali/integration-api/debug.h>
// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/characters-helper-functions.h>
+#include <dali-toolkit/internal/text/emoji-helper.h>
#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
namespace
logicalIndex = (bidiLineFetched ? logicalModel->GetLogicalCursorIndex(visualIndex) : visualIndex);
+ // Handle Emoji clustering for cursor handling:
+ // Fixing this case:
+ // - When there is Emoji contains multi unicodes and it is layoutted at the end of line (LineWrap case , is not new line case)
+ // - Try to click at the center or at the end of Emoji then the cursor appears inside Emoji
+ // - Example:"FamilyManWomanGirlBoy 👨‍👩‍👧‍👦"
+ const Script script = logicalModel->GetScript(logicalIndex);
+ if(IsOneOfEmojiScripts(script))
+ {
+ //TODO: Use this clustering for Emoji cases only. This needs more testing to generalize to all scripts.
+ CharacterRun emojiClusteredCharacters = RetrieveClusteredCharactersOfCharacterIndex(visualModel, logicalModel, logicalIndex);
+ logicalIndex = emojiClusteredCharacters.characterIndex;
+ }
+
DALI_LOG_INFO(gLogFilter, Debug::Verbose, "closest visualIndex %d logicalIndex %d\n", visualIndex, logicalIndex);
DALI_ASSERT_DEBUG((logicalIndex <= logicalModel->mText.Count() && logicalIndex >= 0) && "GetClosestCursorIndex - Out of bounds index");
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/character-set-conversion.h>
+#include <dali-toolkit/internal/text/characters-helper-functions.h>
+#include <dali-toolkit/internal/text/emoji-helper.h>
#include <dali-toolkit/internal/text/markup-processor.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
ModelPtr& model = impl.mModel;
LogicalModelPtr& logicalModel = model->mLogicalModel;
+ VisualModelPtr& visualModel = model->mVisualModel;
DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n", &controller, logicalModel->mText.Count(), eventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters);
cursorIndex = eventData->mPrimaryCursorPosition + cursorOffset;
}
+ //Handle Emoji clustering for cursor handling
+ // Deletion case: this is handling the deletion cases when the cursor is before or after Emoji
+ // - Before: when use delete key and cursor is before Emoji (cursorOffset = -1)
+ // - After: when use backspace key and cursor is after Emoji (cursorOffset = 0)
+
+ const Script script = logicalModel->GetScript(cursorIndex);
+ if((numberOfCharacters == 1u) &&
+ (IsOneOfEmojiScripts(script)))
+ {
+ //TODO: Use this clustering for Emoji cases only. This needs more testing to generalize to all scripts.
+ CharacterRun emojiClusteredCharacters = RetrieveClusteredCharactersOfCharacterIndex(visualModel, logicalModel, cursorIndex);
+ Length actualNumberOfCharacters = emojiClusteredCharacters.numberOfCharacters;
+
+ //Set cursorIndex at the first characterIndex of clustred Emoji
+ cursorIndex = emojiClusteredCharacters.characterIndex;
+
+ numberOfCharacters = actualNumberOfCharacters;
+ }
+
if((cursorIndex + numberOfCharacters) > currentText.Count())
{
numberOfCharacters = currentText.Count() - cursorIndex;