2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/rendering/renderer.h>
23 #include <dali/integration-api/debug.h>
26 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
36 #include <dali-toolkit/internal/text/text-run-container.h>
37 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
38 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
49 struct BackgroundVertex
51 Vector2 mPosition; ///< Vertex posiiton
52 Vector4 mColor; ///< Vertex color
57 Vector< BackgroundVertex > mVertices; ///< container of vertices
58 Vector< unsigned short > mIndices; ///< container of indices
61 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
62 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
63 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
64 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
65 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
78 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
79 : mDecorator( decorator ),
80 mInputMethodContext( inputMethodContext ),
81 mPlaceholderFont( NULL ),
82 mPlaceholderTextActive(),
83 mPlaceholderTextInactive(),
84 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
86 mInputStyleChangedQueue(),
87 mPreviousState( INACTIVE ),
89 mPrimaryCursorPosition( 0u ),
90 mLeftSelectionPosition( 0u ),
91 mRightSelectionPosition( 0u ),
92 mPreEditStartPosition( 0u ),
94 mCursorHookPositionX( 0.f ),
95 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
96 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
97 mIsShowingPlaceholderText( false ),
98 mPreEditFlag( false ),
99 mDecoratorUpdated( false ),
100 mCursorBlinkEnabled( true ),
101 mGrabHandleEnabled( true ),
102 mGrabHandlePopupEnabled( true ),
103 mSelectionEnabled( true ),
104 mUpdateCursorHookPosition( false ),
105 mUpdateCursorPosition( false ),
106 mUpdateGrabHandlePosition( false ),
107 mUpdateLeftSelectionPosition( false ),
108 mUpdateRightSelectionPosition( false ),
109 mIsLeftHandleSelected( false ),
110 mIsRightHandleSelected( false ),
111 mUpdateHighlightBox( false ),
112 mScrollAfterUpdatePosition( false ),
113 mScrollAfterDelete( false ),
114 mAllTextSelected( false ),
115 mUpdateInputStyle( false ),
116 mPasswordInput( false ),
117 mCheckScrollAmount( false ),
118 mIsPlaceholderPixelSize( false ),
119 mIsPlaceholderElideEnabled( false ),
120 mPlaceholderEllipsisFlag( false ),
121 mShiftSelectionFlag( true ),
122 mUpdateAlignment( false ),
123 mEditingEnabled( true )
127 bool Controller::Impl::ProcessInputEvents()
129 return ControllerImplEventHandler::ProcessInputEvents(*this);
132 void Controller::Impl::NotifyInputMethodContext()
134 if( mEventData && mEventData->mInputMethodContext )
136 CharacterIndex cursorPosition = GetLogicalCursorPosition();
138 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
140 // Update the cursor position by removing the initial white spaces.
141 if( cursorPosition < numberOfWhiteSpaces )
147 cursorPosition -= numberOfWhiteSpaces;
150 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
151 mEventData->mInputMethodContext.NotifyCursorPosition();
155 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
157 if ( mEventData && mEventData->mInputMethodContext )
159 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
160 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
164 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
166 CharacterIndex cursorPosition = 0u;
170 if( ( EventData::SELECTING == mEventData->mState ) ||
171 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
173 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
177 cursorPosition = mEventData->mPrimaryCursorPosition;
181 return cursorPosition;
184 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
186 Length numberOfWhiteSpaces = 0u;
188 // Get the buffer to the text.
189 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
191 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
192 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
194 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
200 return numberOfWhiteSpaces;
203 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
205 // Get the total number of characters.
206 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
208 // Retrieve the text.
209 if( 0u != numberOfCharacters )
211 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
215 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
217 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
218 mTextUpdateInfo.mStartGlyphIndex = 0u;
219 mTextUpdateInfo.mStartLineIndex = 0u;
220 numberOfCharacters = 0u;
222 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
223 if( 0u == numberOfParagraphs )
225 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
226 numberOfCharacters = 0u;
228 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
230 // Nothing else to do if there are no paragraphs.
234 // Find the paragraphs to be updated.
235 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
236 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
238 // Text is being added at the end of the current text.
239 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
241 // Text is being added in a new paragraph after the last character of the text.
242 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
243 numberOfCharacters = 0u;
244 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
246 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
247 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
249 // Nothing else to do;
253 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
257 Length numberOfCharactersToUpdate = 0u;
258 if( mTextUpdateInfo.mFullRelayoutNeeded )
260 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
264 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
266 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
267 numberOfCharactersToUpdate,
268 paragraphsToBeUpdated );
271 if( 0u != paragraphsToBeUpdated.Count() )
273 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
274 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
275 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
277 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
278 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
280 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
281 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
282 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
283 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
285 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
286 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
288 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
292 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
296 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
297 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
300 void Controller::Impl::ClearFullModelData( OperationsMask operations )
302 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
304 mModel->mLogicalModel->mLineBreakInfo.Clear();
305 mModel->mLogicalModel->mParagraphInfo.Clear();
308 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
310 mModel->mLogicalModel->mScriptRuns.Clear();
313 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
315 mModel->mLogicalModel->mFontRuns.Clear();
318 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
320 if( NO_OPERATION != ( BIDI_INFO & operations ) )
322 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
323 mModel->mLogicalModel->mCharacterDirections.Clear();
326 if( NO_OPERATION != ( REORDER & operations ) )
328 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
329 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
330 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
334 BidirectionalLineInfoRun& bidiLineInfo = *it;
336 free( bidiLineInfo.visualToLogicalMap );
337 bidiLineInfo.visualToLogicalMap = NULL;
339 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
343 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
345 mModel->mVisualModel->mGlyphs.Clear();
346 mModel->mVisualModel->mGlyphsToCharacters.Clear();
347 mModel->mVisualModel->mCharactersToGlyph.Clear();
348 mModel->mVisualModel->mCharactersPerGlyph.Clear();
349 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
350 mModel->mVisualModel->mGlyphPositions.Clear();
353 if( NO_OPERATION != ( LAYOUT & operations ) )
355 mModel->mVisualModel->mLines.Clear();
358 if( NO_OPERATION != ( COLOR & operations ) )
360 mModel->mVisualModel->mColorIndices.Clear();
361 mModel->mVisualModel->mBackgroundColorIndices.Clear();
365 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
367 const CharacterIndex endIndexPlusOne = endIndex + 1u;
369 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
371 // Clear the line break info.
372 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
374 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
375 lineBreakInfoBuffer + endIndexPlusOne );
377 // Clear the paragraphs.
378 ClearCharacterRuns( startIndex,
380 mModel->mLogicalModel->mParagraphInfo );
383 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
385 // Clear the scripts.
386 ClearCharacterRuns( startIndex,
388 mModel->mLogicalModel->mScriptRuns );
391 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
394 ClearCharacterRuns( startIndex,
396 mModel->mLogicalModel->mFontRuns );
399 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
401 if( NO_OPERATION != ( BIDI_INFO & operations ) )
403 // Clear the bidirectional paragraph info.
404 ClearCharacterRuns( startIndex,
406 mModel->mLogicalModel->mBidirectionalParagraphInfo );
408 // Clear the character's directions.
409 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
411 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
412 characterDirectionsBuffer + endIndexPlusOne );
415 if( NO_OPERATION != ( REORDER & operations ) )
417 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
418 uint32_t endRemoveIndex = startRemoveIndex;
419 ClearCharacterRuns( startIndex,
421 mModel->mLogicalModel->mBidirectionalLineInfo,
425 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
427 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
428 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
429 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
433 BidirectionalLineInfoRun& bidiLineInfo = *it;
435 free( bidiLineInfo.visualToLogicalMap );
436 bidiLineInfo.visualToLogicalMap = NULL;
439 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
440 bidirectionalLineInfoBuffer + endRemoveIndex );
445 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
447 const CharacterIndex endIndexPlusOne = endIndex + 1u;
448 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
450 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
451 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
452 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
454 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
455 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
457 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
459 // Update the character to glyph indices.
460 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
461 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
465 CharacterIndex& index = *it;
466 index -= numberOfGlyphsRemoved;
469 // Clear the character to glyph conversion table.
470 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
471 charactersToGlyphBuffer + endIndexPlusOne );
473 // Clear the glyphs per character table.
474 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
475 glyphsPerCharacterBuffer + endIndexPlusOne );
477 // Clear the glyphs buffer.
478 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
479 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
480 glyphsBuffer + endGlyphIndexPlusOne );
482 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
484 // Update the glyph to character indices.
485 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
486 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
490 CharacterIndex& index = *it;
491 index -= numberOfCharactersRemoved;
494 // Clear the glyphs to characters buffer.
495 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
496 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
498 // Clear the characters per glyph buffer.
499 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
500 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
501 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
503 // Clear the positions buffer.
504 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
505 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
506 positionsBuffer + endGlyphIndexPlusOne );
509 if( NO_OPERATION != ( LAYOUT & operations ) )
512 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
513 uint32_t endRemoveIndex = startRemoveIndex;
514 ClearCharacterRuns( startIndex,
516 mModel->mVisualModel->mLines,
520 // Will update the glyph runs.
521 startRemoveIndex = mModel->mVisualModel->mLines.Count();
522 endRemoveIndex = startRemoveIndex;
523 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
524 endGlyphIndexPlusOne - 1u,
525 mModel->mVisualModel->mLines,
529 // Set the line index from where to insert the new laid-out lines.
530 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
532 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
533 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
534 linesBuffer + endRemoveIndex );
537 if( NO_OPERATION != ( COLOR & operations ) )
539 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
541 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
542 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
543 colorIndexBuffer + endGlyphIndexPlusOne );
546 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
548 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
549 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
550 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
555 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
557 if( mTextUpdateInfo.mClearAll ||
558 ( ( 0u == startIndex ) &&
559 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
561 ClearFullModelData( operations );
565 // Clear the model data related with characters.
566 ClearCharacterModelData( startIndex, endIndex, operations );
568 // Clear the model data related with glyphs.
569 ClearGlyphModelData( startIndex, endIndex, operations );
572 // The estimated number of lines. Used to avoid reallocations when layouting.
573 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
575 mModel->mVisualModel->ClearCaches();
578 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
580 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
582 // Calculate the operations to be done.
583 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
585 if( NO_OPERATION == operations )
587 // Nothing to do if no operations are pending and required.
591 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
592 Vector<Character> displayCharacters;
593 bool useHiddenText = false;
594 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
596 mHiddenInput->Substitute( srcCharacters,displayCharacters );
597 useHiddenText = true;
600 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
601 const Length numberOfCharacters = utf32Characters.Count();
603 // Index to the first character of the first paragraph to be updated.
604 CharacterIndex startIndex = 0u;
605 // Number of characters of the paragraphs to be removed.
606 Length paragraphCharacters = 0u;
608 CalculateTextUpdateIndices( paragraphCharacters );
610 // Check whether the indices for updating the text is valid
611 if ( numberOfCharacters > 0u &&
612 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
613 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
615 std::string currentText;
616 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
618 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
619 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
621 // Dump mTextUpdateInfo
622 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
623 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
624 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
625 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
626 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
627 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
628 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
629 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
630 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
631 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
632 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
633 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
634 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
639 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
641 if( mTextUpdateInfo.mClearAll ||
642 ( 0u != paragraphCharacters ) )
644 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
647 mTextUpdateInfo.mClearAll = false;
649 // Whether the model is updated.
650 bool updated = false;
652 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
653 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
655 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
657 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
658 // calculate the bidirectional info for each 'paragraph'.
659 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
660 // is not shaped together).
661 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
663 SetLineBreakInfo( utf32Characters,
665 requestedNumberOfCharacters,
668 // Create the paragraph info.
669 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
670 requestedNumberOfCharacters );
674 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
675 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
677 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
678 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
680 if( getScripts || validateFonts )
682 // Validates the fonts assigned by the application or assigns default ones.
683 // It makes sure all the characters are going to be rendered by the correct font.
684 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
688 // Retrieves the scripts used in the text.
689 multilanguageSupport.SetScripts( utf32Characters,
691 requestedNumberOfCharacters,
697 // Validate the fonts set through the mark-up string.
698 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
700 // Get the default font's description.
701 TextAbstraction::FontDescription defaultFontDescription;
702 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
704 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
706 // If the placeholder font is set specifically, only placeholder font is changed.
707 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
708 if( mEventData->mPlaceholderFont->sizeDefined )
710 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u;
713 else if( NULL != mFontDefaults )
715 // Set the normal font and the placeholder font.
716 defaultFontDescription = mFontDefaults->mFontDescription;
718 if( mTextFitEnabled )
720 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
724 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * 64u;
728 // Validates the fonts. If there is a character with no assigned font it sets a default one.
729 // After this call, fonts are validated.
730 multilanguageSupport.ValidateFonts( utf32Characters,
733 defaultFontDescription,
736 requestedNumberOfCharacters,
742 Vector<Character> mirroredUtf32Characters;
743 bool textMirrored = false;
744 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
745 if( NO_OPERATION != ( BIDI_INFO & operations ) )
747 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
748 bidirectionalInfo.Reserve( numberOfParagraphs );
750 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
751 SetBidirectionalInfo( utf32Characters,
755 requestedNumberOfCharacters,
757 mModel->mMatchSystemLanguageDirection,
760 if( 0u != bidirectionalInfo.Count() )
762 // Only set the character directions if there is right to left characters.
763 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
764 GetCharactersDirection( bidirectionalInfo,
767 requestedNumberOfCharacters,
770 // This paragraph has right to left text. Some characters may need to be mirrored.
771 // TODO: consider if the mirrored string can be stored as well.
773 textMirrored = GetMirroredText( utf32Characters,
777 requestedNumberOfCharacters,
778 mirroredUtf32Characters );
782 // There is no right to left characters. Clear the directions vector.
783 mModel->mLogicalModel->mCharacterDirections.Clear();
788 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
789 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
790 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
791 Vector<GlyphIndex> newParagraphGlyphs;
792 newParagraphGlyphs.Reserve( numberOfParagraphs );
794 const Length currentNumberOfGlyphs = glyphs.Count();
795 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
797 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
799 ShapeText( textToShape,
804 mTextUpdateInfo.mStartGlyphIndex,
805 requestedNumberOfCharacters,
807 glyphsToCharactersMap,
809 newParagraphGlyphs );
811 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
812 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
813 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
817 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
819 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
821 GlyphInfo* glyphsBuffer = glyphs.Begin();
822 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
824 // Update the width and advance of all new paragraph characters.
825 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
827 const GlyphIndex index = *it;
828 GlyphInfo& glyph = *( glyphsBuffer + index );
830 glyph.xBearing = 0.f;
837 if( ( NULL != mEventData ) &&
838 mEventData->mPreEditFlag &&
839 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
841 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
842 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
843 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
845 // Check the type of preedit and run it.
846 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
848 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
849 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
850 type = attrData.preeditType;
852 // Check the number of commit characters for the start position.
853 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
854 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
858 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
860 // Add the underline for the pre-edit text.
861 GlyphRun underlineRun;
862 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
863 underlineRun.numberOfGlyphs = numberOfIndices;
864 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
867 case Dali::InputMethodContext::PreeditStyle::REVERSE:
869 Vector4 textColor = mModel->mVisualModel->GetTextColor();
870 ColorRun backgroundColorRun;
871 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
872 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
873 backgroundColorRun.color = textColor;
874 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
876 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
877 Vector<ColorRun> colorRuns;
878 colorRuns.Resize( 1u );
879 ColorRun& colorRun = *( colorRuns.Begin() );
880 colorRun.color = backgroundColor;
881 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
882 colorRun.characterRun.numberOfCharacters = numberOfIndices;
884 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
887 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
889 ColorRun backgroundColorRun;
890 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
891 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
892 backgroundColorRun.color = LIGHT_BLUE;
893 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
896 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
898 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
899 ColorRun backgroundColorRun;
900 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
901 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
902 backgroundColorRun.color = BACKGROUND_SUB4;
903 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
905 GlyphRun underlineRun;
906 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
907 underlineRun.numberOfGlyphs = numberOfIndices;
908 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
911 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
913 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
914 ColorRun backgroundColorRun;
915 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
916 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
917 backgroundColorRun.color = BACKGROUND_SUB5;
918 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
920 GlyphRun underlineRun;
921 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
922 underlineRun.numberOfGlyphs = numberOfIndices;
923 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
926 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
928 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
929 ColorRun backgroundColorRun;
930 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
931 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
932 backgroundColorRun.color = BACKGROUND_SUB6;
933 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
935 GlyphRun underlineRun;
936 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
937 underlineRun.numberOfGlyphs = numberOfIndices;
938 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
941 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
943 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
944 ColorRun backgroundColorRun;
945 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
946 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
947 backgroundColorRun.color = BACKGROUND_SUB7;
948 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
950 GlyphRun underlineRun;
951 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
952 underlineRun.numberOfGlyphs = numberOfIndices;
953 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
956 case Dali::InputMethodContext::PreeditStyle::NONE:
967 if( NO_OPERATION != ( COLOR & operations ) )
969 // Set the color runs in glyphs.
970 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
971 mModel->mVisualModel->mCharactersToGlyph,
972 mModel->mVisualModel->mGlyphsPerCharacter,
974 mTextUpdateInfo.mStartGlyphIndex,
975 requestedNumberOfCharacters,
976 mModel->mVisualModel->mColors,
977 mModel->mVisualModel->mColorIndices );
979 // Set the background color runs in glyphs.
980 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
981 mModel->mVisualModel->mCharactersToGlyph,
982 mModel->mVisualModel->mGlyphsPerCharacter,
984 mTextUpdateInfo.mStartGlyphIndex,
985 requestedNumberOfCharacters,
986 mModel->mVisualModel->mBackgroundColors,
987 mModel->mVisualModel->mBackgroundColorIndices );
993 // The estimated number of lines. Used to avoid reallocations when layouting.
994 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
996 // Set the previous number of characters for the next time the text is updated.
997 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1002 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1004 // Sets the default text's color.
1005 inputStyle.textColor = mTextColor;
1006 inputStyle.isDefaultColor = true;
1008 inputStyle.familyName.clear();
1009 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1010 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1011 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1012 inputStyle.size = 0.f;
1014 inputStyle.lineSpacing = 0.f;
1016 inputStyle.underlineProperties.clear();
1017 inputStyle.shadowProperties.clear();
1018 inputStyle.embossProperties.clear();
1019 inputStyle.outlineProperties.clear();
1021 inputStyle.isFamilyDefined = false;
1022 inputStyle.isWeightDefined = false;
1023 inputStyle.isWidthDefined = false;
1024 inputStyle.isSlantDefined = false;
1025 inputStyle.isSizeDefined = false;
1027 inputStyle.isLineSpacingDefined = false;
1029 inputStyle.isUnderlineDefined = false;
1030 inputStyle.isShadowDefined = false;
1031 inputStyle.isEmbossDefined = false;
1032 inputStyle.isOutlineDefined = false;
1034 // Sets the default font's family name, weight, width, slant and size.
1037 if( mFontDefaults->familyDefined )
1039 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1040 inputStyle.isFamilyDefined = true;
1043 if( mFontDefaults->weightDefined )
1045 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1046 inputStyle.isWeightDefined = true;
1049 if( mFontDefaults->widthDefined )
1051 inputStyle.width = mFontDefaults->mFontDescription.width;
1052 inputStyle.isWidthDefined = true;
1055 if( mFontDefaults->slantDefined )
1057 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1058 inputStyle.isSlantDefined = true;
1061 if( mFontDefaults->sizeDefined )
1063 inputStyle.size = mFontDefaults->mDefaultPointSize;
1064 inputStyle.isSizeDefined = true;
1069 float Controller::Impl::GetDefaultFontLineHeight()
1071 FontId defaultFontId = 0u;
1072 if( NULL == mFontDefaults )
1074 TextAbstraction::FontDescription fontDescription;
1075 defaultFontId = mFontClient.GetFontId( fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale );
1079 defaultFontId = mFontDefaults->GetFontId( mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale );
1082 Text::FontMetrics fontMetrics;
1083 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1085 return( fontMetrics.ascender - fontMetrics.descender );
1088 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
1090 if( nullptr == mEventData )
1092 // Nothing to do if there is no text.
1096 if( mEventData->mSelectionEnabled && (pStart || pEnd))
1098 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1102 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1106 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1109 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1111 ChangeState( EventData::EDITING );
1112 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1113 mEventData->mUpdateCursorPosition = true;
1117 ChangeState( EventData::SELECTING );
1118 mEventData->mUpdateHighlightBox = true;
1119 mEventData->mUpdateLeftSelectionPosition = true;
1120 mEventData->mUpdateRightSelectionPosition = true;
1125 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1127 if( nullptr == mEventData )
1131 return mEventData->mPrimaryCursorPosition;
1134 bool Controller::Impl::SetPrimaryCursorPosition( CharacterIndex index )
1136 if( nullptr == mEventData )
1138 // Nothing to do if there is no text.
1142 if( mEventData->mPrimaryCursorPosition == index )
1144 // Nothing for same cursor position.
1148 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1149 mEventData->mPrimaryCursorPosition = std::min(index, length);
1150 ChangeState( EventData::EDITING );
1151 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1152 mEventData->mUpdateCursorPosition = true;
1153 ScrollTextToMatchCursor();
1157 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1163 range.first = mEventData->mLeftSelectionPosition;
1164 range.second = mEventData->mRightSelectionPosition;
1170 bool Controller::Impl::IsEditable() const
1172 return mEventData && mEventData->mEditingEnabled;
1175 void Controller::Impl::SetEditable( bool editable )
1179 mEventData->mEditingEnabled = editable;
1183 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1185 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1187 // Nothing to select if handles are in the same place.
1188 selectedText.clear();
1192 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1194 //Get start and end position of selection
1195 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1196 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1198 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1199 const Length numberOfCharacters = utf32Characters.Count();
1201 // Validate the start and end selection points
1202 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1204 //Get text as a UTF8 string
1205 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1207 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1209 // Keep a copy of the current input style.
1210 InputStyle currentInputStyle;
1211 currentInputStyle.Copy( mEventData->mInputStyle );
1213 // Set as input style the style of the first deleted character.
1214 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1216 // Compare if the input style has changed.
1217 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1219 if( hasInputStyleChanged )
1221 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1222 // Queue the input style changed signal.
1223 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1226 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1228 // Mark the paragraphs to be updated.
1229 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1231 mTextUpdateInfo.mCharacterIndex = 0;
1232 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1233 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1234 mTextUpdateInfo.mClearAll = true;
1238 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1239 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1242 // Delete text between handles
1243 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1244 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1245 utf32Characters.Erase( first, last );
1247 // Will show the cursor at the first character of the selection.
1248 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1252 // Will show the cursor at the last character of the selection.
1253 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1256 mEventData->mDecoratorUpdated = true;
1260 void Controller::Impl::SetSelection( int start, int end )
1262 mEventData->mLeftSelectionPosition = start;
1263 mEventData->mRightSelectionPosition = end;
1264 mEventData->mUpdateCursorPosition = true;
1267 std::pair< int, int > Controller::Impl::GetSelectionIndexes() const
1269 return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition };
1272 void Controller::Impl::ShowClipboard()
1276 mClipboard.ShowClipboard();
1280 void Controller::Impl::HideClipboard()
1282 if( mClipboard && mClipboardHideEnabled )
1284 mClipboard.HideClipboard();
1288 void Controller::Impl::SetClipboardHideEnable(bool enable)
1290 mClipboardHideEnabled = enable;
1293 bool Controller::Impl::CopyStringToClipboard( const std::string& source )
1295 //Send string to clipboard
1296 return ( mClipboard && mClipboard.SetItem( source ) );
1299 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1301 std::string selectedText;
1302 RetrieveSelection( selectedText, deleteAfterSending );
1303 CopyStringToClipboard( selectedText );
1304 ChangeState( EventData::EDITING );
1307 void Controller::Impl::RequestGetTextFromClipboard()
1311 mClipboard.RequestItem();
1315 void Controller::Impl::RepositionSelectionHandles()
1317 SelectionHandleController::Reposition(*this);
1319 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
1321 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1324 void Controller::Impl::SetPopupButtons()
1327 * Sets the Popup buttons to be shown depending on State.
1329 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1331 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1334 bool isEditable = IsEditable();
1335 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1337 if( EventData::SELECTING == mEventData->mState )
1339 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
1342 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
1345 if( !IsClipboardEmpty() )
1349 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1351 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1354 if( !mEventData->mAllTextSelected )
1356 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1359 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1361 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1363 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1366 if( !IsClipboardEmpty() )
1370 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1372 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1375 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1377 if ( !IsClipboardEmpty() )
1381 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1383 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1387 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1390 void Controller::Impl::ChangeState( EventData::State newState )
1392 if( NULL == mEventData )
1394 // Nothing to do if there is no text input.
1398 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1400 if( mEventData->mState != newState )
1402 mEventData->mPreviousState = mEventData->mState;
1403 mEventData->mState = newState;
1405 switch( mEventData->mState )
1407 case EventData::INACTIVE:
1409 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1410 mEventData->mDecorator->StopCursorBlink();
1411 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1412 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1413 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1414 mEventData->mDecorator->SetHighlightActive( false );
1415 mEventData->mDecorator->SetPopupActive( false );
1416 mEventData->mDecoratorUpdated = true;
1419 case EventData::INTERRUPTED:
1421 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1422 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1423 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1424 mEventData->mDecorator->SetHighlightActive( false );
1425 mEventData->mDecorator->SetPopupActive( false );
1426 mEventData->mDecoratorUpdated = true;
1429 case EventData::SELECTING:
1431 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1432 mEventData->mDecorator->StopCursorBlink();
1433 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1434 if ( mEventData->mGrabHandleEnabled )
1436 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1437 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1439 mEventData->mDecorator->SetHighlightActive( true );
1440 if( mEventData->mGrabHandlePopupEnabled )
1443 mEventData->mDecorator->SetPopupActive( true );
1445 mEventData->mDecoratorUpdated = true;
1448 case EventData::EDITING:
1450 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1451 if( mEventData->mCursorBlinkEnabled )
1453 mEventData->mDecorator->StartCursorBlink();
1455 // Grab handle is not shown until a tap is received whilst EDITING
1456 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1457 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1458 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1459 mEventData->mDecorator->SetHighlightActive( false );
1460 if( mEventData->mGrabHandlePopupEnabled )
1462 mEventData->mDecorator->SetPopupActive( false );
1464 mEventData->mDecoratorUpdated = true;
1467 case EventData::EDITING_WITH_POPUP:
1469 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1471 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1472 if( mEventData->mCursorBlinkEnabled )
1474 mEventData->mDecorator->StartCursorBlink();
1476 if( mEventData->mSelectionEnabled )
1478 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1479 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1480 mEventData->mDecorator->SetHighlightActive( false );
1482 else if ( mEventData->mGrabHandleEnabled )
1484 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1486 if( mEventData->mGrabHandlePopupEnabled )
1489 mEventData->mDecorator->SetPopupActive( true );
1491 mEventData->mDecoratorUpdated = true;
1494 case EventData::EDITING_WITH_GRAB_HANDLE:
1496 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1498 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1499 if( mEventData->mCursorBlinkEnabled )
1501 mEventData->mDecorator->StartCursorBlink();
1503 // Grab handle is not shown until a tap is received whilst EDITING
1504 if ( mEventData->mGrabHandleEnabled )
1506 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1508 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1509 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1510 mEventData->mDecorator->SetHighlightActive( false );
1511 if( mEventData->mGrabHandlePopupEnabled )
1513 mEventData->mDecorator->SetPopupActive( false );
1515 mEventData->mDecoratorUpdated = true;
1518 case EventData::SELECTION_HANDLE_PANNING:
1520 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1521 mEventData->mDecorator->StopCursorBlink();
1522 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1523 if ( mEventData->mGrabHandleEnabled )
1525 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1526 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1528 mEventData->mDecorator->SetHighlightActive( true );
1529 if( mEventData->mGrabHandlePopupEnabled )
1531 mEventData->mDecorator->SetPopupActive( false );
1533 mEventData->mDecoratorUpdated = true;
1536 case EventData::GRAB_HANDLE_PANNING:
1538 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1540 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1541 if( mEventData->mCursorBlinkEnabled )
1543 mEventData->mDecorator->StartCursorBlink();
1545 if ( mEventData->mGrabHandleEnabled )
1547 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1549 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1550 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1551 mEventData->mDecorator->SetHighlightActive( false );
1552 if( mEventData->mGrabHandlePopupEnabled )
1554 mEventData->mDecorator->SetPopupActive( false );
1556 mEventData->mDecoratorUpdated = true;
1559 case EventData::EDITING_WITH_PASTE_POPUP:
1561 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1563 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1564 if( mEventData->mCursorBlinkEnabled )
1566 mEventData->mDecorator->StartCursorBlink();
1569 if ( mEventData->mGrabHandleEnabled )
1571 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1573 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1574 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1575 mEventData->mDecorator->SetHighlightActive( false );
1577 if( mEventData->mGrabHandlePopupEnabled )
1580 mEventData->mDecorator->SetPopupActive( true );
1582 mEventData->mDecoratorUpdated = true;
1585 case EventData::TEXT_PANNING:
1587 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1588 mEventData->mDecorator->StopCursorBlink();
1589 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1590 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
1591 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
1593 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1594 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1595 mEventData->mDecorator->SetHighlightActive( true );
1598 if( mEventData->mGrabHandlePopupEnabled )
1600 mEventData->mDecorator->SetPopupActive( false );
1603 mEventData->mDecoratorUpdated = true;
1610 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1611 CursorInfo& cursorInfo )
1613 if( !IsShowingRealText() )
1615 // Do not want to use the place-holder text to set the cursor position.
1617 // Use the line's height of the font's family set to set the cursor's size.
1618 // If there is no font's family set, use the default font.
1619 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1621 cursorInfo.lineOffset = 0.f;
1622 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1623 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1626 if( mModel->mMatchSystemLanguageDirection )
1628 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1631 switch( mModel->mHorizontalAlignment )
1633 case Text::HorizontalAlignment::BEGIN :
1637 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1641 cursorInfo.primaryPosition.x = 0.f;
1645 case Text::HorizontalAlignment::CENTER:
1647 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
1650 case Text::HorizontalAlignment::END:
1654 cursorInfo.primaryPosition.x = 0.f;
1658 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1664 // Nothing else to do.
1668 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
1669 GetCursorPositionParameters parameters;
1670 parameters.visualModel = mModel->mVisualModel;
1671 parameters.logicalModel = mModel->mLogicalModel;
1672 parameters.metrics = mMetrics;
1673 parameters.logical = logical;
1674 parameters.isMultiline = isMultiLine;
1676 Text::GetCursorPosition( parameters,
1679 // Adds Outline offset.
1680 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
1681 cursorInfo.primaryPosition.x += outlineWidth;
1682 cursorInfo.primaryPosition.y += outlineWidth;
1683 cursorInfo.secondaryPosition.x += outlineWidth;
1684 cursorInfo.secondaryPosition.y += outlineWidth;
1688 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1690 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1691 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1693 if( 0.f > cursorInfo.primaryPosition.x )
1695 cursorInfo.primaryPosition.x = 0.f;
1698 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
1699 if( cursorInfo.primaryPosition.x > edgeWidth )
1701 cursorInfo.primaryPosition.x = edgeWidth;
1706 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1708 if( NULL == mEventData )
1710 // Nothing to do if there is no text input.
1714 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1716 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1717 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1719 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1720 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1722 if( numberOfCharacters > 1u )
1724 const Script script = mModel->mLogicalModel->GetScript( index );
1725 if( HasLigatureMustBreak( script ) )
1727 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1728 numberOfCharacters = 1u;
1733 while( 0u == numberOfCharacters )
1736 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1740 if( index < mEventData->mPrimaryCursorPosition )
1742 cursorIndex -= numberOfCharacters;
1746 cursorIndex += numberOfCharacters;
1749 // Will update the cursor hook position.
1750 mEventData->mUpdateCursorHookPosition = true;
1755 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1757 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1758 if( NULL == mEventData )
1760 // Nothing to do if there is no text input.
1761 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1765 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1767 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
1769 // Sets the cursor position.
1770 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1773 cursorInfo.primaryCursorHeight,
1774 cursorInfo.lineHeight );
1775 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1777 if( mEventData->mUpdateGrabHandlePosition )
1779 // Sets the grab handle position.
1780 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1782 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1783 cursorInfo.lineHeight );
1786 if( cursorInfo.isSecondaryCursor )
1788 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1789 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1790 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1791 cursorInfo.secondaryCursorHeight,
1792 cursorInfo.lineHeight );
1793 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
1796 // Set which cursors are active according the state.
1797 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1799 if( cursorInfo.isSecondaryCursor )
1801 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1805 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1810 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1813 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1816 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
1817 const CursorInfo& cursorInfo )
1819 SelectionHandleController::Update(*this, handleType, cursorInfo);
1822 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
1824 // Clamp between -space & -alignment offset.
1826 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
1828 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
1829 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
1830 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1832 mEventData->mDecoratorUpdated = true;
1836 mModel->mScrollPosition.x = 0.f;
1840 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
1842 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1844 // Nothing to do if the text is single line.
1848 // Clamp between -space & 0.
1849 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
1851 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
1852 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
1853 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
1855 mEventData->mDecoratorUpdated = true;
1859 mModel->mScrollPosition.y = 0.f;
1863 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
1865 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
1867 // position is in actor's coords.
1868 const float positionEndX = position.x + cursorWidth;
1869 const float positionEndY = position.y + lineHeight;
1871 // Transform the position to decorator coords.
1872 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1873 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1875 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1876 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1878 if( decoratorPositionBeginX < 0.f )
1880 mModel->mScrollPosition.x = -position.x;
1882 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
1884 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1887 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
1889 if( decoratorPositionBeginY < 0.f )
1891 mModel->mScrollPosition.y = -position.y;
1893 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
1895 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1900 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
1902 // Get the current cursor position in decorator coords.
1903 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1905 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
1909 // Calculate the offset to match the cursor position before the character was deleted.
1910 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1912 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1913 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
1915 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
1916 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1920 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
1921 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
1923 // Makes the new cursor position visible if needed.
1924 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
1927 void Controller::Impl::ScrollTextToMatchCursor()
1929 CursorInfo cursorInfo;
1930 GetCursorPosition( mEventData->mPrimaryCursorPosition, cursorInfo );
1931 ScrollTextToMatchCursor(cursorInfo);
1934 void Controller::Impl::RequestRelayout()
1936 if( NULL != mControlInterface )
1938 mControlInterface->RequestTextRelayout();
1942 Actor Controller::Impl::CreateBackgroundActor()
1944 // NOTE: Currently we only support background color for one line left-to-right text,
1945 // so the following calculation is based on one line left-to-right text only!
1949 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1950 if( numberOfGlyphs > 0u )
1952 Vector<GlyphInfo> glyphs;
1953 glyphs.Resize( numberOfGlyphs );
1955 Vector<Vector2> positions;
1956 positions.Resize( numberOfGlyphs );
1958 // Get the line where the glyphs are laid-out.
1959 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1960 float alignmentOffset = lineRun->alignmentOffset;
1961 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
1967 glyphs.Resize( numberOfGlyphs );
1968 positions.Resize( numberOfGlyphs );
1970 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
1971 const Vector2* const positionsBuffer = positions.Begin();
1973 BackgroundMesh mesh;
1974 mesh.mVertices.Reserve( 4u * glyphs.Size() );
1975 mesh.mIndices.Reserve( 6u * glyphs.Size() );
1977 const Vector2 textSize = mView.GetLayoutSize();
1979 const float offsetX = textSize.width * 0.5f;
1980 const float offsetY = textSize.height * 0.5f;
1982 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
1983 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
1984 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
1987 uint32_t numberOfQuads = 0u;
1989 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
1991 const GlyphInfo& glyph = *( glyphsBuffer + i );
1993 // Get the background color of the character.
1994 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
1995 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
1996 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
1998 // Only create quads for glyphs with a background color
1999 if ( backgroundColor != Color::TRANSPARENT )
2001 const Vector2 position = *( positionsBuffer + i );
2003 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
2005 quad.x = position.x;
2007 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2008 quad.w = textSize.height;
2010 else if ( i == 0u ) // The first glyph in the whole text
2012 quad.x = position.x;
2014 quad.z = quad.x - glyph.xBearing + glyph.advance;
2015 quad.w = textSize.height;
2017 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
2019 quad.x = position.x - glyph.xBearing;
2021 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2022 quad.w = textSize.height;
2024 else // The glyph in the middle of the text
2026 quad.x = position.x - glyph.xBearing;
2028 quad.z = quad.x + glyph.advance;
2029 quad.w = textSize.height;
2032 BackgroundVertex vertex;
2035 vertex.mPosition.x = quad.x - offsetX;
2036 vertex.mPosition.y = quad.y - offsetY;
2037 vertex.mColor = backgroundColor;
2038 mesh.mVertices.PushBack( vertex );
2041 vertex.mPosition.x = quad.z - offsetX;
2042 vertex.mPosition.y = quad.y - offsetY;
2043 vertex.mColor = backgroundColor;
2044 mesh.mVertices.PushBack( vertex );
2047 vertex.mPosition.x = quad.x - offsetX;
2048 vertex.mPosition.y = quad.w - offsetY;
2049 vertex.mColor = backgroundColor;
2050 mesh.mVertices.PushBack( vertex );
2053 vertex.mPosition.x = quad.z - offsetX;
2054 vertex.mPosition.y = quad.w - offsetY;
2055 vertex.mColor = backgroundColor;
2056 mesh.mVertices.PushBack( vertex );
2058 // Six indices in counter clockwise winding
2059 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2060 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
2061 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2062 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2063 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
2064 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2070 // Only create the background actor if there are glyphs with background color
2071 if ( mesh.mVertices.Count() > 0u )
2073 Property::Map quadVertexFormat;
2074 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2075 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2077 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2078 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2080 Geometry quadGeometry = Geometry::New();
2081 quadGeometry.AddVertexBuffer( quadVertices );
2082 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2084 if( !mShaderBackground )
2086 mShaderBackground = Shader::New( SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG );
2089 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2090 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2091 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2093 actor = Actor::New();
2094 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2095 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2096 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2097 actor.SetProperty( Actor::Property::SIZE, textSize );
2098 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2099 actor.AddRenderer( renderer );
2108 } // namespace Toolkit