/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
#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>
+#include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
namespace
{
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)
{
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;
return logicalIndex;
}
+ // Get the character-spacing runs.
+ const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = visualModel->GetCharacterSpacingGlyphRuns();
+ const float modelCharacterSpacing = visualModel->GetCharacterSpacing();
+
// Whether there is a hit on a line.
bool matchedLine = false;
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.
// Get the metrics for the group of glyphs.
GlyphMetrics glyphMetrics;
+ const float characterSpacing = GetGlyphCharacterSpacing(firstLogicalGlyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ 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);
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");
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)
{
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();
- index = totalNumberOfCharacters - 1;
+ if(totalNumberOfCharacters > 0u)
+ {
+ 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.
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.
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 modelCharacterSpacing = parameters.visualModel->GetCharacterSpacing();
+
+ const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.visualModel->GetCharacterSpacingGlyphRuns();
- 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);
(isFirstPositionOfLine && isRightToLeftParagraph) ||
(!isFirstPositionOfLine && !isLastPosition && !isCurrentRightToLeft));
- float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
+ float glyphAdvance = addGlyphAdvance ? (glyphMetrics.advance) : 0.f;
if(!isLastPositionOfLine &&
(primaryNumberOfCharacters > 1u))
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).
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);
const bool addGlyphAdvance = ((!isFirstPositionOfLine && !isCurrentRightToLeft) ||
(isFirstPositionOfLine && !isRightToLeftParagraph));
- cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + (addGlyphAdvance ? glyphMetrics.advance : 0.f);
+ const float characterSpacing = GetGlyphCharacterSpacing(secondaryGlyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ 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.