/*
- * 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);
- // 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.
- totalHeight += lineRun.ascender - lineRun.descender;
+ totalHeight += GetLineHeight(lineRun, isLastLine);
if(visualY < totalHeight)
{
it != endIt;
++it)
{
- const LineRun& lineRun = *it;
+ const LineRun& lineRun = *it;
+ bool isLastLine = (it + 1 == lines.End());
- // 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.
- offset += lineRun.ascender - lineRun.descender;
+ 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");
}
void GetCursorPosition(GetCursorPositionParameters& parameters,
+ float defaultFontLineHeight,
CursorInfo& cursorInfo)
{
const LineRun* const modelLines = parameters.visualModel->mLines.Begin();
const LineIndex lineIndex = parameters.visualModel->GetLineOfCharacter(characterOfLine);
const LineRun& line = *(modelLines + lineIndex);
+ CharacterIndex index;
+ GlyphMetrics glyphMetrics;
+ MetricsPtr& metrics = parameters.metrics;
+ GlyphIndex glyphIndex = 0u;
+ Length numberOfGlyphs = 0u;
+
if(isLastNewParagraph)
{
// The cursor is in a new line with no characters. Place the cursor in that line.
newLineIndex);
// 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.
+ // 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();
+ if(totalNumberOfCharacters > 0u)
+ {
+ index = totalNumberOfCharacters - 1u;
+ }
+
+ GetGlyphMetricsFromCharacterIndex(index, parameters.visualModel, parameters.logicalModel, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
+
// Set the primary cursor's height.
- cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
+ // 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.primaryCursorHeight = (totalNumberOfCharacters > 0) ? (cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight) : defaultFontLineHeight;
// Set the primary cursor's position.
cursorInfo.primaryPosition.x = (LTR == line.direction) ? newLine.alignmentOffset : parameters.visualModel->mControlSize.width - newLine.alignmentOffset;
lineIndex);
// 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.
+ // 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.
- CharacterIndex index = characterIndex;
+ index = characterIndex;
if(cursorInfo.isSecondaryCursor)
{
// If there is a secondary position, the primary cursor may be in a different place than the logical index.
}
}
- const GlyphIndex* const charactersToGlyphBuffer = parameters.visualModel->mCharactersToGlyph.Begin();
- const Length* const glyphsPerCharacterBuffer = parameters.visualModel->mGlyphsPerCharacter.Begin();
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 GlyphInfo* const glyphInfoBuffer = parameters.visualModel->mGlyphs.Begin();
+ const float modelCharacterSpacing = parameters.visualModel->GetCharacterSpacing();
- // Convert the cursor position into the glyph position.
- const GlyphIndex primaryGlyphIndex = *(charactersToGlyphBuffer + index);
- const Length primaryNumberOfGlyphs = *(glyphsPerCharacterBuffer + index);
- const Length primaryNumberOfCharacters = *(charactersPerGlyphBuffer + primaryGlyphIndex);
+ const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.visualModel->GetCharacterSpacingGlyphRuns();
// Get the metrics for the group of glyphs.
- GlyphMetrics glyphMetrics;
- GetGlyphsMetrics(primaryGlyphIndex,
- primaryNumberOfGlyphs,
- glyphMetrics,
- glyphInfoBuffer,
- parameters.metrics);
+ 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);
// Whether to add the glyph's advance to the cursor position.
// i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
(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;
}
- const GlyphIndex secondaryGlyphIndex = *(charactersToGlyphBuffer + index);
- const Length secondaryNumberOfGlyphs = *(glyphsPerCharacterBuffer + index);
-
- const Vector2& secondaryPosition = *(glyphPositionsBuffer + secondaryGlyphIndex);
+ GetGlyphMetricsFromCharacterIndex(index, parameters.visualModel, parameters.logicalModel, metrics, glyphMetrics, glyphIndex, numberOfGlyphs);
- GetGlyphsMetrics(secondaryGlyphIndex,
- secondaryNumberOfGlyphs,
- glyphMetrics,
- glyphInfoBuffer,
- parameters.metrics);
+ const GlyphIndex secondaryGlyphIndex = glyphIndex;
+ const Vector2& secondaryPosition = *(glyphPositionsBuffer + secondaryGlyphIndex);
// Set the secondary cursor's position.
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.