#include <dali/devel-api/text-abstraction/font-client.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
#include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
namespace Dali
{
const bool RTL = true;
const float CURSOR_WIDTH = 1.f;
- Length CountParagraphs( const LayoutParameters& layoutParameters )
- {
- Length numberOfParagraphs = 0u;
-
- const CharacterIndex startCharacterIndex = *( layoutParameters.glyphsToCharactersBuffer + layoutParameters.startGlyphIndex );
-
- const GlyphIndex lastGlyphIndex = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs - 1u;
- const CharacterIndex lastCharacterIndexPlusOne = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
-
- for( CharacterIndex index = startCharacterIndex; index < lastCharacterIndexPlusOne; ++index )
- {
- if( TextAbstraction::LINE_MUST_BREAK == *( layoutParameters.lineBreakInfoBuffer + index ) )
- {
- ++numberOfParagraphs;
- }
- }
-
- return numberOfParagraphs;
- }
-
} //namespace
/**
LineLayout tmpLineLayout;
const bool isMultiline = mLayout == MULTI_LINE_BOX;
- const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
+
+ // The last glyph to be laid-out.
+ const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
// If the first glyph has a negative bearing its absolute value needs to be added to the line length.
// In the case the line starts with a right to left character, if the width is longer than the advance,
// the difference needs to be added to the line length.
- const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
+
+ // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ parameters.charactersPerGlyphBuffer );
+
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( lineLayout.glyphIndex,
+ numberOfGLyphsInGroup,
+ glyphMetrics,
+ parameters.glyphsBuffer,
+ mMetrics );
// Set the direction of the first character of the line.
lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
CharacterDirection previousCharacterDirection = firstCharacterDirection;
- const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
+ const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
- float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
+ float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
// Calculate the line height if there is no characters.
- FontId lastFontId = glyphInfo.fontId;
+ FontId lastFontId = glyphMetrics.fontId;
UpdateLineHeight( lastFontId, tmpLineLayout );
bool oneWordLaidOut = false;
- const GlyphIndex lastGlyphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
- glyphIndex < lastGlyphPlusOne;
- ++glyphIndex )
+ glyphIndex < lastGlyphOfParagraphPlusOne; )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
- const bool isLastGlyph = glyphIndex == lastGlyphIndex;
- // Get the glyph info.
- const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
+ // Check whether this glyph comes from a character that is shaped in multiple glyphs.
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ parameters.charactersPerGlyphBuffer );
+
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( glyphIndex,
+ numberOfGLyphsInGroup,
+ glyphMetrics,
+ parameters.glyphsBuffer,
+ mMetrics );
+
+ const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs;
// Check if the font of the current glyph is the same of the previous one.
// If it's different the ascender and descender need to be updated.
- if( lastFontId != glyphInfo.fontId )
+ if( lastFontId != glyphMetrics.fontId )
{
- UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
- lastFontId = glyphInfo.fontId;
+ UpdateLineHeight( glyphMetrics.fontId, tmpLineLayout );
+ lastFontId = glyphMetrics.fontId;
}
// Get the character indices for the current glyph. The last character index is needed
// because there are glyphs formed by more than one character but their break info is
// given only for the last character.
- const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
+ const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
+ const bool hasCharacters = charactersPerGlyph > 0u;
const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
- const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
+ const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
// Get the line break info for the current character.
- const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
+ const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
// Get the word break info for the current character.
const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
tmpLineLayout.numberOfCharacters += charactersPerGlyph;
// Increase the number of glyphs.
- tmpLineLayout.numberOfGlyphs++;
+ tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
// Check whether is a white space.
const Character character = *( parameters.textBuffer + characterFirstIndex );
if( isWhiteSpace )
{
// Add the length to the length of white spaces at the end of the line.
- tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
+ tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
}
else
{
// Add as well any previous white space length.
- tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
+ tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance;
// An extra space may be added to the line for the first and last glyph of the line.
// If the bearing of the first glyph is negative, its positive value needs to be added.
// | Rll|
//
- tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
+ tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
}
else // LTR
{
// |rrL |
//
- const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
+ const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
}
}
// -->
// |lllR |
- const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
+ const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
}
else // LTR
// <--
// | Lrrrr|
- tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
+ tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
}
}
else if( characterDirection == firstCharacterDirection )
// |llllllrr|
// |Rr |
- tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
+ tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
}
else // LTR
{
// |llllrrrr|
// | llL|
- const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
+ const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
}
}
if( tmpLineLayout.numberOfGlyphs > 0u )
{
tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
- --tmpLineLayout.numberOfGlyphs;
+ tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
tmpLineLayout.length = previousTmpLineLength;
tmpExtraBearing = previousTmpExtraBearing;
tmpExtraWidth = previousTmpExtraWidth;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
+
return;
}
}
previousCharacterDirection = characterDirection;
+ glyphIndex += numberOfGLyphsInGroup;
}
lineLayout.extraBearing = tmpExtraBearing;
void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
Length numberOfGlyphs,
- float penY,
Vector2* glyphPositionsBuffer )
{
// Traverse the glyphs and set the positions.
Vector2& position = *( glyphPositionsBuffer + i );
position.x = penX + glyph.xBearing;
- position.y = penY - glyph.yBearing;
+ position.y = -glyph.yBearing;
penX += glyph.advance;
}
SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
ellipsisLayout.numberOfGlyphs,
- penY,
glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
}
* @brief Updates the text layout with the last laid-out line.
*
* @param[in] layoutParameters The parameters needed to layout the text.
- * @param[in] layout The line layout.
+ * @param[in] characterIndex The character index of the line.
+ * @param[in] glyphIndex The glyph index of the line.
* @param[in,out] layoutSize The text's layout size.
* @param[in,out] linesBuffer Pointer to the line's buffer.
- * @param[in] index Index to the vector of glyphs.
* @param[in,out] numberOfLines The number of laid-out lines.
*/
void UpdateTextLayout( const LayoutParameters& layoutParameters,
- const LineLayout& layout,
+ CharacterIndex characterIndex,
+ GlyphIndex glyphIndex,
Size& layoutSize,
LineRun* linesBuffer,
- GlyphIndex index,
Length& numberOfLines )
{
// Need to add a new line with no characters but with height to increase the layoutSize.height
LineRun& lineRun = *( linesBuffer + numberOfLines );
++numberOfLines;
- lineRun.glyphRun.glyphIndex = index + layout.numberOfGlyphs;
+ lineRun.glyphRun.glyphIndex = glyphIndex;
lineRun.glyphRun.numberOfGlyphs = 0u;
- lineRun.characterRun.characterIndex = layout.characterIndex + layout.numberOfCharacters;
+ lineRun.characterRun.characterIndex = characterIndex;
lineRun.characterRun.numberOfCharacters = 0u;
lineRun.width = 0.f;
lineRun.ascender = fontMetrics.ascender;
if( 0u == layoutParameters.numberOfGlyphs )
{
- // Nothing to do if there are no glyphs to layout.
+ // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
+ if( layoutParameters.isLastNewParagraph )
+ {
+ Length numberOfLines = lines.Count();
+ if( 0u != numberOfLines )
+ {
+ const LineRun& lastLine = *( lines.End() - 1u );
+
+ if( 0u != lastLine.characterRun.numberOfCharacters )
+ {
+ // Need to add a new line with no characters but with height to increase the layoutSize.height
+ LineRun newLine;
+ lines.PushBack( newLine );
+
+ UpdateTextLayout( layoutParameters,
+ lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
+ lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
+ layoutSize,
+ lines.Begin(),
+ numberOfLines );
+ }
+ }
+ }
+
+ // Nothing else do if there are no glyphs to layout.
return false;
}
+ const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
+
+ // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
+ // This extra line needs to be removed.
+ if( 0u != lines.Count() )
+ {
+ Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
+
+ if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
+ ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
+ {
+ lines.Remove( lastLine );
+ }
+ }
+
// Set the first paragraph's direction.
CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
Vector<LineRun> newLines;
// Estimate the number of lines.
- // TODO: In a next patch the paragraphs are properly managed and this can be removed.
- Length linesCapacity = CountParagraphs( layoutParameters );
+ Length linesCapacity = layoutParameters.estimatedNumberOfLines;
Length numberOfLines = 0u;
if( updateCurrentBuffer )
float penY = SetParagraphOffset( lines,
layoutParameters.startLineIndex );
- const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
{
CharacterDirection currentParagraphDirection = paragraphDirection;
numberOfLines,
isLastLine );
- if( isLastLine &&
+ const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
+
+ if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
layoutParameters.isLastNewParagraph &&
( mLayout == MULTI_LINE_BOX ) )
{
}
UpdateTextLayout( layoutParameters,
- layout,
+ layout.characterIndex + layout.numberOfCharacters,
+ index + layout.numberOfGlyphs,
layoutSize,
linesBuffer,
- index,
numberOfLines );
} // whether to add a last line.
// Sets the positions of the glyphs.
SetGlyphPositions( layoutParameters.glyphsBuffer + index,
layout.numberOfGlyphs,
- penY,
glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
// Updates the vertical pen's position.
penY += -layout.descender;
// Increase the glyph index.
- index += layout.numberOfGlyphs;
-
+ index = nextIndex;
} // no ellipsis
} // end for() traversing glyphs.
glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
newGlyphPositions.Begin(),
newGlyphPositions.End() );
+ glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
newLines.Resize( numberOfLines );
#include <dali-toolkit/internal/text/bidirectional-support.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
#include <dali-toolkit/internal/text/color-segmentation.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
#include <dali-toolkit/internal/text/multi-language-support.h>
#include <dali-toolkit/internal/text/segmentation.h>
#include <dali-toolkit/internal/text/shaper.h>
+ #include <dali-toolkit/internal/text/text-run-container.h>
namespace
{
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
#endif
-/**
- * @brief Some characters can be shaped in more than one glyph.
- * This struct is used to retrieve metrics from these group of glyphs.
- */
-struct GlyphMetrics
-{
- GlyphMetrics()
- : fontHeight( 0.f ),
- advance( 0.f ),
- ascender( 0.f ),
- xBearing( 0.f )
- {}
-
- ~GlyphMetrics()
- {}
-
- float fontHeight; ///< The font's height of that glyphs.
- float advance; ///< The sum of all the advances of all the glyphs.
- float ascender; ///< The font's ascender.
- float xBearing; ///< The x bearing of the first glyph.
-};
-
} // namespace
namespace Dali
namespace Text
{
-/**
- * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
- *
- * @param[in] glyphIndex The index to the first glyph.
- * @param[in] numberOfGlyphs The number of glyphs.
- * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
- * @param[in] visualModel The visual model.
- * @param[in] metrics Used to access metrics from FontClient.
- */
-void GetGlyphsMetrics( GlyphIndex glyphIndex,
- Length numberOfGlyphs,
- GlyphMetrics& glyphMetrics,
- VisualModelPtr& visualModel,
- MetricsPtr& metrics )
-{
- const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
-
- const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
-
- Text::FontMetrics fontMetrics;
- metrics->GetFontMetrics( firstGlyph.fontId, fontMetrics );
-
- glyphMetrics.fontHeight = fontMetrics.height;
- glyphMetrics.advance = firstGlyph.advance;
- glyphMetrics.ascender = fontMetrics.ascender;
- glyphMetrics.xBearing = firstGlyph.xBearing;
-
- for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
- {
- const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
-
- glyphMetrics.advance += glyphInfo.advance;
- }
-}
-
EventData::EventData( DecoratorPtr decorator )
: mDecorator( decorator ),
mImfManager(),
return decoratorUpdated;
}
+ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
+ {
+ mTextUpdateInfo.mParagraphCharacterIndex = 0u;
+ mTextUpdateInfo.mStartGlyphIndex = 0u;
+ mTextUpdateInfo.mStartLineIndex = 0u;
+ numberOfCharacters = 0u;
+
+ const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
+ if( 0u == numberOfParagraphs )
+ {
+ mTextUpdateInfo.mParagraphCharacterIndex = 0u;
+ numberOfCharacters = 0u;
+
+ mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
+
+ // Nothing else to do if there are no paragraphs.
+ return;
+ }
+
+ // Find the paragraphs to be updated.
+ Vector<ParagraphRunIndex> paragraphsToBeUpdated;
+ if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
+ {
+ // Text is being added at the end of the current text.
+ if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
+ {
+ // Text is being added in a new paragraph after the last character of the text.
+ mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ numberOfCharacters = 0u;
+ mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
+
+ mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
+ mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
+
+ // Nothing else to do;
+ return;
+ }
+
+ paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
+ }
+ else
+ {
+ CharacterIndex lastIndex = 0u;
+ if( mTextUpdateInfo.mFullRelayoutNeeded )
+ {
+ lastIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ }
+ else
+ {
+ lastIndex = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
+ }
+ mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
+ lastIndex,
+ paragraphsToBeUpdated );
+ }
+
+ if( 0u != paragraphsToBeUpdated.Count() )
+ {
+ const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
+ const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
+ mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
+
+ ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
+ const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
+
+ if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
+ ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
+ ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
+ ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
+ {
+ // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
+ const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
+
+ numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
+ }
+ else
+ {
+ numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
+ }
+ }
+ mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
+ mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
+ }
+
+ void Controller::Impl::ClearFullModelData( OperationsMask operations )
+ {
+ if( GET_LINE_BREAKS & operations )
+ {
+ mLogicalModel->mLineBreakInfo.Clear();
+ mLogicalModel->mParagraphInfo.Clear();
+ }
+
+ if( GET_WORD_BREAKS & operations )
+ {
+ mLogicalModel->mLineBreakInfo.Clear();
+ }
+
+ if( GET_SCRIPTS & operations )
+ {
+ mLogicalModel->mScriptRuns.Clear();
+ }
+
+ if( VALIDATE_FONTS & operations )
+ {
+ mLogicalModel->mFontRuns.Clear();
+ }
+
+ if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
+ {
+ if( BIDI_INFO & operations )
+ {
+ mLogicalModel->mBidirectionalParagraphInfo.Clear();
+ mLogicalModel->mCharacterDirections.Clear();
+ }
+
+ if( REORDER & operations )
+ {
+ // Free the allocated memory used to store the conversion table in the bidirectional line info run.
+ for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
+ endIt = mLogicalModel->mBidirectionalLineInfo.End();
+ it != endIt;
+ ++it )
+ {
+ BidirectionalLineInfoRun& bidiLineInfo = *it;
+
+ free( bidiLineInfo.visualToLogicalMap );
+ bidiLineInfo.visualToLogicalMap = NULL;
+ }
+ mLogicalModel->mBidirectionalLineInfo.Clear();
+
+ mLogicalModel->mLogicalToVisualMap.Clear();
+ mLogicalModel->mVisualToLogicalMap.Clear();
+ }
+ }
+
+ if( SHAPE_TEXT & operations )
+ {
+ mVisualModel->mGlyphs.Clear();
+ mVisualModel->mGlyphsToCharacters.Clear();
+ mVisualModel->mCharactersToGlyph.Clear();
+ mVisualModel->mCharactersPerGlyph.Clear();
+ mVisualModel->mGlyphsPerCharacter.Clear();
+ mVisualModel->mGlyphPositions.Clear();
+ }
+
+ if( LAYOUT & operations )
+ {
+ mVisualModel->mLines.Clear();
+ }
+ }
+
+ void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
+ {
+ const CharacterIndex endIndexPlusOne = endIndex + 1u;
+
+ if( GET_LINE_BREAKS & operations )
+ {
+ // Clear the line break info.
+ LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
+
+ mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
+ lineBreakInfoBuffer + endIndexPlusOne );
+
+ // Clear the paragraphs.
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mLogicalModel->mParagraphInfo );
+ }
+
+ if( GET_WORD_BREAKS & operations )
+ {
+ // Clear the word break info.
+ WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
+
+ mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
+ wordBreakInfoBuffer + endIndexPlusOne );
+ }
+
+ if( GET_SCRIPTS & operations )
+ {
+ // Clear the scripts.
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mLogicalModel->mScriptRuns );
+ }
+
+ if( VALIDATE_FONTS & operations )
+ {
+ // Clear the fonts.
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mLogicalModel->mFontRuns );
+ }
+
+ if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
+ {
+ if( BIDI_INFO & operations )
+ {
+ // Clear the bidirectional paragraph info.
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mLogicalModel->mBidirectionalParagraphInfo );
+
+ // Clear the character's directions.
+ CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
+
+ mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
+ characterDirectionsBuffer + endIndexPlusOne );
+ }
+
+ if( REORDER & operations )
+ {
+ uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
+ uint32_t endRemoveIndex = startRemoveIndex;
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mLogicalModel->mBidirectionalLineInfo,
+ startRemoveIndex,
+ endRemoveIndex );
+
+ BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
+
+ // Free the allocated memory used to store the conversion table in the bidirectional line info run.
+ for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
+ endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
+ it != endIt;
+ ++it )
+ {
+ BidirectionalLineInfoRun& bidiLineInfo = *it;
+
+ free( bidiLineInfo.visualToLogicalMap );
+ bidiLineInfo.visualToLogicalMap = NULL;
+ }
+
+ mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
+ bidirectionalLineInfoBuffer + endRemoveIndex );
+
+ // Clear the logical to visual and the visual to logical conversion tables.
+ CharacterIndex* logicalToVisualMapBuffer = mLogicalModel->mLogicalToVisualMap.Begin();
+ mLogicalModel->mLogicalToVisualMap.Erase( logicalToVisualMapBuffer + startIndex,
+ logicalToVisualMapBuffer + endIndexPlusOne );
+
+ CharacterIndex* visualToLogicalMapBuffer = mLogicalModel->mVisualToLogicalMap.Begin();
+ mLogicalModel->mVisualToLogicalMap.Erase( visualToLogicalMapBuffer + startIndex,
+ visualToLogicalMapBuffer + endIndexPlusOne );
+ }
+ }
+ }
+
+ void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
+ {
+ const CharacterIndex endIndexPlusOne = endIndex + 1u;
+ const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
+
+ const bool clearShape = SHAPE_TEXT & operations;
+ const bool clearLayout = LAYOUT & operations;
+
+ if( clearShape || clearLayout )
+ {
+ // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
+ GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+ Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+
+ const GlyphIndex endGlyphIndex = *( charactersToGlyphBuffer + endIndex );
+ const GlyphIndex endGlyphIndexPlusOne = endGlyphIndex + *( glyphsPerCharacterBuffer + endIndex );
+ const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
+
+ if( clearShape )
+ {
+ // Update the character to glyph indices.
+ for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
+ endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
+ it != endIt;
+ ++it )
+ {
+ CharacterIndex& index = *it;
+ index -= numberOfGlyphsRemoved;
+ }
+
+ // Clear the character to glyph conversion table.
+ mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
+ charactersToGlyphBuffer + endIndexPlusOne );
+
+ // Clear the glyphs per character table.
+ mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
+ glyphsPerCharacterBuffer + endIndexPlusOne );
+
+ // Clear the glyphs buffer.
+ GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
+ mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
+ glyphsBuffer + endGlyphIndexPlusOne );
+
+ CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
+
+ // Update the glyph to character indices.
+ for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
+ endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
+ it != endIt;
+ ++it )
+ {
+ CharacterIndex& index = *it;
+ index -= numberOfCharactersRemoved;
+ }
+
+ // Clear the glyphs to characters buffer.
+ mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
+ glyphsToCharactersBuffer + endGlyphIndexPlusOne );
+
+ // Clear the characters per glyph buffer.
+ Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+ mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
+ charactersPerGlyphBuffer + endGlyphIndexPlusOne );
+
+ // Clear the positions buffer.
+ Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+ mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
+ positionsBuffer + endGlyphIndexPlusOne );
+ }
+
+ if( clearLayout )
+ {
+ // Clear the lines.
+ uint32_t startRemoveIndex = mVisualModel->mLines.Count();
+ uint32_t endRemoveIndex = startRemoveIndex;
+ ClearCharacterRuns( startIndex,
+ endIndex,
+ mVisualModel->mLines,
+ startRemoveIndex,
+ endRemoveIndex );
+
+ // Will update the glyph runs.
+ uint32_t startRemoveGlyphIndex = mVisualModel->mLines.Count();
+ uint32_t endRemoveGlyphIndex = startRemoveIndex;
+ ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
+ endGlyphIndex,
+ mVisualModel->mLines,
+ startRemoveGlyphIndex,
+ endRemoveGlyphIndex );
+
+ // Set the line index from where to insert the new laid-out lines.
+ mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
+
+ LineRun* linesBuffer = mVisualModel->mLines.Begin();
+ mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
+ linesBuffer + endRemoveIndex );
+ }
+ }
+ }
+
+ void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
+ {
+ if( mTextUpdateInfo.mClearAll ||
+ ( ( 0u == startIndex ) &&
+ ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
+ {
+ ClearFullModelData( operations );
+ }
+ else
+ {
+ // Clear the model data related with characters.
+ ClearCharacterModelData( startIndex, endIndex, operations );
+
+ // Clear the model data related with glyphs.
+ ClearGlyphModelData( startIndex, endIndex, operations );
+ }
+
+ // The estimated number of lines. Used to avoid reallocations when layouting.
+ mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
+
+ mVisualModel->ClearCaches();
+
+ // TODO finish the mark-up.
+ mVisualModel->mColorRuns.Clear();
+ }
+
void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
{
DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
// Calculate the operations to be done.
const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
+ if( NO_OPERATION == operations )
+ {
+ // Nothing to do if no operations are pending and required.
+ return;
+ }
+
Vector<Character>& utf32Characters = mLogicalModel->mText;
const Length numberOfCharacters = utf32Characters.Count();
- Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
+ // Index to the first character of the first paragraph to be updated.
CharacterIndex startIndex = 0u;
- Length requestedNumberOfCharacters = numberOfCharacters;
+ // Number of characters of the paragraphs to be removed.
+ Length paragraphCharacters = 0u;
+
+ CalculateTextUpdateIndices( paragraphCharacters );
+ startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
+
+ if( mTextUpdateInfo.mClearAll ||
+ ( 0u != paragraphCharacters ) )
+ {
+ ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operationsRequired );
+ }
+
+ mTextUpdateInfo.mClearAll = false;
+
+ Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
+ const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
+
if( GET_LINE_BREAKS & operations )
{
// Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
Vector<Character> mirroredUtf32Characters;
bool textMirrored = false;
- Length numberOfParagraphs = 0u;
+ const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
if( BIDI_INFO & operations )
{
- // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
- // bidirectional info.
-
- const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
- for( Length index = 0u; index < numberOfCharacters; ++index )
- {
- if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
- {
- ++numberOfParagraphs;
- }
- }
-
Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
bidirectionalInfo.Reserve( numberOfParagraphs );
Vector<GlyphIndex> newParagraphGlyphs;
newParagraphGlyphs.Reserve( numberOfParagraphs );
- GlyphIndex startGlyphIndex = 0u;
+ const Length currentNumberOfGlyphs = glyphs.Count();
if( SHAPE_TEXT & operations )
{
const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
scripts,
validFonts,
startIndex,
- startGlyphIndex,
+ mTextUpdateInfo.mStartGlyphIndex,
requestedNumberOfCharacters,
glyphs,
glyphsToCharactersMap,
newParagraphGlyphs );
// Create the 'number of glyphs' per character and the glyph to character conversion tables.
- mVisualModel->CreateGlyphsPerCharacterTable( startIndex, startGlyphIndex, numberOfCharacters );
- mVisualModel->CreateCharacterToGlyphTable( startIndex, startGlyphIndex, numberOfCharacters );
+ mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
+ mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
}
- const Length numberOfGlyphs = glyphs.Count();
+ const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
if( GET_GLYPH_METRICS & operations )
{
- GlyphInfo* glyphsBuffer = glyphs.Begin() + startGlyphIndex;
- mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
+ GlyphInfo* glyphsBuffer = glyphs.Begin();
+ mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
// Update the width and advance of all new paragraph characters.
for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
// TODO: At the moment the underline runs are only for pre-edit.
mVisualModel->mUnderlineRuns.PushBack( underlineRun );
}
+
+ // The estimated number of lines. Used to avoid reallocations when layouting.
+ mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
+
+ // Set the previous number of characters for the next time the text is updated.
+ mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
}
bool Controller::Impl::UpdateModelStyle( OperationsMask operationsRequired )
const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
+ Vector<Character>& utf32Characters = mLogicalModel->mText;
+ const Length numberOfCharacters = utf32Characters.Count();
+
// Validate the start and end selection points
- if( ( startOfSelectedText + lengthOfSelectedText ) <= mLogicalModel->mText.Count() )
+ if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
{
//Get text as a UTF8 string
- Vector<Character>& utf32Characters = mLogicalModel->mText;
-
Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
if( deleteAfterRetrieval ) // Only delete text if copied successfully
mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
- // Delete text between handles
- Vector<Character>& currentText = mLogicalModel->mText;
+ // Mark the paragraphs to be updated.
+ mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
+ mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
- Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
+ // Delete text between handles
+ Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
Vector<Character>::Iterator last = first + lengthOfSelectedText;
- currentText.Erase( first, last );
+ utf32Characters.Erase( first, last );
// Scroll after delete.
mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
// Get the glyphs per character table.
const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+ // Get the glyph's info buffer.
+ const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
+
// If the vector is void, there is no right to left characters.
const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
GetGlyphsMetrics( firstLogicalGlyphIndex,
numberOfGlyphs,
glyphMetrics,
- mVisualModel,
+ glyphInfoBuffer,
mMetrics );
// Get the position of the first glyph.
const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
+ const GlyphInfo* const glyphInfoBuffer = mVisualModel->mGlyphs.Begin();
// Convert the cursor position into the glyph position.
const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
GetGlyphsMetrics( primaryGlyphIndex,
primaryNumberOfGlyphs,
glyphMetrics,
- mVisualModel,
+ glyphInfoBuffer,
mMetrics );
// Whether to add the glyph's advance to the cursor position.
GetGlyphsMetrics( secondaryGlyphIndex,
secondaryNumberOfGlyphs,
glyphMetrics,
- mVisualModel,
+ glyphInfoBuffer,
mMetrics );
// Set the secondary cursor's position.