Handle Emoji clustering for cursor handling
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / cursor-helper-functions.cpp
index 2379bcc..d895418 100644 (file)
@@ -22,6 +22,8 @@
 #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
@@ -153,9 +155,10 @@ LineIndex GetClosestLine(VisualModelPtr visualModel,
       it != endIt;
       ++it, ++lineIndex)
   {
-    const LineRun& lineRun = *it;
+    const LineRun& lineRun    = *it;
+    bool           isLastLine = (it + 1 == endIt);
 
-    totalHeight += GetLineHeight(lineRun);
+    totalHeight += GetLineHeight(lineRun, isLastLine);
 
     if(visualY < totalHeight)
     {
@@ -182,9 +185,10 @@ float CalculateLineOffset(const Vector<LineRun>& lines,
       it != endIt;
       ++it)
   {
-    const LineRun& lineRun = *it;
+    const LineRun& lineRun    = *it;
+    bool           isLastLine = (it + 1 == lines.End());
 
-    offset += GetLineHeight(lineRun);
+    offset += GetLineHeight(lineRun, isLastLine);
   }
 
   return offset;
@@ -212,6 +216,7 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
   {
     return logicalIndex;
   }
+  const float characterSpacing = visualModel->GetCharacterSpacing();
 
   // Whether there is a hit on a line.
   bool matchedLine = false;
@@ -262,8 +267,11 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
   bool isBeforeFirstGlyph = false;
 
   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
-  CharacterIndex visualIndex              = startCharacter;
-  Length         numberOfVisualCharacters = 0;
+  CharacterIndex          visualIndex               = startCharacter;
+  Length                  numberOfVisualCharacters  = 0;
+  float                   calculatedAdvance         = 0.f;
+  Vector<CharacterIndex>& glyphToCharacterMap       = visualModel->mGlyphsToCharacters;
+  const CharacterIndex*   glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
   for(; visualIndex < endCharacter; ++visualIndex)
   {
     // The character in logical order.
@@ -283,11 +291,13 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
 
       // Get the metrics for the group of glyphs.
       GlyphMetrics glyphMetrics;
+      calculatedAdvance = GetCalculatedAdvance(*(logicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + firstLogicalGlyphIndex))), characterSpacing, (*(visualModel->mGlyphs.Begin() + firstLogicalGlyphIndex)).advance);
       GetGlyphsMetrics(firstLogicalGlyphIndex,
                        numberOfGlyphs,
                        glyphMetrics,
                        glyphInfoBuffer,
-                       metrics);
+                       metrics,
+                       calculatedAdvance);
 
       // Get the position of the first glyph.
       const Vector2& position = *(positionsBuffer + firstLogicalGlyphIndex);
@@ -450,6 +460,19 @@ CharacterIndex GetClosestCursorIndex(VisualModelPtr         visualModel,
 
   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 &#x1F468;&#x200D;&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;"
+  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");
@@ -480,14 +503,11 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
   const LineIndex lineIndex = parameters.visualModel->GetLineOfCharacter(characterOfLine);
   const LineRun&  line      = *(modelLines + lineIndex);
 
-  const GlyphIndex* const charactersToGlyphBuffer  = parameters.visualModel->mCharactersToGlyph.Begin();
-  const Length* const     glyphsPerCharacterBuffer = parameters.visualModel->mGlyphsPerCharacter.Begin();
-  const GlyphInfo* const  glyphInfoBuffer          = parameters.visualModel->mGlyphs.Begin();
-  CharacterIndex          index;
-  GlyphMetrics            glyphMetrics;
-  MetricsPtr&             metrics        = parameters.metrics;
-  GlyphIndex              glyphIndex     = 0u;
-  Length                  numberOfGlyphs = 0u;
+  CharacterIndex index;
+  GlyphMetrics   glyphMetrics;
+  MetricsPtr&    metrics        = parameters.metrics;
+  GlyphIndex     glyphIndex     = 0u;
+  Length         numberOfGlyphs = 0u;
 
   if(isLastNewParagraph)
   {
@@ -501,7 +521,9 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 newLineIndex);
 
-    cursorInfo.lineHeight = GetLineHeight(newLine);
+    // The line height is the addition of the line ascender and the line descender.
+    // However, the line descender has a negative value, hence the subtraction also line spacing should not be included in cursor height.
+    cursorInfo.lineHeight = newLine.ascender - newLine.descender;
 
     index                                = 0u;
     const Length totalNumberOfCharacters = parameters.logicalModel->mText.Count();
@@ -510,7 +532,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
       index = totalNumberOfCharacters - 1u;
     }
 
-    GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+    GetGlyphMetricsFromCharacterIndex(index, parameters.visualModel, parameters.logicalModel, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
 
     // Set the primary cursor's height.
     // The primary cursor height will take the font height of the last character and if there are no characters, it'll take the default font line height.
@@ -557,7 +579,9 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 lineIndex);
 
-    cursorInfo.lineHeight = GetLineHeight(line);
+    // The line height is the addition of the line ascender and the line descender.
+    // However, the line descender has a negative value, hence the subtraction also line spacing should not be included in cursor height.
+    cursorInfo.lineHeight = line.ascender - line.descender;
 
     // Calculate the primary cursor.
 
@@ -597,8 +621,12 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     const Length* const         charactersPerGlyphBuffer = parameters.visualModel->mCharactersPerGlyph.Begin();
     const CharacterIndex* const glyphsToCharactersBuffer = parameters.visualModel->mGlyphsToCharacters.Begin();
     const Vector2* const        glyphPositionsBuffer     = parameters.visualModel->mGlyphPositions.Begin();
+    const float                 characterSpacing         = parameters.visualModel->GetCharacterSpacing();
 
-    GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+    // Get the metrics for the group of glyphs.
+    GetGlyphMetricsFromCharacterIndex(index, parameters.visualModel, parameters.logicalModel, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+
+    // Convert the cursor position into the glyph position.
     const GlyphIndex primaryGlyphIndex         = glyphIndex;
     const Length     primaryNumberOfCharacters = *(charactersPerGlyphBuffer + primaryGlyphIndex);
 
@@ -636,7 +664,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
                                   (isFirstPositionOfLine && isRightToLeftParagraph) ||
                                   (!isFirstPositionOfLine && !isLastPosition && !isCurrentRightToLeft));
 
-    float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
+    float glyphAdvance = addGlyphAdvance ? (glyphMetrics.advance) : 0.f;
 
     if(!isLastPositionOfLine &&
        (primaryNumberOfCharacters > 1u))
@@ -655,7 +683,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
         numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
       }
 
-      glyphAdvance = static_cast<float>(numberOfGlyphAdvance) * glyphMetrics.advance / static_cast<float>(primaryNumberOfCharacters);
+      glyphAdvance = static_cast<float>(numberOfGlyphAdvance) * (glyphMetrics.advance) / static_cast<float>(primaryNumberOfCharacters);
     }
 
     // Get the glyph position and x bearing (in the line's coords).
@@ -684,7 +712,8 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
         index = (isRightToLeftParagraph == isCurrentRightToLeft) ? nextCharacterIndex : characterIndex;
       }
 
-      GetGlyphMetricsFromCharacterIndex(index, glyphInfoBuffer, charactersToGlyphBuffer, glyphsPerCharacterBuffer, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+      GetGlyphMetricsFromCharacterIndex(index, parameters.visualModel, parameters.logicalModel, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+
       const GlyphIndex secondaryGlyphIndex = glyphIndex;
       const Vector2&   secondaryPosition   = *(glyphPositionsBuffer + secondaryGlyphIndex);
 
@@ -709,7 +738,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
       const bool addGlyphAdvance = ((!isFirstPositionOfLine && !isCurrentRightToLeft) ||
                                     (isFirstPositionOfLine && !isRightToLeftParagraph));
 
-      cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + (addGlyphAdvance ? glyphMetrics.advance : 0.f);
+      cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + (addGlyphAdvance ? (glyphMetrics.advance + characterSpacing) : 0.f);
       cursorInfo.secondaryPosition.y = cursorInfo.lineOffset + cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight;
 
       // Transform the cursor info from line's coords to text's coords.