+ // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
+ wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
+
+ SetWordBreakInfo( utf32Characters,
+ startIndex,
+ requestedNumberOfCharacters,
+ wordBreakInfo );
+ updated = true;
+ }
+
+ const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
+ const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
+
+ Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
+ Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
+
+ if( getScripts || validateFonts )
+ {
+ // Validates the fonts assigned by the application or assigns default ones.
+ // It makes sure all the characters are going to be rendered by the correct font.
+ MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
+
+ if( getScripts )
+ {
+ // Retrieves the scripts used in the text.
+ multilanguageSupport.SetScripts( utf32Characters,
+ startIndex,
+ requestedNumberOfCharacters,
+ scripts );
+ }
+
+ if( validateFonts )
+ {
+ // Validate the fonts set through the mark-up string.
+ Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
+
+ // Get the default font's description.
+ TextAbstraction::FontDescription defaultFontDescription;
+ TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
+
+ if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
+ {
+ // If the placeholder font is set specifically, only placeholder font is changed.
+ defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
+ if( mEventData->mPlaceholderFont->sizeDefined )
+ {
+ defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
+ }
+ }
+ else if( NULL != mFontDefaults )
+ {
+ // Set the normal font and the placeholder font.
+ defaultFontDescription = mFontDefaults->mFontDescription;
+ defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
+ }
+
+ // Validates the fonts. If there is a character with no assigned font it sets a default one.
+ // After this call, fonts are validated.
+ multilanguageSupport.ValidateFonts( utf32Characters,
+ scripts,
+ fontDescriptionRuns,
+ defaultFontDescription,
+ defaultPointSize,
+ startIndex,
+ requestedNumberOfCharacters,
+ validFonts );
+ }
+ updated = true;
+ }
+
+ Vector<Character> mirroredUtf32Characters;
+ bool textMirrored = false;
+ const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
+ if( NO_OPERATION != ( BIDI_INFO & operations ) )
+ {
+ Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
+ bidirectionalInfo.Reserve( numberOfParagraphs );
+
+ // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
+ SetBidirectionalInfo( utf32Characters,
+ scripts,
+ lineBreakInfo,
+ startIndex,
+ requestedNumberOfCharacters,
+ bidirectionalInfo,
+ mModel->mMatchSystemLanguageDirection,
+ mLayoutDirection );
+
+ if( 0u != bidirectionalInfo.Count() )
+ {
+ // Only set the character directions if there is right to left characters.
+ Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
+ GetCharactersDirection( bidirectionalInfo,
+ numberOfCharacters,
+ startIndex,
+ requestedNumberOfCharacters,
+ directions );
+
+ // This paragraph has right to left text. Some characters may need to be mirrored.
+ // TODO: consider if the mirrored string can be stored as well.
+
+ textMirrored = GetMirroredText( utf32Characters,
+ directions,
+ bidirectionalInfo,
+ startIndex,
+ requestedNumberOfCharacters,
+ mirroredUtf32Characters );
+ }
+ else
+ {
+ // There is no right to left characters. Clear the directions vector.
+ mModel->mLogicalModel->mCharacterDirections.Clear();
+ }
+ updated = true;
+ }
+
+ Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
+ Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
+ Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
+ Vector<GlyphIndex> newParagraphGlyphs;
+ newParagraphGlyphs.Reserve( numberOfParagraphs );
+
+ const Length currentNumberOfGlyphs = glyphs.Count();
+ if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
+ {
+ const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
+ // Shapes the text.
+ ShapeText( textToShape,
+ lineBreakInfo,
+ scripts,
+ validFonts,
+ startIndex,
+ mTextUpdateInfo.mStartGlyphIndex,
+ requestedNumberOfCharacters,
+ glyphs,
+ glyphsToCharactersMap,
+ charactersPerGlyph,
+ newParagraphGlyphs );
+
+ // Create the 'number of glyphs' per character and the glyph to character conversion tables.
+ mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
+ mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
+ updated = true;
+ }
+
+ const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
+
+ if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
+ {
+ 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 )
+ {
+ const GlyphIndex index = *it;
+ GlyphInfo& glyph = *( glyphsBuffer + index );
+
+ glyph.xBearing = 0.f;
+ glyph.width = 0.f;
+ glyph.advance = 0.f;
+ }
+ updated = true;
+ }
+
+ if( NO_OPERATION != ( COLOR & operations ) )
+ {
+ // Set the color runs in glyphs.
+ SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
+ mModel->mVisualModel->mCharactersToGlyph,
+ mModel->mVisualModel->mGlyphsPerCharacter,
+ startIndex,
+ mTextUpdateInfo.mStartGlyphIndex,
+ requestedNumberOfCharacters,
+ mModel->mVisualModel->mColors,
+ mModel->mVisualModel->mColorIndices );
+
+ updated = true;
+ }
+
+ if( ( NULL != mEventData ) &&
+ mEventData->mPreEditFlag &&
+ ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
+ {
+ // Add the underline for the pre-edit text.
+ const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
+ const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
+
+ const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
+ const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
+ const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
+ const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
+
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = glyphStart;
+ underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
+
+ // TODO: At the moment the underline runs are only for pre-edit.
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ }
+
+ // The estimated number of lines. Used to avoid reallocations when layouting.
+ mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
+
+ // Set the previous number of characters for the next time the text is updated.
+ mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
+
+ return updated;
+}
+
+void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
+{
+ // Sets the default text's color.
+ inputStyle.textColor = mTextColor;
+ inputStyle.isDefaultColor = true;
+
+ inputStyle.familyName.clear();
+ inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
+ inputStyle.width = TextAbstraction::FontWidth::NORMAL;
+ inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
+ inputStyle.size = 0.f;
+
+ inputStyle.lineSpacing = 0.f;
+
+ inputStyle.underlineProperties.clear();
+ inputStyle.shadowProperties.clear();
+ inputStyle.embossProperties.clear();
+ inputStyle.outlineProperties.clear();
+
+ inputStyle.isFamilyDefined = false;
+ inputStyle.isWeightDefined = false;
+ inputStyle.isWidthDefined = false;
+ inputStyle.isSlantDefined = false;
+ inputStyle.isSizeDefined = false;
+
+ inputStyle.isLineSpacingDefined = false;
+
+ inputStyle.isUnderlineDefined = false;
+ inputStyle.isShadowDefined = false;
+ inputStyle.isEmbossDefined = false;
+ inputStyle.isOutlineDefined = false;
+
+ // Sets the default font's family name, weight, width, slant and size.
+ if( mFontDefaults )
+ {
+ if( mFontDefaults->familyDefined )
+ {
+ inputStyle.familyName = mFontDefaults->mFontDescription.family;
+ inputStyle.isFamilyDefined = true;
+ }
+
+ if( mFontDefaults->weightDefined )
+ {
+ inputStyle.weight = mFontDefaults->mFontDescription.weight;
+ inputStyle.isWeightDefined = true;
+ }
+
+ if( mFontDefaults->widthDefined )
+ {
+ inputStyle.width = mFontDefaults->mFontDescription.width;
+ inputStyle.isWidthDefined = true;
+ }
+
+ if( mFontDefaults->slantDefined )
+ {
+ inputStyle.slant = mFontDefaults->mFontDescription.slant;
+ inputStyle.isSlantDefined = true;
+ }
+
+ if( mFontDefaults->sizeDefined )
+ {
+ inputStyle.size = mFontDefaults->mDefaultPointSize;
+ inputStyle.isSizeDefined = true;
+ }
+ }
+}
+
+float Controller::Impl::GetDefaultFontLineHeight()
+{
+ FontId defaultFontId = 0u;
+ if( NULL == mFontDefaults )
+ {
+ TextAbstraction::FontDescription fontDescription;
+ defaultFontId = mFontClient.GetFontId( fontDescription );
+ }
+ else
+ {
+ defaultFontId = mFontDefaults->GetFontId( mFontClient );
+ }
+
+ Text::FontMetrics fontMetrics;
+ mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
+
+ return( fontMetrics.ascender - fontMetrics.descender );
+}
+
+void Controller::Impl::OnCursorKeyEvent( const Event& event )
+{
+ if( NULL == mEventData || !IsShowingRealText() )
+ {
+ // Nothing to do if there is no text input.
+ return;
+ }
+
+ int keyCode = event.p1.mInt;
+ bool isShiftModifier = event.p2.mBool;
+
+ CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
+
+ if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
+ {
+ if( mEventData->mPrimaryCursorPosition > 0u )
+ {
+ if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
+ {
+ mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
+ else
+ {
+ mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
+ }
+ }
+ }
+ else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
+ {
+ if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
+ {
+ if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
+ {
+ mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
+ else
+ {
+ mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
+ }
+ }
+ }
+ else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
+ {
+ // Ignore Shift-Up for text selection for now.
+
+ // Get first the line index of the current cursor position index.
+ CharacterIndex characterIndex = 0u;
+
+ if( mEventData->mPrimaryCursorPosition > 0u )
+ {
+ characterIndex = mEventData->mPrimaryCursorPosition - 1u;
+ }
+
+ const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
+ const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
+
+ // Retrieve the cursor position info.
+ CursorInfo cursorInfo;
+ GetCursorPosition( mEventData->mPrimaryCursorPosition,
+ cursorInfo );
+
+ // Get the line above.
+ const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
+
+ // Get the next hit 'y' point.
+ const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
+
+ // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+ bool matchedCharacter = false;
+ mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
+ mModel->mLogicalModel,
+ mMetrics,
+ mEventData->mCursorHookPositionX,
+ hitPointY,
+ CharacterHitTest::TAP,
+ matchedCharacter );
+ }
+ else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
+ {
+ // Ignore Shift-Down for text selection for now.
+
+ // Get first the line index of the current cursor position index.
+ CharacterIndex characterIndex = 0u;
+
+ if( mEventData->mPrimaryCursorPosition > 0u )
+ {
+ characterIndex = mEventData->mPrimaryCursorPosition - 1u;
+ }
+
+ const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
+
+ if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
+ {
+ // Retrieve the cursor position info.
+ CursorInfo cursorInfo;
+ GetCursorPosition( mEventData->mPrimaryCursorPosition,
+ cursorInfo );
+
+ // Get the line below.
+ const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
+
+ // Get the next hit 'y' point.
+ const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
+
+ // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+ bool matchedCharacter = false;
+ mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
+ mModel->mLogicalModel,
+ mMetrics,
+ mEventData->mCursorHookPositionX,
+ hitPointY,
+ CharacterHitTest::TAP,
+ matchedCharacter );
+ }
+ }
+
+ if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
+ {
+ // Update selection position after moving the cursor
+ mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
+ mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
+ }
+
+ if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
+ {
+ // Handle text selection
+ bool selecting = false;
+
+ if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
+ {
+ // Shift-Left/Right to select the text
+ int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
+ if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
+ {
+ mEventData->mRightSelectionPosition += cursorPositionDelta;
+ }
+ selecting = true;
+ }
+ else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
+ {
+ // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
+ selecting = true;
+ }
+
+ if ( selecting )
+ {
+ // Notify the cursor position to the InputMethodContext.
+ if( mEventData->mInputMethodContext )
+ {
+ mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
+ mEventData->mInputMethodContext.NotifyCursorPosition();
+ }
+
+ ChangeState( EventData::SELECTING );
+
+ mEventData->mUpdateLeftSelectionPosition = true;
+ mEventData->mUpdateRightSelectionPosition = true;
+ mEventData->mUpdateGrabHandlePosition = true;
+ mEventData->mUpdateHighlightBox = true;
+
+ // Hide the text selection popup if select the text using keyboard instead of moving grab handles
+ if( mEventData->mGrabHandlePopupEnabled )
+ {
+ mEventData->mDecorator->SetPopupActive( false );
+ }
+ }
+ }
+ else
+ {
+ // Handle normal cursor move
+ ChangeState( EventData::EDITING );
+ mEventData->mUpdateCursorPosition = true;