X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fcursor-helper-functions.cpp;h=c70d530c3577705f43dc6f7f67737db33213b349;hp=91b6a406ab5f883e7ebeaf22de36c33ff98ece8f;hb=2512aa2794f3c5ea5841542b9ed9c8c32973540b;hpb=eedb83ac31c3728a311dbe462bedc660f54094d8 diff --git a/dali-toolkit/internal/text/cursor-helper-functions.cpp b/dali-toolkit/internal/text/cursor-helper-functions.cpp index 91b6a40..c70d530 100644 --- a/dali-toolkit/internal/text/cursor-helper-functions.cpp +++ b/dali-toolkit/internal/text/cursor-helper-functions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 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. @@ -26,98 +26,99 @@ namespace { - #if defined(DEBUG_ENABLED) - Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction. struct FindWordData { - FindWordData( const Dali::Toolkit::Text::Character* const textBuffer, - Dali::Toolkit::Text::Length totalNumberOfCharacters, - Dali::Toolkit::Text::CharacterIndex hitCharacter, - bool isWhiteSpace, - bool isNewParagraph ) - : textBuffer( textBuffer ), - totalNumberOfCharacters( totalNumberOfCharacters ), - hitCharacter( hitCharacter ), - foundIndex( 0 ), - isWhiteSpace( isWhiteSpace ), - isNewParagraph( isNewParagraph ) - {} + FindWordData(const Dali::Toolkit::Text::Character* const textBuffer, + Dali::Toolkit::Text::Length totalNumberOfCharacters, + Dali::Toolkit::Text::CharacterIndex hitCharacter, + bool isWhiteSpace, + bool isNewParagraph) + : textBuffer(textBuffer), + totalNumberOfCharacters(totalNumberOfCharacters), + hitCharacter(hitCharacter), + foundIndex(0), + isWhiteSpace(isWhiteSpace), + isNewParagraph(isNewParagraph) + { + } ~FindWordData() - {} + { + } const Dali::Toolkit::Text::Character* const textBuffer; Dali::Toolkit::Text::Length totalNumberOfCharacters; Dali::Toolkit::Text::CharacterIndex hitCharacter; Dali::Toolkit::Text::CharacterIndex foundIndex; - bool isWhiteSpace : 1u; + bool isWhiteSpace : 1u; bool isNewParagraph : 1u; }; -bool IsWhiteSpaceOrNewParagraph( Dali::Toolkit::Text::Character character, - bool isHitWhiteSpace, - bool isHitWhiteSpaceOrNewParagraph ) +bool IsWhiteSpaceOrNewParagraph(Dali::Toolkit::Text::Character character, + bool isHitWhiteSpace, + bool isHitWhiteSpaceOrNewParagraph) { bool isWhiteSpaceOrNewParagraph = false; - if( isHitWhiteSpaceOrNewParagraph ) + if(isHitWhiteSpaceOrNewParagraph) { - if( isHitWhiteSpace ) + if(isHitWhiteSpace) { // Whether the current character is a white space. Note a new paragraph character is a white space as well but here is not wanted. - isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsWhiteSpace( character ) && !Dali::TextAbstraction::IsNewParagraph( character ); + isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsWhiteSpace(character) && !Dali::TextAbstraction::IsNewParagraph(character); } else { // Whether the current character is a new paragraph character. - isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsNewParagraph( character ); + isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsNewParagraph(character); } } else { // Whether the current character is a white space or a new paragraph character (note the new paragraph character is a white space as well). - isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsWhiteSpace( character ); + isWhiteSpaceOrNewParagraph = Dali::TextAbstraction::IsWhiteSpace(character); } return isWhiteSpaceOrNewParagraph; } -void FindStartOfWord( FindWordData& data ) +void FindStartOfWord(FindWordData& data) { const bool isHitWhiteSpaceOrNewParagraph = data.isWhiteSpace || data.isNewParagraph; - for( data.foundIndex = data.hitCharacter; data.foundIndex > 0; --data.foundIndex ) + for(data.foundIndex = data.hitCharacter; data.foundIndex > 0; --data.foundIndex) { - const Dali::Toolkit::Text::Character character = *( data.textBuffer + data.foundIndex - 1u ); + const Dali::Toolkit::Text::Character character = *(data.textBuffer + data.foundIndex - 1u); - const bool isWhiteSpaceOrNewParagraph = IsWhiteSpaceOrNewParagraph( character, - data.isWhiteSpace, - isHitWhiteSpaceOrNewParagraph ); + const bool isWhiteSpaceOrNewParagraph = IsWhiteSpaceOrNewParagraph(character, + data.isWhiteSpace, + isHitWhiteSpaceOrNewParagraph); - if( isHitWhiteSpaceOrNewParagraph != isWhiteSpaceOrNewParagraph ) + if(isHitWhiteSpaceOrNewParagraph != isWhiteSpaceOrNewParagraph) { break; } } } -void FindEndOfWord( FindWordData& data ) +void FindEndOfWord(FindWordData& data) { const bool isHitWhiteSpaceOrNewParagraph = data.isWhiteSpace || data.isNewParagraph; - for( data.foundIndex = data.hitCharacter + 1u; data.foundIndex < data.totalNumberOfCharacters; ++data.foundIndex ) + for(data.foundIndex = data.hitCharacter + 1u; data.foundIndex < data.totalNumberOfCharacters; ++data.foundIndex) { - const Dali::Toolkit::Text::Character character = *( data.textBuffer + data.foundIndex ); + const Dali::Toolkit::Text::Character character = *(data.textBuffer + data.foundIndex); - const bool isWhiteSpaceOrNewParagraph = IsWhiteSpaceOrNewParagraph( character, - data.isWhiteSpace, - isHitWhiteSpaceOrNewParagraph ); + const bool isWhiteSpaceOrNewParagraph = IsWhiteSpaceOrNewParagraph(character, + data.isWhiteSpace, + isHitWhiteSpaceOrNewParagraph); - if( isHitWhiteSpaceOrNewParagraph != isWhiteSpaceOrNewParagraph ) + if(isHitWhiteSpaceOrNewParagraph != isWhiteSpaceOrNewParagraph) { break; } @@ -128,47 +129,42 @@ void FindEndOfWord( FindWordData& data ) namespace Dali { - namespace Toolkit { - namespace Text { - -LineIndex GetClosestLine( VisualModelPtr visualModel, - float visualY, - bool& matchedLine ) +LineIndex GetClosestLine(VisualModelPtr visualModel, + float visualY, + bool& matchedLine) { - float totalHeight = 0.f; - LineIndex lineIndex = 0; - matchedLine = false; + float totalHeight = 0.f; + LineIndex lineIndex = 0; + matchedLine = false; - if( visualY < 0.f ) + if(visualY < 0.f) { return 0; } const Vector& lines = visualModel->mLines; - for( Vector::ConstIterator it = lines.Begin(), - endIt = lines.End(); - it != endIt; - ++it, ++lineIndex ) + for(Vector::ConstIterator it = lines.Begin(), + endIt = lines.End(); + it != endIt; + ++it, ++lineIndex) { const LineRun& lineRun = *it; - // 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); - if( visualY < totalHeight ) + if(visualY < totalHeight) { matchedLine = true; return lineIndex; } } - if( lineIndex == 0 ) + if(lineIndex == 0) { return 0; } @@ -176,35 +172,33 @@ LineIndex GetClosestLine( VisualModelPtr visualModel, return lineIndex - 1u; } -float CalculateLineOffset( const Vector& lines, - LineIndex lineIndex ) +float CalculateLineOffset(const Vector& lines, + LineIndex lineIndex) { float offset = 0.f; - for( Vector::ConstIterator it = lines.Begin(), - endIt = lines.Begin() + lineIndex; - it != endIt; - ++it ) + for(Vector::ConstIterator it = lines.Begin(), + endIt = lines.Begin() + lineIndex; + it != endIt; + ++it) { const LineRun& lineRun = *it; - // 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); } return offset; } -CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, - LogicalModelPtr logicalModel, - MetricsPtr metrics, - float visualX, - float visualY, - CharacterHitTest::Mode mode, - bool& matchedCharacter ) +CharacterIndex GetClosestCursorIndex(VisualModelPtr visualModel, + LogicalModelPtr logicalModel, + MetricsPtr metrics, + float visualX, + float visualY, + CharacterHitTest::Mode mode, + bool& matchedCharacter) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex, closest visualX %f visualY %f\n", visualX, visualY ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetClosestCursorIndex, closest visualX %f visualY %f\n", visualX, visualY); // Whether there is a hit on a glyph. matchedCharacter = false; @@ -213,28 +207,29 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, const Length totalNumberOfGlyphs = visualModel->mGlyphs.Count(); const Length totalNumberOfLines = visualModel->mLines.Count(); - if( ( 0 == totalNumberOfGlyphs ) || - ( 0 == totalNumberOfLines ) ) + if((0 == totalNumberOfGlyphs) || + (0 == totalNumberOfLines)) { return logicalIndex; } + const float characterSpacing = visualModel->GetCharacterSpacing(); // Whether there is a hit on a line. bool matchedLine = false; // Find which line is closest. - const LineIndex lineIndex = Text::GetClosestLine( visualModel, - visualY, - matchedLine ); + const LineIndex lineIndex = Text::GetClosestLine(visualModel, + visualY, + matchedLine); - if( !matchedLine && ( CharacterHitTest::TAP == mode ) ) + if(!matchedLine && (CharacterHitTest::TAP == mode)) { // Return the first or the last character if the touch point doesn't hit a line. - return ( visualY < 0.f ) ? 0 : logicalModel->mText.Count(); + return (visualY < 0.f) ? 0 : logicalModel->mText.Count(); } // Convert from text's coords to line's coords. - const LineRun& line = *( visualModel->mLines.Begin() + lineIndex ); + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex); // Transform the tap point from text's coords to line's coords. visualX -= line.alignmentOffset; @@ -256,10 +251,10 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, const CharacterIndex startCharacter = line.characterRun.characterIndex; const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters; - DALI_ASSERT_DEBUG( endCharacter <= logicalModel->mText.Count() && "Invalid line info" ); + DALI_ASSERT_DEBUG(endCharacter <= logicalModel->mText.Count() && "Invalid line info"); // Whether this line is a bidirectional line. - const bool bidiLineFetched = logicalModel->FetchBidirectionalLineInfo( startCharacter ); + const bool bidiLineFetched = logicalModel->FetchBidirectionalLineInfo(startCharacter); // The character's direction buffer. const CharacterDirection* const directionsBuffer = bidiLineFetched ? logicalModel->mCharacterDirections.Begin() : NULL; @@ -268,41 +263,46 @@ 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; - for( ; visualIndex < endCharacter; ++visualIndex ) + CharacterIndex visualIndex = startCharacter; + Length numberOfVisualCharacters = 0; + float calculatedAdvance = 0.f; + Vector& glyphToCharacterMap = visualModel->mGlyphsToCharacters; + const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin(); + for(; visualIndex < endCharacter; ++visualIndex) { // The character in logical order. - const CharacterIndex characterLogicalOrderIndex = ( bidiLineFetched ? logicalModel->GetLogicalCharacterIndex( visualIndex ) : visualIndex ); - const CharacterDirection direction = ( bidiLineFetched ? *( directionsBuffer + characterLogicalOrderIndex ) : LTR ); + const CharacterIndex characterLogicalOrderIndex = (bidiLineFetched ? logicalModel->GetLogicalCharacterIndex(visualIndex) : visualIndex); + const CharacterDirection direction = (bidiLineFetched ? *(directionsBuffer + characterLogicalOrderIndex) : LTR); // The number of glyphs for that character - const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex ); + const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterLogicalOrderIndex); ++numberOfVisualCharacters; - if( 0 != numberOfGlyphs ) + if(0 != numberOfGlyphs) { // Get the first character/glyph of the group of glyphs. - const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfVisualCharacters; - const CharacterIndex firstLogicalCharacterIndex = ( bidiLineFetched ? logicalModel->GetLogicalCharacterIndex( firstVisualCharacterIndex ) : firstVisualCharacterIndex ); - const GlyphIndex firstLogicalGlyphIndex = *( charactersToGlyphBuffer + firstLogicalCharacterIndex ); + const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfVisualCharacters; + const CharacterIndex firstLogicalCharacterIndex = (bidiLineFetched ? logicalModel->GetLogicalCharacterIndex(firstVisualCharacterIndex) : firstVisualCharacterIndex); + const GlyphIndex firstLogicalGlyphIndex = *(charactersToGlyphBuffer + firstLogicalCharacterIndex); // Get the metrics for the group of glyphs. GlyphMetrics glyphMetrics; - GetGlyphsMetrics( firstLogicalGlyphIndex, - numberOfGlyphs, - glyphMetrics, - glyphInfoBuffer, - metrics ); + calculatedAdvance = GetCalculatedAdvance(*(logicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + firstLogicalGlyphIndex))), characterSpacing, (*(visualModel->mGlyphs.Begin() + firstLogicalGlyphIndex)).advance); + GetGlyphsMetrics(firstLogicalGlyphIndex, + numberOfGlyphs, + glyphMetrics, + glyphInfoBuffer, + metrics, + calculatedAdvance); // Get the position of the first glyph. - const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex ); + const Vector2& position = *(positionsBuffer + firstLogicalGlyphIndex); - if( startCharacter == visualIndex ) + if(startCharacter == visualIndex) { const float glyphPosition = -glyphMetrics.xBearing + position.x; - if( visualX < glyphPosition ) + if(visualX < glyphPosition) { isBeforeFirstGlyph = true; break; @@ -310,15 +310,15 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, } // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic (ل + ا). - Length numberOfCharacters = *( charactersPerGlyphBuffer + firstLogicalGlyphIndex ); - if( direction != LTR ) + Length numberOfCharacters = *(charactersPerGlyphBuffer + firstLogicalGlyphIndex); + if(direction != LTR) { // As characters are being traversed in visual order, // for right to left ligatures, the character which contains the // number of glyphs in the table is found first. // Jump the number of characters to the next glyph is needed. - if( 0 == numberOfCharacters ) + if(0 == numberOfCharacters) { // TODO: This is a workaround to fix an issue with complex characters in the arabic // script like i.e. رّ or الأَبْجَدِيَّة العَرَبِيَّة @@ -331,14 +331,14 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, // to hang in an infinite loop. // Find the number of characters. - for( GlyphIndex index = firstLogicalGlyphIndex + 1u; - ( 0 == numberOfCharacters ) && ( index < totalNumberOfGlyphs ); - ++index ) + for(GlyphIndex index = firstLogicalGlyphIndex + 1u; + (0 == numberOfCharacters) && (index < totalNumberOfGlyphs); + ++index) { - numberOfCharacters = *( charactersPerGlyphBuffer + index ); + numberOfCharacters = *(charactersPerGlyphBuffer + index); } - if( 2u > numberOfCharacters ) + if(2u > numberOfCharacters) { continue; } @@ -350,26 +350,26 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, } // Get the script of the character. - const Script script = logicalModel->GetScript( characterLogicalOrderIndex ); + const Script script = logicalModel->GetScript(characterLogicalOrderIndex); - const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script ); - const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u; - const float glyphAdvance = glyphMetrics.advance / static_cast( numberOfBlocks ); + const bool isInterglyphIndex = (numberOfCharacters > numberOfGlyphs) && HasLigatureMustBreak(script); + const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u; + const float glyphAdvance = glyphMetrics.advance / static_cast(numberOfBlocks); CharacterIndex index = 0; - for( ; index < numberOfBlocks; ++index ) + for(; index < numberOfBlocks; ++index) { // Find the mid-point of the area containing the glyph - const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast( index ) + 0.5f ) * glyphAdvance; + const float glyphCenter = -glyphMetrics.xBearing + position.x + (static_cast(index) + 0.5f) * glyphAdvance; - if( visualX < glyphCenter ) + if(visualX < glyphCenter) { matchedCharacter = true; break; } } - if( matchedCharacter ) + if(matchedCharacter) { // If the glyph is shaped from more than one character, it matches the character of the glyph. visualIndex = firstVisualCharacterIndex + index; @@ -385,9 +385,9 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, // Return the logical position of the cursor in characters. - if( !matchedCharacter ) + if(!matchedCharacter) { - if( isBeforeFirstGlyph ) + if(isBeforeFirstGlyph) { // If no character is matched, then the first character (in visual order) of the line is used. visualIndex = startCharacter; @@ -402,15 +402,15 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, // Get the paragraph direction. const CharacterDirection paragraphDirection = line.direction; - if( totalNumberOfCharacters != visualIndex ) + if(totalNumberOfCharacters != visualIndex) { // The visual index is not at the end of the text. - if( LTR == paragraphDirection ) + if(LTR == paragraphDirection) { // The paragraph direction is left to right. - if( visualIndex == endCharacter ) + if(visualIndex == endCharacter) { // It places the cursor just before the last character in visual order. // i.e. it places the cursor just before the '\n' or before the last character @@ -426,8 +426,8 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, { // The paragraph direction is right to left. - if( ( lineIndex != totalNumberOfLines - 1u ) && // is not the last line. - ( visualIndex == startCharacter ) ) + if((lineIndex != totalNumberOfLines - 1u) && // is not the last line. + (visualIndex == startCharacter)) { // It places the cursor just after the first character in visual order. // i.e. it places the cursor just after the '\n' or after the last character @@ -447,115 +447,129 @@ CharacterIndex GetClosestCursorIndex( VisualModelPtr visualModel, // This branch checks if the closest line is the one with the last '\n'. If it is, it decrements the visual index to place // the cursor just before the last '\n'. - if( ( lineIndex != totalNumberOfLines - 1u ) && - TextAbstraction::IsNewParagraph( *( logicalModel->mText.Begin() + visualIndex - 1u ) ) ) + if((lineIndex != totalNumberOfLines - 1u) && + TextAbstraction::IsNewParagraph(*(logicalModel->mText.Begin() + visualIndex - 1u))) { --visualIndex; } } - logicalIndex = ( bidiLineFetched ? logicalModel->GetLogicalCursorIndex( visualIndex ) : visualIndex ); + logicalIndex = (bidiLineFetched ? logicalModel->GetLogicalCursorIndex(visualIndex) : visualIndex); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "closest visualIndex %d logicalIndex %d\n", visualIndex, logicalIndex ); + 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" ); + DALI_ASSERT_DEBUG((logicalIndex <= logicalModel->mText.Count() && logicalIndex >= 0) && "GetClosestCursorIndex - Out of bounds index"); return logicalIndex; } - -void GetCursorPosition( GetCursorPositionParameters& parameters, - CursorInfo& cursorInfo ) +void GetCursorPosition(GetCursorPositionParameters& parameters, + float defaultFontLineHeight, + CursorInfo& cursorInfo) { + const LineRun* const modelLines = parameters.visualModel->mLines.Begin(); + if(NULL == modelLines) + { + // Nothing to do. + return; + } + // Whether the logical cursor position is at the end of the whole text. const bool isLastPosition = parameters.logicalModel->mText.Count() == parameters.logical; // Get the line where the character is laid-out. - const CharacterIndex characterOfLine = isLastPosition ? ( parameters.logical - 1u ) : parameters.logical; + const CharacterIndex characterOfLine = isLastPosition ? (parameters.logical - 1u) : parameters.logical; // Whether the cursor is in the last position and the last position is a new paragraph character. - const bool isLastNewParagraph = parameters.isMultiline && isLastPosition && TextAbstraction::IsNewParagraph( *( parameters.logicalModel->mText.Begin() + characterOfLine ) ); + const bool isLastNewParagraph = parameters.isMultiline && isLastPosition && TextAbstraction::IsNewParagraph(*(parameters.logicalModel->mText.Begin() + characterOfLine)); - const LineRun* const modelLines = parameters.visualModel->mLines.Begin(); + const LineIndex lineIndex = parameters.visualModel->GetLineOfCharacter(characterOfLine); + const LineRun& line = *(modelLines + lineIndex); - 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 ) + if(isLastNewParagraph) { // The cursor is in a new line with no characters. Place the cursor in that line. const LineIndex newLineIndex = lineIndex + 1u; - const LineRun& newLine = *( modelLines + newLineIndex ); + const LineRun& newLine = *(modelLines + newLineIndex); cursorInfo.isSecondaryCursor = false; // Set the line offset and height. - cursorInfo.lineOffset = CalculateLineOffset( parameters.visualModel->mLines, - newLineIndex ); + cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines, + 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. - cursorInfo.lineHeight = newLine.ascender - newLine.descender; + cursorInfo.lineHeight = GetLineHeight(newLine); + + 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 = 0.f; + cursorInfo.primaryPosition.x = (LTR == line.direction) ? newLine.alignmentOffset : parameters.visualModel->mControlSize.width - newLine.alignmentOffset; cursorInfo.primaryPosition.y = cursorInfo.lineOffset; - - // Transform the cursor info from line's coords to text's coords. - cursorInfo.primaryPosition.x += ( LTR == line.direction ) ? 0.f : parameters.visualModel->mControlSize.width; } else { // Whether this line is a bidirectional line. - const bool bidiLineFetched = parameters.logicalModel->FetchBidirectionalLineInfo( characterOfLine ); + const bool bidiLineFetched = parameters.logicalModel->FetchBidirectionalLineInfo(characterOfLine); // Check if the logical position is the first or the last one of the line. const bool isFirstPositionOfLine = line.characterRun.characterIndex == parameters.logical; - const bool isLastPositionOfLine = line.characterRun.characterIndex + line.characterRun.numberOfCharacters == parameters.logical; + const bool isLastPositionOfLine = line.characterRun.characterIndex + line.characterRun.numberOfCharacters == parameters.logical; // 'logical' is the logical 'cursor' index. // Get the next and current logical 'character' index. - const CharacterIndex characterIndex = isFirstPositionOfLine ? parameters.logical : parameters.logical - 1u; + const CharacterIndex characterIndex = isFirstPositionOfLine ? parameters.logical : parameters.logical - 1u; const CharacterIndex nextCharacterIndex = isLastPositionOfLine ? characterIndex : parameters.logical; // The character's direction buffer. const CharacterDirection* const directionsBuffer = bidiLineFetched ? parameters.logicalModel->mCharacterDirections.Begin() : NULL; CharacterDirection isCurrentRightToLeft = false; - CharacterDirection isNextRightToLeft = false; - if( bidiLineFetched ) // If bidiLineFetched is false, it means the whole text is left to right. + CharacterDirection isNextRightToLeft = false; + if(bidiLineFetched) // If bidiLineFetched is false, it means the whole text is left to right. { - isCurrentRightToLeft = *( directionsBuffer + characterIndex ); - isNextRightToLeft = *( directionsBuffer + nextCharacterIndex ); + isCurrentRightToLeft = *(directionsBuffer + characterIndex); + isNextRightToLeft = *(directionsBuffer + nextCharacterIndex); } // Get the paragraph's direction. const CharacterDirection isRightToLeftParagraph = line.direction; // Check whether there is an alternative position: - cursorInfo.isSecondaryCursor = ( ( !isLastPositionOfLine && ( isCurrentRightToLeft != isNextRightToLeft ) ) || - ( isLastPositionOfLine && ( isRightToLeftParagraph != isCurrentRightToLeft ) ) || - ( isFirstPositionOfLine && ( isRightToLeftParagraph != isCurrentRightToLeft ) ) ); + cursorInfo.isSecondaryCursor = ((!isLastPositionOfLine && (isCurrentRightToLeft != isNextRightToLeft)) || + (isLastPositionOfLine && (isRightToLeftParagraph != isCurrentRightToLeft)) || + (isFirstPositionOfLine && (isRightToLeftParagraph != isCurrentRightToLeft))); // Set the line offset and height. - cursorInfo.lineOffset = CalculateLineOffset( parameters.visualModel->mLines, - lineIndex ); + cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines, + 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. - cursorInfo.lineHeight = line.ascender - line.descender; + cursorInfo.lineHeight = GetLineHeight(line); // Calculate the primary cursor. - CharacterIndex index = characterIndex; - if( cursorInfo.isSecondaryCursor ) + index = characterIndex; + if(cursorInfo.isSecondaryCursor) { // If there is a secondary position, the primary cursor may be in a different place than the logical index. - if( isLastPositionOfLine ) + if(isLastPositionOfLine) { // The position of the cursor after the last character needs special // care depending on its direction and the direction of the paragraph. @@ -564,44 +578,36 @@ void GetCursorPosition( GetCursorPositionParameters& parameters, // i.e l0 l1 l2 r0 r1 should find r0. index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u; - if( bidiLineFetched ) + if(bidiLineFetched) { - index = parameters.logicalModel->GetLogicalCharacterIndex( index ); + index = parameters.logicalModel->GetLogicalCharacterIndex(index); } } - else if( isFirstPositionOfLine ) + else if(isFirstPositionOfLine) { index = isRightToLeftParagraph ? line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u : line.characterRun.characterIndex; - if( bidiLineFetched ) + if(bidiLineFetched) { - index = parameters.logicalModel->GetLogicalCharacterIndex( index ); + index = parameters.logicalModel->GetLogicalCharacterIndex(index); } } else { - index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex; + index = (isRightToLeftParagraph == isCurrentRightToLeft) ? characterIndex : nextCharacterIndex; } } - 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 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(); - - // Convert the cursor position into the glyph position. - const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index ); - const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index ); - const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex ); + const Vector2* const glyphPositionsBuffer = parameters.visualModel->mGlyphPositions.Begin(); + const float characterSpacing = parameters.visualModel->GetCharacterSpacing(); // 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, @@ -633,39 +639,38 @@ void GetCursorPosition( GetCursorPositionParameters& parameters, // P -> isRightToLeftParagraph // A -> Whether to add the glyph's advance. - const bool addGlyphAdvance = ( ( isLastPositionOfLine && !isRightToLeftParagraph ) || - ( isFirstPositionOfLine && isRightToLeftParagraph ) || - ( !isFirstPositionOfLine && !isLastPosition && !isCurrentRightToLeft ) ); + const bool addGlyphAdvance = ((isLastPositionOfLine && !isRightToLeftParagraph) || + (isFirstPositionOfLine && isRightToLeftParagraph) || + (!isFirstPositionOfLine && !isLastPosition && !isCurrentRightToLeft)); - float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f; + float glyphAdvance = addGlyphAdvance ? (glyphMetrics.advance) : 0.f; - if( !isLastPositionOfLine && - ( primaryNumberOfCharacters > 1u ) ) + if(!isLastPositionOfLine && + (primaryNumberOfCharacters > 1u)) { - const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex ); + const CharacterIndex firstIndex = *(glyphsToCharactersBuffer + primaryGlyphIndex); bool isCurrentRightToLeft = false; - if( bidiLineFetched ) // If bidiLineFetched is false, it means the whole text is left to right. + if(bidiLineFetched) // If bidiLineFetched is false, it means the whole text is left to right. { - isCurrentRightToLeft = *( directionsBuffer + index ); + isCurrentRightToLeft = *(directionsBuffer + index); } - Length numberOfGlyphAdvance = ( isFirstPositionOfLine ? 0 : 1u ) + characterIndex - firstIndex; - if( isCurrentRightToLeft ) + Length numberOfGlyphAdvance = (isFirstPositionOfLine ? 0 : 1u) + characterIndex - firstIndex; + if(isCurrentRightToLeft) { numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance; } - glyphAdvance = static_cast( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast( primaryNumberOfCharacters ); + glyphAdvance = static_cast(numberOfGlyphAdvance) * (glyphMetrics.advance) / static_cast(primaryNumberOfCharacters); } // Get the glyph position and x bearing (in the line's coords). - const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex ); + const Vector2& primaryPosition = *(glyphPositionsBuffer + primaryGlyphIndex); // Set the primary cursor's height. cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight; - cursorInfo.glyphOffset = line.ascender - glyphMetrics.ascender; // Set the primary cursor's position. cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance; @@ -675,27 +680,21 @@ void GetCursorPosition( GetCursorPositionParameters& parameters, cursorInfo.primaryPosition.x += line.alignmentOffset; // Calculate the secondary cursor. - if( cursorInfo.isSecondaryCursor ) + if(cursorInfo.isSecondaryCursor) { // Set the secondary cursor's height. cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight; CharacterIndex index = characterIndex; - if( !isLastPositionOfLine ) + if(!isLastPositionOfLine) { - index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex; + 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. @@ -715,10 +714,10 @@ void GetCursorPosition( GetCursorPositionParameters& parameters, // P -> isRightToLeftParagraph // A -> Whether to add the glyph's advance. - const bool addGlyphAdvance = ( ( !isFirstPositionOfLine && !isCurrentRightToLeft ) || - ( isFirstPositionOfLine && !isRightToLeftParagraph ) ); + 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. @@ -727,16 +726,16 @@ void GetCursorPosition( GetCursorPositionParameters& parameters, } } -bool FindSelectionIndices( VisualModelPtr visualModel, - LogicalModelPtr logicalModel, - MetricsPtr metrics, - float visualX, - float visualY, - CharacterIndex& startIndex, - CharacterIndex& endIndex, - CharacterIndex& noTextHitIndex ) +bool FindSelectionIndices(VisualModelPtr visualModel, + LogicalModelPtr logicalModel, + MetricsPtr metrics, + float visualX, + float visualY, + CharacterIndex& startIndex, + CharacterIndex& endIndex, + CharacterIndex& noTextHitIndex) { -/* + /* Hit character Select |-------------------------------------------------------|------------------------------------------| | On a word | The word | @@ -747,36 +746,36 @@ bool FindSelectionIndices( VisualModelPtr visualModel, |-------------------------------------------------------|------------------------------------------| */ const Length totalNumberOfCharacters = logicalModel->mText.Count(); - startIndex = 0; - endIndex = 0; - noTextHitIndex = 0; + startIndex = 0; + endIndex = 0; + noTextHitIndex = 0; - if( 0 == totalNumberOfCharacters ) + if(0 == totalNumberOfCharacters) { // Nothing to do if the model is empty. return false; } - bool matchedCharacter = false; - CharacterIndex hitCharacter = Text::GetClosestCursorIndex( visualModel, - logicalModel, - metrics, - visualX, - visualY, - CharacterHitTest::TAP, - matchedCharacter ); + bool matchedCharacter = false; + CharacterIndex hitCharacter = Text::GetClosestCursorIndex(visualModel, + logicalModel, + metrics, + visualX, + visualY, + CharacterHitTest::TAP, + matchedCharacter); - if( !matchedCharacter ) + if(!matchedCharacter) { noTextHitIndex = hitCharacter; } - DALI_ASSERT_DEBUG( ( hitCharacter <= totalNumberOfCharacters ) && "GetClosestCursorIndex returned out of bounds index" ); + DALI_ASSERT_DEBUG((hitCharacter <= totalNumberOfCharacters) && "GetClosestCursorIndex returned out of bounds index"); - if( hitCharacter >= totalNumberOfCharacters ) + if(hitCharacter >= totalNumberOfCharacters) { // Closest hit character is the last character. - if( hitCharacter == totalNumberOfCharacters ) + if(hitCharacter == totalNumberOfCharacters) { hitCharacter--; //Hit character index set to last character in logical model } @@ -790,69 +789,69 @@ bool FindSelectionIndices( VisualModelPtr visualModel, const Character* const textBuffer = logicalModel->mText.Begin(); startIndex = hitCharacter; - endIndex = hitCharacter; + endIndex = hitCharacter; // Whether the hit character is a new paragraph character. - const bool isHitCharacterNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + hitCharacter ) ); + const bool isHitCharacterNewParagraph = TextAbstraction::IsNewParagraph(*(textBuffer + hitCharacter)); // Whether the hit character is a white space. Note a new paragraph character is a white space as well but here is not wanted. - const bool isHitCharacterWhiteSpace = TextAbstraction::IsWhiteSpace( *( textBuffer + hitCharacter ) ) && !isHitCharacterNewParagraph; + const bool isHitCharacterWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + hitCharacter)) && !isHitCharacterNewParagraph; - FindWordData data( textBuffer, - totalNumberOfCharacters, - hitCharacter, - isHitCharacterWhiteSpace, - isHitCharacterNewParagraph ); + FindWordData data(textBuffer, + totalNumberOfCharacters, + hitCharacter, + isHitCharacterWhiteSpace, + isHitCharacterNewParagraph); - if( isHitCharacterNewParagraph ) + if(isHitCharacterNewParagraph) { // Find the first character before the hit one which is not a new paragraph character. - if( hitCharacter > 0 ) + if(hitCharacter > 0) { endIndex = hitCharacter - 1u; - for( ; endIndex > 0; --endIndex ) + for(; endIndex > 0; --endIndex) { - const Dali::Toolkit::Text::Character character = *( data.textBuffer + endIndex ); + const Dali::Toolkit::Text::Character character = *(data.textBuffer + endIndex); - if( !Dali::TextAbstraction::IsNewParagraph( character ) ) + if(!Dali::TextAbstraction::IsNewParagraph(character)) { break; } } } - data.hitCharacter = endIndex; + data.hitCharacter = endIndex; data.isNewParagraph = false; - data.isWhiteSpace = TextAbstraction::IsWhiteSpace( *( textBuffer + data.hitCharacter ) ); + data.isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + data.hitCharacter)); } // Find the start of the word. - FindStartOfWord( data ); + FindStartOfWord(data); startIndex = data.foundIndex; // Find the end of the word. - FindEndOfWord( data ); + FindEndOfWord(data); endIndex = data.foundIndex; - if( 1u == ( endIndex - startIndex ) ) + if(1u == (endIndex - startIndex)) { - if( isHitCharacterWhiteSpace ) + if(isHitCharacterWhiteSpace) { // Select the word before or after the white space - if( 0 == hitCharacter ) + if(0 == hitCharacter) { data.isWhiteSpace = false; - FindEndOfWord( data ); + FindEndOfWord(data); endIndex = data.foundIndex; } - else if( hitCharacter > 0 ) + else if(hitCharacter > 0) { // Find the start of the word. data.hitCharacter = hitCharacter - 1u; data.isWhiteSpace = false; - FindStartOfWord( data ); + FindStartOfWord(data); startIndex = data.foundIndex; --endIndex;