X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Frendering%2Fview-model.cpp;h=2187487f157f28bd48cb3ed2efcb952029f53a63;hp=f695cd2e53a6206cc9053199355e0d4853f1bdbe;hb=73a2e4a851e3b5b1f35281ed895d06c2bc8b76c5;hpb=656f6eec4881d0305fc1322a0531cadd816287dc diff --git a/dali-toolkit/internal/text/rendering/view-model.cpp b/dali-toolkit/internal/text/rendering/view-model.cpp index f695cd2..2187487 100644 --- a/dali-toolkit/internal/text/rendering/view-model.cpp +++ b/dali-toolkit/internal/text/rendering/view-model.cpp @@ -35,7 +35,11 @@ ViewModel::ViewModel(const ModelInterface* const model) : mModel(model), mElidedGlyphs(), mElidedLayout(), - mIsTextElided(false) + mIsTextElided(false), + mStartIndexOfElidedGlyphs(0u), + mEndIndexOfElidedGlyphs(0u), + mFirstMiddleIndexOfElidedGlyphs(0u), + mSecondMiddleIndexOfElidedGlyphs(0u) { } @@ -73,6 +77,11 @@ DevelText::VerticalLineAlignment::Type ViewModel::GetVerticalLineAlignment() con return mModel->GetVerticalLineAlignment(); } +DevelText::EllipsisPosition::Type ViewModel::GetEllipsisPosition() const +{ + return mModel->GetEllipsisPosition(); +} + bool ViewModel::IsTextElideEnabled() const { return mModel->IsTextElideEnabled(); @@ -112,6 +121,46 @@ Length ViewModel::GetNumberOfGlyphs() const return 0u; } +GlyphIndex ViewModel::GetStartIndexOfElidedGlyphs() const +{ + if(mIsTextElided && mModel->IsTextElideEnabled()) + { + return mStartIndexOfElidedGlyphs; + } + + return mModel->GetStartIndexOfElidedGlyphs(); +} + +GlyphIndex ViewModel::GetEndIndexOfElidedGlyphs() const +{ + if(mIsTextElided && mModel->IsTextElideEnabled()) + { + return mEndIndexOfElidedGlyphs; + } + + return mModel->GetEndIndexOfElidedGlyphs(); +} + +GlyphIndex ViewModel::GetFirstMiddleIndexOfElidedGlyphs() const +{ + if(mIsTextElided && mModel->IsTextElideEnabled()) + { + return mFirstMiddleIndexOfElidedGlyphs; + } + + return mModel->GetFirstMiddleIndexOfElidedGlyphs(); +} + +GlyphIndex ViewModel::GetSecondMiddleIndexOfElidedGlyphs() const +{ + if(mIsTextElided && mModel->IsTextElideEnabled()) + { + return mSecondMiddleIndexOfElidedGlyphs; + } + + return mModel->GetSecondMiddleIndexOfElidedGlyphs(); +} + const GlyphInfo* const ViewModel::GetGlyphs() const { if(mIsTextElided && mModel->IsTextElideEnabled()) @@ -160,6 +209,11 @@ const ColorIndex* const ViewModel::GetBackgroundColorIndices() const return mModel->GetBackgroundColorIndices(); } +bool const ViewModel::IsMarkupBackgroundColorSet() const +{ + return mModel->IsMarkupBackgroundColorSet(); +} + const Vector4& ViewModel::GetDefaultColor() const { return mModel->GetDefaultColor(); @@ -247,157 +301,283 @@ Length ViewModel::GetHyphensCount() const void ViewModel::ElideGlyphs() { - mIsTextElided = false; + mIsTextElided = false; + mStartIndexOfElidedGlyphs = mFirstMiddleIndexOfElidedGlyphs = mSecondMiddleIndexOfElidedGlyphs = 0; + mEndIndexOfElidedGlyphs = mModel->GetNumberOfGlyphs() - 1u; - if(mModel->IsTextElideEnabled()) + auto ellipsisPosition = GetEllipsisPosition(); + + if(IsTextElideEnabled()) { const Length numberOfLines = mModel->GetNumberOfLines(); if(0u != numberOfLines) { const LineRun* const lines = mModel->GetLines(); - const LineRun& lastLine = *(lines + (numberOfLines - 1u)); - const Length numberOfLaidOutGlyphs = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs; + //Get line of ellipsis + const LineRun* ellipsisLine = nullptr; + const LineRun* ellipsisNextLine = nullptr; - if(lastLine.ellipsis && (0u != numberOfLaidOutGlyphs)) + for(Length lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { - mIsTextElided = true; - TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + const LineRun* line = (lines + lineIndex); + if(line->ellipsis) + { + ellipsisLine = line; + if(lineIndex < numberOfLines - 1u) + { + ellipsisNextLine = (lines + lineIndex + 1u); + } + break; + } + } - const GlyphInfo* const glyphs = mModel->GetGlyphs(); - const Vector2* const positions = mModel->GetLayout(); + // Check if there is a line contains Ellipsis. + // Then find total number of glyphs and total number of laid out glyphs. + // Check where to set Ellipsis glyph in line. + // Determine index of Ellipsis glyph and how many glyphs should be replaced by Ellipsis glyph, according to width of Ellipsis glyph. + if(ellipsisLine != nullptr) + { + // Total number of glyphs. + const Length numberOfGlyphs = mModel->GetNumberOfGlyphs(); + // Total number of laid out glyphs. + Length numberOfActualLaidOutGlyphs = 0u; - // Copy the glyphs to be elided. - mElidedGlyphs.Resize(numberOfLaidOutGlyphs); - mElidedLayout.Resize(numberOfLaidOutGlyphs); + // Accumulate laid out glyphs for each line to find total number of laid out glyphs. + for(Length lineIndex = 0u; lineIndex < numberOfLines; lineIndex++) + { + numberOfActualLaidOutGlyphs += lines[lineIndex].glyphRun.numberOfGlyphs + lines[lineIndex].glyphRunSecondHalf.numberOfGlyphs; + } - GlyphInfo* elidedGlyphsBuffer = mElidedGlyphs.Begin(); - Vector2* elidedPositionsBuffer = mElidedLayout.Begin(); + // Make sure there are laid out glyphs. + if(0u != numberOfActualLaidOutGlyphs) + { + // There are elided glyphs. + mIsTextElided = true; + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); - memcpy(elidedGlyphsBuffer, glyphs, numberOfLaidOutGlyphs * sizeof(GlyphInfo)); - memcpy(elidedPositionsBuffer, positions, numberOfLaidOutGlyphs * sizeof(Vector2)); + // Retrieve the whole glyphs and their positions. + const GlyphInfo* const glyphs = mModel->GetGlyphs(); + const Vector2* const positions = mModel->GetLayout(); - const Size& controlSize = mModel->GetControlSize(); + // Copy the glyphs to be elided. + mElidedGlyphs.Resize(numberOfGlyphs); + mElidedLayout.Resize(numberOfGlyphs); + GlyphInfo* elidedGlyphsBuffer = mElidedGlyphs.Begin(); + Vector2* elidedPositionsBuffer = mElidedLayout.Begin(); - if((1u == numberOfLines) && - (lastLine.ascender - lastLine.descender > controlSize.height)) - { - // Get the first glyph which is going to be replaced and the ellipsis glyph. - GlyphInfo& glyphToRemove = *elidedGlyphsBuffer; - const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId)); + memcpy(elidedGlyphsBuffer, glyphs, numberOfGlyphs * sizeof(GlyphInfo)); + memcpy(elidedPositionsBuffer, positions, numberOfGlyphs * sizeof(Vector2)); - // Change the 'x' and 'y' position of the ellipsis glyph. - Vector2& position = *elidedPositionsBuffer; + const Size& controlSize = mModel->GetControlSize(); - position.x = ellipsisGlyph.xBearing; - position.y = -lastLine.ascender + controlSize.height - ellipsisGlyph.yBearing; + // Set index where to set Ellipsis according to the selected position of Ellipsis. + // Start with this index to replace its glyph by Ellipsis, if the width is not enough, then remove more glyphs. + GlyphIndex startIndexOfEllipsis = 0u; + if(ellipsisPosition == DevelText::EllipsisPosition::START) + { + // It's the fisrt glyph in line. + startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex; + } + else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE) + { + // It's the second middle of the line in case the line split to two halves. + // Otherwise it's It's the last glyph in line (line before all removed lines). + startIndexOfEllipsis = ellipsisLine->isSplitToTwoHalves ? (ellipsisLine->glyphRunSecondHalf.glyphIndex) : (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u); + } + else // DevelText::EllipsisPosition::END + { + // It's the last glyph in line. + startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u; + } - // Replace the glyph by the ellipsis glyph and resize the buffers. - glyphToRemove = ellipsisGlyph; + // When the hight is not enough then show one glyph and that should be the first laid out glyph. + if((1u == numberOfLines) && + (ellipsisLine->ascender - ellipsisLine->descender > controlSize.height)) + { + // Replace the first glyph with ellipsis glyph + auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u; - mElidedGlyphs.Resize(1u); - mElidedLayout.Resize(1u); + // Regardless where the location of ellipsis,in-case the hight of line is greater than control's height + // then replace the first glyph with ellipsis glyph. - return; - } + // Get the first glyph which is going to be replaced and the ellipsis glyph. + GlyphInfo& glyphToRemove = *(elidedGlyphsBuffer + indexOfFirstGlyph); + const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId)); - // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed. - float firstPenX = 0.f; // Used if rtl text is elided. - float penY = 0.f; - bool firstPenSet = false; + // Change the 'x' and 'y' position of the ellipsis glyph. + Vector2& position = *(elidedPositionsBuffer + indexOfFirstGlyph); - // Add the ellipsis glyph. - bool inserted = false; - float removedGlypsWidth = 0.f; - Length numberOfRemovedGlyphs = 0u; - GlyphIndex index = numberOfLaidOutGlyphs - 1u; + position.x = ellipsisGlyph.xBearing; + position.y = -ellipsisLine->ascender + controlSize.height - ellipsisGlyph.yBearing; - // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed. - while(!inserted) - { - const GlyphInfo& glyphToRemove = *(elidedGlyphsBuffer + index); + // Replace the glyph by the ellipsis glyph and resize the buffers. + glyphToRemove = ellipsisGlyph; - if(0u != glyphToRemove.fontId) - { - // i.e. The font id of the glyph shaped from the '\n' character is zero. + mElidedGlyphs.Resize(1u); + mElidedLayout.Resize(1u); - // Need to reshape the glyph as the font may be different in size. - const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId)); + mEndIndexOfElidedGlyphs = mStartIndexOfElidedGlyphs = mFirstMiddleIndexOfElidedGlyphs = mSecondMiddleIndexOfElidedGlyphs = indexOfFirstGlyph; + + return; + } + + // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed. + float firstPenX = 0.f; // Used if rtl text is elided. + float penY = 0.f; + bool firstPenSet = false; + + // Add the ellipsis glyph. + bool inserted = false; + float removedGlypsWidth = 0.f; + Length numberOfRemovedGlyphs = 0u; + GlyphIndex indexOfEllipsis = startIndexOfEllipsis; - if(!firstPenSet || EqualsZero(glyphToRemove.advance)) + // Tail Mode: start by the end of line. + bool isTailMode = (ellipsisPosition == DevelText::EllipsisPosition::END) || + (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && numberOfLines != 1u); + + // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed. + while(!inserted) + { + const GlyphInfo& glyphToRemove = *(elidedGlyphsBuffer + indexOfEllipsis); + + if(0u != glyphToRemove.fontId) { - const Vector2& position = *(elidedPositionsBuffer + index); + // i.e. The font id of the glyph shaped from the '\n' character is zero. - // Calculates the penY of the current line. It will be used to position the ellipsis glyph. - penY = position.y + glyphToRemove.yBearing; + // Need to reshape the glyph as the font may be different in size. + const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId)); - // Calculates the first penX which will be used if rtl text is elided. - firstPenX = position.x - glyphToRemove.xBearing; - if(firstPenX < -ellipsisGlyph.xBearing) + if(!firstPenSet || EqualsZero(glyphToRemove.advance)) { - // Avoids to exceed the bounding box when rtl text is elided. - firstPenX = -ellipsisGlyph.xBearing; - } + const Vector2& position = *(elidedPositionsBuffer + indexOfEllipsis); - removedGlypsWidth = -ellipsisGlyph.xBearing; + // Calculates the penY of the current line. It will be used to position the ellipsis glyph. + penY = position.y + glyphToRemove.yBearing; + + // Calculates the first penX which will be used if rtl text is elided. + firstPenX = position.x - glyphToRemove.xBearing; + if(firstPenX < -ellipsisGlyph.xBearing) + { + // Avoids to exceed the bounding box when rtl text is elided. + firstPenX = -ellipsisGlyph.xBearing; + } + + removedGlypsWidth = -ellipsisGlyph.xBearing; - if(!EqualsZero(firstPenX)) - { firstPenSet = true; } - } - removedGlypsWidth += std::min(glyphToRemove.advance, (glyphToRemove.xBearing + glyphToRemove.width)); + removedGlypsWidth += std::min(glyphToRemove.advance, (glyphToRemove.xBearing + glyphToRemove.width)); - // Calculate the width of the ellipsis glyph and check if it fits. - const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing; + // Calculate the width of the ellipsis glyph and check if it fits. + const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing; - // If it is the last glyph to remove, add the ellipsis glyph without checking its width. - if((ellipsisGlyphWidth < removedGlypsWidth) || (index == 0u)) - { - GlyphInfo& glyphInfo = *(elidedGlyphsBuffer + index); - Vector2& position = *(elidedPositionsBuffer + index); - position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f; + // If it is the last glyph to remove, add the ellipsis glyph without checking its width. + if((ellipsisGlyphWidth < removedGlypsWidth) || (isTailMode ? (indexOfEllipsis == 0u) : (indexOfEllipsis == numberOfGlyphs - 1u))) + { + GlyphInfo& glyphInfo = *(elidedGlyphsBuffer + indexOfEllipsis); + Vector2& position = *(elidedPositionsBuffer + indexOfEllipsis); + position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f; - // Replace the glyph by the ellipsis glyph. - glyphInfo = ellipsisGlyph; + // Replace the glyph by the ellipsis glyph. + glyphInfo = ellipsisGlyph; - // Change the 'x' and 'y' position of the ellipsis glyph. - if(position.x > firstPenX) - { - position.x = firstPenX; - if(ellipsisGlyphWidth < removedGlypsWidth) + // Change the 'x' and 'y' position of the ellipsis glyph. + if(position.x > firstPenX) { - position.x += removedGlypsWidth - ellipsisGlyphWidth; + position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth; } - } - position.x += ellipsisGlyph.xBearing; - position.y = penY - ellipsisGlyph.yBearing; + position.x += ellipsisGlyph.xBearing; + position.y = penY - ellipsisGlyph.yBearing; - inserted = true; + inserted = true; + } } - } - if(!inserted) + if(!inserted) + { + if(!isTailMode && indexOfEllipsis < numberOfGlyphs - 1u) + { + // Tail Mode: remove glyphs from startIndexOfEllipsis then decrement indexOfEllipsis, until arrive to index zero. + ++indexOfEllipsis; + } + else if(isTailMode && indexOfEllipsis > 0u) + { + // Not Tail Mode: remove glyphs from startIndexOfEllipsis then increase indexOfEllipsis, until arrive to last index (numberOfGlyphs - 1u). + --indexOfEllipsis; + } + else + { + // No space for the ellipsis. + inserted = true; + } + ++numberOfRemovedGlyphs; + } + } // while( !inserted ) + + //Reduce size, shift glyphs and start from ellipsis glyph + Length numberOfElidedGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs; + mElidedGlyphs.Resize(numberOfElidedGlyphs); + mElidedLayout.Resize(numberOfElidedGlyphs); + + if(ellipsisPosition == DevelText::EllipsisPosition::START) { - if(index > 0u) + // 'Shifts' glyphs after ellipsis glyph and 'Removes' before ellipsis glyph + memcpy(elidedGlyphsBuffer, elidedGlyphsBuffer + indexOfEllipsis, numberOfElidedGlyphs * sizeof(GlyphInfo)); + memcpy(elidedPositionsBuffer, elidedPositionsBuffer + indexOfEllipsis, numberOfElidedGlyphs * sizeof(Vector2)); + + mStartIndexOfElidedGlyphs = mFirstMiddleIndexOfElidedGlyphs = mSecondMiddleIndexOfElidedGlyphs = indexOfEllipsis; + } + else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE) + { + // 'Shifts and connects' glyphs before and after ellipsis glyph and 'Removes' in-between. + bool isOnlySecondHalf = false; + + if(isTailMode) + { + mFirstMiddleIndexOfElidedGlyphs = indexOfEllipsis; + if(ellipsisNextLine != nullptr) + { + mSecondMiddleIndexOfElidedGlyphs = ellipsisNextLine->glyphRun.glyphIndex; + } + else + { + mEndIndexOfElidedGlyphs = mSecondMiddleIndexOfElidedGlyphs = mFirstMiddleIndexOfElidedGlyphs; + } + } + else { - --index; + mFirstMiddleIndexOfElidedGlyphs = (ellipsisLine->glyphRun.numberOfGlyphs > 0u) ? (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u) : (ellipsisLine->glyphRun.glyphIndex); + mSecondMiddleIndexOfElidedGlyphs = indexOfEllipsis; + isOnlySecondHalf = ellipsisLine->glyphRun.numberOfGlyphs == 0u && ellipsisLine->glyphRunSecondHalf.numberOfGlyphs > 0u; + } + + if(isOnlySecondHalf) + { + Length numberOfSecondHalfGlyphs = numberOfElidedGlyphs - mFirstMiddleIndexOfElidedGlyphs; + + //Copy elided glyphs after the ellipsis glyph. + memcpy(elidedGlyphsBuffer + mFirstMiddleIndexOfElidedGlyphs, elidedGlyphsBuffer + mSecondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo)); + memcpy(elidedPositionsBuffer + mFirstMiddleIndexOfElidedGlyphs, elidedPositionsBuffer + mSecondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2)); } else { - // No space for the ellipsis. - inserted = true; + Length numberOfSecondHalfGlyphs = numberOfElidedGlyphs - mFirstMiddleIndexOfElidedGlyphs + 1u; + + //Copy elided glyphs after the ellipsis glyph. + memcpy(elidedGlyphsBuffer + mFirstMiddleIndexOfElidedGlyphs + 1u, elidedGlyphsBuffer + mSecondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo)); + memcpy(elidedPositionsBuffer + mFirstMiddleIndexOfElidedGlyphs + 1u, elidedPositionsBuffer + mSecondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2)); } - ++numberOfRemovedGlyphs; } - } // while( !inserted ) - - // 'Removes' all the glyphs after the ellipsis glyph. - const Length numberOfGlyphs = numberOfLaidOutGlyphs - numberOfRemovedGlyphs; - mElidedGlyphs.Resize(numberOfGlyphs); - mElidedLayout.Resize(numberOfGlyphs); + else // DevelText::EllipsisPosition::END + { + // 'Removes' all the glyphs after the ellipsis glyph. + mEndIndexOfElidedGlyphs = indexOfEllipsis; + } + } } } }