#include <dali-toolkit/internal/text/segmentation.h>
#include <dali-toolkit/internal/text/shaper.h>
#include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/markup-processor.h>
namespace Dali
{
Size& layoutSize,
LogicalModelPtr& logicalModel,
VisualModelPtr& visualModel,
- MetricsPtr& metrics )
+ MetricsPtr& metrics,
+ bool markupProcessorEnabled )
{
logicalModel = LogicalModel::New();
visualModel = VisualModel::New();
+ MarkupProcessData markupProcessData( logicalModel->mColorRuns,
+ logicalModel->mFontDescriptionRuns );
+
+ Length textSize = 0u;
+ const uint8_t* utf8 = NULL;
+ if( markupProcessorEnabled )
+ {
+ ProcessMarkupString( text, markupProcessData );
+ textSize = markupProcessData.markupProcessedText.size();
+
+ // This is a bit horrible but std::string returns a (signed) char*
+ utf8 = reinterpret_cast<const uint8_t*>( markupProcessData.markupProcessedText.c_str() );
+ }
+ else
+ {
+ textSize = text.size();
+
+ // This is a bit horrible but std::string returns a (signed) char*
+ utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
+ }
+
// 1) Convert to utf32
Vector<Character>& utf32Characters = logicalModel->mText;
- utf32Characters.Resize( text.size() );
+ utf32Characters.Resize( textSize );
- const uint32_t numberOfCharacters = ( text.size() == 0) ? 0 :
- Utf8ToUtf32( reinterpret_cast<const uint8_t* const>( text.c_str() ),
- text.size(),
- &utf32Characters[0u] );
- utf32Characters.Resize( numberOfCharacters );
+ // Transform a text array encoded in utf8 into an array encoded in utf32.
+ // It returns the actual number of characters.
+ Length characterCount = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
+ utf32Characters.Resize( characterCount );
// 2) Set the break and paragraph info.
Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
- lineBreakInfo.Resize( numberOfCharacters );
+ lineBreakInfo.Resize( characterCount );
SetLineBreakInfo( utf32Characters,
0u,
- numberOfCharacters,
+ characterCount,
lineBreakInfo );
- if( 0u == numberOfCharacters )
+ if( 0u == characterCount )
{
// Nothing else to do if the number of characters is zero.
return;
// Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
Vector<WordBreakInfo>& wordBreakInfo = logicalModel->mWordBreakInfo;
- wordBreakInfo.Resize( numberOfCharacters );
+ wordBreakInfo.Resize( characterCount );
SetWordBreakInfo( utf32Characters,
0u,
- numberOfCharacters,
+ characterCount,
wordBreakInfo );
// 3) Set the script info.
Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
multilanguageSupport.SetScripts( utf32Characters,
0u,
- numberOfCharacters,
+ characterCount,
scripts );
// 4) Set the font info
fontDescription,
TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
0u,
- numberOfCharacters,
+ characterCount,
validFonts );
// 5) Set the bidirectional info per paragraph.
scripts,
lineBreakInfo,
0u,
- numberOfCharacters,
+ characterCount,
bidirectionalInfo );
// Create the paragraph info.
logicalModel->CreateParagraphInfo( 0u,
- numberOfCharacters );
+ characterCount );
// 6) Set character directions.
Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
{
// Only set the character directions if there is right to left characters.
GetCharactersDirection( bidirectionalInfo,
- numberOfCharacters,
+ characterCount,
0u,
- numberOfCharacters,
+ characterCount,
characterDirections );
characterDirections,
bidirectionalInfo,
0u,
- numberOfCharacters,
+ characterCount,
mirroredUtf32Characters );
}
else
validFonts,
0u,
0u,
- numberOfCharacters,
+ characterCount,
glyphs,
glyphsToCharactersMap,
charactersPerGlyph,
newParagraphGlyphs );
// Create the 'number of glyphs' per character and the glyph to character conversion tables.
- visualModel->CreateGlyphsPerCharacterTable( 0u, 0u, numberOfCharacters );
- visualModel->CreateCharacterToGlyphTable( 0u, 0u, numberOfCharacters );
+ visualModel->CreateGlyphsPerCharacterTable( 0u, 0u, characterCount );
+ visualModel->CreateCharacterToGlyphTable( 0u, 0u, characterCount );
const Length numberOfGlyphs = glyphs.Count();
numberOfGlyphs,
Text::HorizontalAlignment::BEGIN,
Text::LineWrap::WORD,
- outlineWidth );
+ outlineWidth,
+ true );
Vector<LineRun>& lines = visualModel->mLines;
Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
glyphPositions.Resize( numberOfGlyphs );
- layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( utf32Characters.Begin() + ( numberOfCharacters - 1u ) ) );
+ layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( utf32Characters.Begin() + ( characterCount - 1u ) ) );
// The initial glyph and the number of glyphs to layout.
layoutParameters.startGlyphIndex = 0u;
bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
ReorderLines( bidirectionalInfo,
0u,
- numberOfCharacters,
+ characterCount,
lines,
bidirectionalLineInfo );
// Re-layout the text. Reorder those lines with right to left characters.
layoutEngine.ReLayoutRightToLeftLines( layoutParameters,
0u,
- numberOfCharacters,
+ characterCount,
glyphPositions );
}
}
float alignmentOffset = 0.f;
layoutEngine.Align( textArea,
0u,
- numberOfCharacters,
+ characterCount,
Text::HorizontalAlignment::BEGIN,
lines,
alignmentOffset );
* @param[out] logicalModel Pointer to a logical text model instance.
* @param[out] visualModel Pointer to a visual text model instance.
* @param[out] metrics Pointer to a wrapper around FontClient used to get metrics.
+ * @param[in] markupProcessorEnabled Enable markup processor to use markup text.
*/
void CreateTextModel( const std::string& text,
const Size& textArea,
Size& layoutSize,
LogicalModelPtr& logicalModel,
VisualModelPtr& visualModel,
- MetricsPtr& metrics );
+ MetricsPtr& metrics,
+ bool markupProcessorEnabled );
/**
* @brief Configures the text @p controller similarly to the one configured by the text-label.
struct GetCharactersDirectionData
{
- std::string description; ///< Description of the test.
- std::string text; ///< Input text.
- unsigned int startIndex; ///< The index from where the model is updated.
- unsigned int numberOfCharacters; ///< The number of characters.
- bool* directions; ///< The expected directions.
+ std::string description; ///< Description of the test.
+ std::string text; ///< Input text.
+ unsigned int startIndex; ///< The index from where the model is updated.
+ unsigned int numberOfCharacters; ///< The number of characters.
+ bool* directions; ///< The expected directions.
+ bool markupProcessorEnabled; ///< Enable markup processor to use markup text.
};
bool SetBidirectionalInfoTest( const SetBidirectionalInfoData& data )
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Clear the bidirectional paragraph info data.
Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Clear the bidirectional line info data.
uint32_t startRemoveIndex = logicalModel->mBidirectionalLineInfo.Count();
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Call the GetMirroredText() function for the whole text
Vector<Character> mirroredText;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ data.markupProcessorEnabled );
Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
true, true, true, true, true, true, true, true, true, true,
true, true, false, false, false, false, false };
+ bool directions06[] = {
+ true, true, true, true, true, true, true, true, true, true,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false };
+
struct GetCharactersDirectionData data[] =
{
{
"",
0u,
0u,
- directions01
+ directions01,
+ false
},
{
"Left to right characters only",
"Hello world\nhello world demo",
0u,
28u,
- directions02
+ directions02,
+ false
},
{
"Right to left characters only",
"שלום עולם\nשלום עולם",
0u,
19u,
- directions03
+ directions03,
+ false
},
{
"Mix of bidirectional text",
"Hello world\nhello world שלום עולם\nשלום עולם hello world",
0u,
55u,
- directions04
+ directions04,
+ false
},
{
"Mix of bidirectional text. With more paragraphs.",
" مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello",
0u,
227u,
- directions05
+ directions05,
+ false
},
{
"Mix of bidirectional text. With more paragraphs. Update first paragraph.",
" مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello",
0u,
17u,
- directions05
+ directions05,
+ false
},
{
"Mix of bidirectional text. With more paragraphs. Update from character 29",
" مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello",
29u,
134u,
- directions05
+ directions05,
+ false
},
{
"Mix of bidirectional text. With more paragraphs. Update from character 163",
" مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello",
163u,
35u,
- directions05
+ directions05,
+ false
+ },
+ {
+ "Mix of bidirectional text. With brackets and LRM",
+ "שלום עולם ‎(hello)[world]‎",
+ 0u,
+ 26u,
+ directions06,
+ true
}
};
- const unsigned int numberOfTests = 8u;
+ const unsigned int numberOfTests = 9u;
for( unsigned int index = 0u; index < numberOfTests; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Clear the paragraphs.
Vector<ParagraphRun>& paragraphs = logicalModel->mParagraphInfo;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Find the paragraphs.
Vector<ParagraphRunIndex> paragraphs;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0; index < data.numberOfTests; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0u; index < data.numberOfIndices; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0u; index < data.numberOfIndices; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0; index < data.numberOfTests; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0; index < data.numberOfTests; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
GetCursorPositionParameters parameters;
parameters.visualModel = visualModel;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
for( unsigned int index = 0; index < data.numberOfTests; ++index )
{
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Clear the layout.
Vector<LineRun>& lines = visualModel->mLines;
totalNumberOfGlyphs,
Text::HorizontalAlignment::BEGIN,
Text::LineWrap::WORD,
- outlineWidth );
+ outlineWidth,
+ true );
layoutParameters.isLastNewParagraph = isLastNewParagraph;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Call the ReLayoutRightToLeftLines() method.
Layout::Engine engine;
visualModel->mGlyphs.Count(),
Text::HorizontalAlignment::BEGIN,
Text::LineWrap::WORD,
- outlineWidth );
+ outlineWidth,
+ true );
layoutParameters.numberOfBidirectionalInfoRuns = logicalModel->mBidirectionalLineInfo.Count();
layoutParameters.lineBidirectionalInfoRunsBuffer = logicalModel->mBidirectionalLineInfo.Begin();
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// Call the Align method.
Layout::Engine engine;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
// 2) Clear the model.
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
Vector<GlyphIndex>& charactersToGlyph = visualModel->mCharactersToGlyph;
Vector<Length>& glyphsPerCharacter = visualModel->mGlyphsPerCharacter;
layoutSize,
logicalModel,
visualModel,
- metrics );
+ metrics,
+ false );
Vector<GlyphIndex>& charactersToGlyph = visualModel->mCharactersToGlyph;
Vector<Length>& glyphsPerCharacter = visualModel->mGlyphsPerCharacter;
tet_result(TET_FAIL);
}
+ label.SetProperty( TextLabel::Property::TEXT, "Hello world " );
+ label.SetProperty( DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT, false );
+ label.SetSize( 400.0f, 10.f );
+
+ try
+ {
+ // Render the text.
+ application.SendNotification();
+ application.Render();
+ }
+ catch( ... )
+ {
+ tet_result(TET_FAIL);
+ }
+
END_TEST;
}
* | color | VECTOR4 | No | The color of the background (the default value is Color::CYAN) |
*/
BACKGROUND,
+
+ /**
+ * @brief Ignore spaces after text.
+ * @details Name "ignoreSpacesAfterText", type (Property::BOLEAN), Read/Write
+ * @note The default value is true
+ */
+ IGNORE_SPACES_AFTER_TEXT,
};
} // namespace Property
DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextLabel, "textDirection", INTEGER, TEXT_DIRECTION )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "verticalLineAlignment", INTEGER, VERTICAL_LINE_ALIGNMENT )
DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "textBackground", MAP, BACKGROUND )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextLabel, "ignoreSpacesAfterText", BOOLEAN, IGNORE_SPACES_AFTER_TEXT )
DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, TextLabel, "textColor", Color::BLACK, TEXT_COLOR )
DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorRed", TEXT_COLOR_RED, TEXT_COLOR, 0 )
DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR, 1 )
}
break;
}
+ case Toolkit::DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT:
+ {
+ impl.mController->SetIgnoreSpacesAfterText(value.Get< bool >());
+ break;
+ }
}
// Request relayout when text update is needed. It's necessary to call it
GetBackgroundProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
break;
}
+ case Toolkit::DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT:
+ {
+ value = impl.mController->IsIgnoreSpacesAfterText();
+ break;
+ }
}
}
// Get the line break info for the current character.
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 );
-
// Increase the number of characters.
tmpLineLayout.numberOfCharacters += charactersPerGlyph;
tmpLineLayout.wsLengthEndOfLine = 0.f;
}
+ // If calculation is end but wsLengthEndOfLine is exist, it means end of text is space.
+ // Merge remained length.
+ if ( !parameters.ignoreSpaceAfterText && glyphIndex == lastGlyphOfParagraphPlusOne-1 && tmpLineLayout.wsLengthEndOfLine > 0 )
+ {
+ tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine;
+ tmpLineLayout.wsLengthEndOfLine = 0u;
+ }
+
// Save the current extra width to compare with the next one
mPreviousCharacterExtraWidth = tmpExtraWidth;
// Check if the accumulated length fits in the width of the box.
- if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
+ if( ( completelyFill || isMultiline ) && !(parameters.ignoreSpaceAfterText && isWhiteSpace) &&
( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
{
// Current word does not fit in the box's width.
}
if( isMultiline &&
- ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
+ ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
{
oneWordLaidOut = isWordLaidOut;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
Length totalNumberOfGlyphs,
Text::HorizontalAlignment::Type horizontalAlignment,
Text::LineWrap::Mode lineWrapMode,
- float outlineWidth )
+ float outlineWidth,
+ bool ignoreSpaceAfterText )
: boundingBox( boundingBox ),
textBuffer( textBuffer ),
lineBreakInfoBuffer( lineBreakInfoBuffer ),
startLineIndex( 0u ),
estimatedNumberOfLines( 0u ),
lineWrapMode( lineWrapMode ),
+ outlineWidth( outlineWidth ),
isLastNewParagraph( false ),
- outlineWidth( outlineWidth )
+ ignoreSpaceAfterText( ignoreSpaceAfterText )
{}
Vector2 boundingBox; ///< The size of the box containing the text.
LineIndex startLineIndex; ///< The line index where to insert the new lines.
Length estimatedNumberOfLines; ///< The estimated number of lines.
Text::LineWrap::Mode lineWrapMode; ///< The line wrap mode for moving to next line.
- bool isLastNewParagraph; ///< Whether the last character is a new paragraph character.
float outlineWidth; ///< The outline width.
+ bool isLastNewParagraph:1; ///< Whether the last character is a new paragraph character.
+ bool ignoreSpaceAfterText:1; ///< Whether ignoring spaces after text or not. Default is true.
};
} // namespace Layout
return mImpl->mModel->mVerticalAlignment;
}
+bool Controller::IsIgnoreSpacesAfterText() const
+{
+ return mImpl->mModel->mIgnoreSpacesAfterText;
+}
+
+void Controller::SetIgnoreSpacesAfterText( bool ignore )
+{
+ mImpl->mModel->mIgnoreSpacesAfterText = ignore;
+}
+
void Controller::SetLineWrapMode( Text::LineWrap::Mode lineWrapMode )
{
if( lineWrapMode != mImpl->mModel->mLineWrapMode )
totalNumberOfGlyphs,
mImpl->mModel->mHorizontalAlignment,
mImpl->mModel->mLineWrapMode,
- outlineWidth );
+ outlineWidth,
+ mImpl->mModel->mIgnoreSpacesAfterText );
// Resize the vector of positions to have the same size than the vector of glyphs.
Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
*/
void SetVerticalLineAlignment( Toolkit::DevelText::VerticalLineAlignment::Type alignment );
+ /**
+ * @brief Retrieves ignoreSpaceAfterText value from model
+ * @return The value of ignoreSpaceAfterText
+ */
+ bool IsIgnoreSpacesAfterText() const;
+
+ /**
+ * @brief Sets ignoreSpaceAfterText value to model
+ * @param[in] ignore The value of ignoreSpacesAfterText for the text
+ */
+ void SetIgnoreSpacesAfterText( bool ignore );
+
public: // Relayout.
/**
mVerticalLineAlignment( DevelText::VerticalLineAlignment::TOP ),
mLineWrapMode( Text::LineWrap::WORD ),
mAlignmentOffset( 0.0f ),
- mElideEnabled( false )
+ mElideEnabled( false ),
+ mIgnoreSpacesAfterText( true )
{
mLogicalModel = LogicalModel::New();
mVisualModel = VisualModel::New();
* 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
* Typically this will have a negative value with scrolling occurs.
*/
- Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
- Vector2 mScrollPositionLast; ///< The last offset value of mScrollPosition
- HorizontalAlignment::Type mHorizontalAlignment; ///< The layout's horizontal alignment.
- VerticalAlignment::Type mVerticalAlignment; ///< The layout's vertical alignment.
- DevelText::VerticalLineAlignment::Type mVerticalLineAlignment; ///< The layout's vertical line alignment.
- Text::LineWrap::Mode mLineWrapMode; ///< The text wrap mode
- float mAlignmentOffset; ///< The alignment offset.
- bool mElideEnabled:1; ///< Whether the text's elide is enabled.
+ Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
+ Vector2 mScrollPositionLast; ///< The last offset value of mScrollPosition
+ HorizontalAlignment::Type mHorizontalAlignment; ///< The layout's horizontal alignment.
+ VerticalAlignment::Type mVerticalAlignment; ///< The layout's vertical alignment.
+ DevelText::VerticalLineAlignment::Type mVerticalLineAlignment; ///< The layout's vertical line alignment.
+ Text::LineWrap::Mode mLineWrapMode; ///< The text wrap mode
+ float mAlignmentOffset; ///< The alignment offset.
+ bool mElideEnabled:1; ///< Whether the text's elide is enabled.
+ bool mIgnoreSpacesAfterText:1; ///< Whether ignoring spaces after text or not. Default is true.
};
} // namespace Text
"enableAutoScroll":false,
"autoScrollLoopCount":2,
"autoScrollGap":50,
- "autoScrollSpeed":80
+ "autoScrollSpeed":80,
+ "ignoreSpacesAfterText":false
},
"TextLabelFontSize0":