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>
44 #if defined(DEBUG_ENABLED)
45 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
48 #define MAKE_SHADER(A)#A
50 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
51 attribute mediump vec2 aPosition;
52 attribute mediump vec4 aColor;
53 varying mediump vec4 vColor;
54 uniform highp mat4 uMvpMatrix;
58 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
59 gl_Position = uMvpMatrix * position;
64 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
65 varying mediump vec4 vColor;
66 uniform lowp vec4 uColor;
70 gl_FragColor = vColor * uColor;
74 struct BackgroundVertex
76 Vector2 mPosition; ///< Vertex posiiton
77 Vector4 mColor; ///< Vertex color
82 Vector< BackgroundVertex > mVertices; ///< container of vertices
83 Vector< unsigned short > mIndices; ///< container of indices
86 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
87 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
88 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
89 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
90 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
103 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
104 : mDecorator( decorator ),
105 mInputMethodContext( inputMethodContext ),
106 mPlaceholderFont( NULL ),
107 mPlaceholderTextActive(),
108 mPlaceholderTextInactive(),
109 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
111 mInputStyleChangedQueue(),
112 mPreviousState( INACTIVE ),
114 mPrimaryCursorPosition( 0u ),
115 mLeftSelectionPosition( 0u ),
116 mRightSelectionPosition( 0u ),
117 mPreEditStartPosition( 0u ),
118 mPreEditLength( 0u ),
119 mCursorHookPositionX( 0.f ),
120 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
121 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
122 mIsShowingPlaceholderText( false ),
123 mPreEditFlag( false ),
124 mDecoratorUpdated( false ),
125 mCursorBlinkEnabled( true ),
126 mGrabHandleEnabled( true ),
127 mGrabHandlePopupEnabled( true ),
128 mSelectionEnabled( true ),
129 mUpdateCursorHookPosition( false ),
130 mUpdateCursorPosition( false ),
131 mUpdateGrabHandlePosition( false ),
132 mUpdateLeftSelectionPosition( false ),
133 mUpdateRightSelectionPosition( false ),
134 mIsLeftHandleSelected( false ),
135 mIsRightHandleSelected( false ),
136 mUpdateHighlightBox( false ),
137 mScrollAfterUpdatePosition( false ),
138 mScrollAfterDelete( false ),
139 mAllTextSelected( false ),
140 mUpdateInputStyle( false ),
141 mPasswordInput( false ),
142 mCheckScrollAmount( false ),
143 mIsPlaceholderPixelSize( false ),
144 mIsPlaceholderElideEnabled( false ),
145 mPlaceholderEllipsisFlag( false ),
146 mShiftSelectionFlag( true ),
147 mUpdateAlignment( false ),
148 mEditingEnabled( true )
152 bool Controller::Impl::ProcessInputEvents()
154 return ControllerImplEventHandler::ProcessInputEvents(*this);
157 void Controller::Impl::NotifyInputMethodContext()
159 if( mEventData && mEventData->mInputMethodContext )
161 CharacterIndex cursorPosition = GetLogicalCursorPosition();
163 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
165 // Update the cursor position by removing the initial white spaces.
166 if( cursorPosition < numberOfWhiteSpaces )
172 cursorPosition -= numberOfWhiteSpaces;
175 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
176 mEventData->mInputMethodContext.NotifyCursorPosition();
180 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
182 if ( mEventData && mEventData->mInputMethodContext )
184 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
185 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
189 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
191 CharacterIndex cursorPosition = 0u;
195 if( ( EventData::SELECTING == mEventData->mState ) ||
196 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
198 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
202 cursorPosition = mEventData->mPrimaryCursorPosition;
206 return cursorPosition;
209 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
211 Length numberOfWhiteSpaces = 0u;
213 // Get the buffer to the text.
214 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
216 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
217 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
219 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
225 return numberOfWhiteSpaces;
228 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
230 // Get the total number of characters.
231 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
233 // Retrieve the text.
234 if( 0u != numberOfCharacters )
236 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
240 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
242 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
243 mTextUpdateInfo.mStartGlyphIndex = 0u;
244 mTextUpdateInfo.mStartLineIndex = 0u;
245 numberOfCharacters = 0u;
247 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
248 if( 0u == numberOfParagraphs )
250 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
251 numberOfCharacters = 0u;
253 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
255 // Nothing else to do if there are no paragraphs.
259 // Find the paragraphs to be updated.
260 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
261 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
263 // Text is being added at the end of the current text.
264 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
266 // Text is being added in a new paragraph after the last character of the text.
267 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
268 numberOfCharacters = 0u;
269 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
271 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
272 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
274 // Nothing else to do;
278 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
282 Length numberOfCharactersToUpdate = 0u;
283 if( mTextUpdateInfo.mFullRelayoutNeeded )
285 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
289 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
291 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
292 numberOfCharactersToUpdate,
293 paragraphsToBeUpdated );
296 if( 0u != paragraphsToBeUpdated.Count() )
298 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
299 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
300 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
302 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
303 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
305 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
306 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
307 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
308 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
310 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
311 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
313 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
317 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
321 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
322 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
325 void Controller::Impl::ClearFullModelData( OperationsMask operations )
327 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
329 mModel->mLogicalModel->mLineBreakInfo.Clear();
330 mModel->mLogicalModel->mParagraphInfo.Clear();
333 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
335 mModel->mLogicalModel->mScriptRuns.Clear();
338 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
340 mModel->mLogicalModel->mFontRuns.Clear();
343 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
345 if( NO_OPERATION != ( BIDI_INFO & operations ) )
347 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
348 mModel->mLogicalModel->mCharacterDirections.Clear();
351 if( NO_OPERATION != ( REORDER & operations ) )
353 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
354 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
355 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
359 BidirectionalLineInfoRun& bidiLineInfo = *it;
361 free( bidiLineInfo.visualToLogicalMap );
362 bidiLineInfo.visualToLogicalMap = NULL;
364 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
368 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
370 mModel->mVisualModel->mGlyphs.Clear();
371 mModel->mVisualModel->mGlyphsToCharacters.Clear();
372 mModel->mVisualModel->mCharactersToGlyph.Clear();
373 mModel->mVisualModel->mCharactersPerGlyph.Clear();
374 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
375 mModel->mVisualModel->mGlyphPositions.Clear();
378 if( NO_OPERATION != ( LAYOUT & operations ) )
380 mModel->mVisualModel->mLines.Clear();
383 if( NO_OPERATION != ( COLOR & operations ) )
385 mModel->mVisualModel->mColorIndices.Clear();
386 mModel->mVisualModel->mBackgroundColorIndices.Clear();
390 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
392 const CharacterIndex endIndexPlusOne = endIndex + 1u;
394 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
396 // Clear the line break info.
397 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
399 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
400 lineBreakInfoBuffer + endIndexPlusOne );
402 // Clear the paragraphs.
403 ClearCharacterRuns( startIndex,
405 mModel->mLogicalModel->mParagraphInfo );
408 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
410 // Clear the scripts.
411 ClearCharacterRuns( startIndex,
413 mModel->mLogicalModel->mScriptRuns );
416 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
419 ClearCharacterRuns( startIndex,
421 mModel->mLogicalModel->mFontRuns );
424 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
426 if( NO_OPERATION != ( BIDI_INFO & operations ) )
428 // Clear the bidirectional paragraph info.
429 ClearCharacterRuns( startIndex,
431 mModel->mLogicalModel->mBidirectionalParagraphInfo );
433 // Clear the character's directions.
434 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
436 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
437 characterDirectionsBuffer + endIndexPlusOne );
440 if( NO_OPERATION != ( REORDER & operations ) )
442 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
443 uint32_t endRemoveIndex = startRemoveIndex;
444 ClearCharacterRuns( startIndex,
446 mModel->mLogicalModel->mBidirectionalLineInfo,
450 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
452 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
453 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
454 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
458 BidirectionalLineInfoRun& bidiLineInfo = *it;
460 free( bidiLineInfo.visualToLogicalMap );
461 bidiLineInfo.visualToLogicalMap = NULL;
464 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
465 bidirectionalLineInfoBuffer + endRemoveIndex );
470 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
472 const CharacterIndex endIndexPlusOne = endIndex + 1u;
473 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
475 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
476 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
477 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
479 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
480 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
482 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
484 // Update the character to glyph indices.
485 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
486 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
490 CharacterIndex& index = *it;
491 index -= numberOfGlyphsRemoved;
494 // Clear the character to glyph conversion table.
495 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
496 charactersToGlyphBuffer + endIndexPlusOne );
498 // Clear the glyphs per character table.
499 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
500 glyphsPerCharacterBuffer + endIndexPlusOne );
502 // Clear the glyphs buffer.
503 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
504 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
505 glyphsBuffer + endGlyphIndexPlusOne );
507 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
509 // Update the glyph to character indices.
510 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
511 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
515 CharacterIndex& index = *it;
516 index -= numberOfCharactersRemoved;
519 // Clear the glyphs to characters buffer.
520 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
521 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
523 // Clear the characters per glyph buffer.
524 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
525 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
526 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
528 // Clear the positions buffer.
529 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
530 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
531 positionsBuffer + endGlyphIndexPlusOne );
534 if( NO_OPERATION != ( LAYOUT & operations ) )
537 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
538 uint32_t endRemoveIndex = startRemoveIndex;
539 ClearCharacterRuns( startIndex,
541 mModel->mVisualModel->mLines,
545 // Will update the glyph runs.
546 startRemoveIndex = mModel->mVisualModel->mLines.Count();
547 endRemoveIndex = startRemoveIndex;
548 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
549 endGlyphIndexPlusOne - 1u,
550 mModel->mVisualModel->mLines,
554 // Set the line index from where to insert the new laid-out lines.
555 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
557 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
558 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
559 linesBuffer + endRemoveIndex );
562 if( NO_OPERATION != ( COLOR & operations ) )
564 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
566 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
567 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
568 colorIndexBuffer + endGlyphIndexPlusOne );
571 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
573 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
574 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
575 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
580 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
582 if( mTextUpdateInfo.mClearAll ||
583 ( ( 0u == startIndex ) &&
584 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
586 ClearFullModelData( operations );
590 // Clear the model data related with characters.
591 ClearCharacterModelData( startIndex, endIndex, operations );
593 // Clear the model data related with glyphs.
594 ClearGlyphModelData( startIndex, endIndex, operations );
597 // The estimated number of lines. Used to avoid reallocations when layouting.
598 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
600 mModel->mVisualModel->ClearCaches();
603 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
605 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
607 // Calculate the operations to be done.
608 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
610 if( NO_OPERATION == operations )
612 // Nothing to do if no operations are pending and required.
616 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
617 Vector<Character> displayCharacters;
618 bool useHiddenText = false;
619 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
621 mHiddenInput->Substitute( srcCharacters,displayCharacters );
622 useHiddenText = true;
625 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
626 const Length numberOfCharacters = utf32Characters.Count();
628 // Index to the first character of the first paragraph to be updated.
629 CharacterIndex startIndex = 0u;
630 // Number of characters of the paragraphs to be removed.
631 Length paragraphCharacters = 0u;
633 CalculateTextUpdateIndices( paragraphCharacters );
635 // Check whether the indices for updating the text is valid
636 if ( numberOfCharacters > 0u &&
637 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
638 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
640 std::string currentText;
641 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
643 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
644 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
646 // Dump mTextUpdateInfo
647 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
648 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
649 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
650 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
651 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
652 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
653 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
654 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
655 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
656 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
657 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
658 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
659 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
664 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
666 if( mTextUpdateInfo.mClearAll ||
667 ( 0u != paragraphCharacters ) )
669 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
672 mTextUpdateInfo.mClearAll = false;
674 // Whether the model is updated.
675 bool updated = false;
677 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
678 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
680 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
682 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
683 // calculate the bidirectional info for each 'paragraph'.
684 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
685 // is not shaped together).
686 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
688 SetLineBreakInfo( utf32Characters,
690 requestedNumberOfCharacters,
693 // Create the paragraph info.
694 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
695 requestedNumberOfCharacters );
699 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
700 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
702 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
703 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
705 if( getScripts || validateFonts )
707 // Validates the fonts assigned by the application or assigns default ones.
708 // It makes sure all the characters are going to be rendered by the correct font.
709 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
713 // Retrieves the scripts used in the text.
714 multilanguageSupport.SetScripts( utf32Characters,
716 requestedNumberOfCharacters,
722 // Validate the fonts set through the mark-up string.
723 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
725 // Get the default font's description.
726 TextAbstraction::FontDescription defaultFontDescription;
727 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
729 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
731 // If the placeholder font is set specifically, only placeholder font is changed.
732 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
733 if( mEventData->mPlaceholderFont->sizeDefined )
735 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u;
738 else if( NULL != mFontDefaults )
740 // Set the normal font and the placeholder font.
741 defaultFontDescription = mFontDefaults->mFontDescription;
743 if( mTextFitEnabled )
745 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
749 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * 64u;
753 // Validates the fonts. If there is a character with no assigned font it sets a default one.
754 // After this call, fonts are validated.
755 multilanguageSupport.ValidateFonts( utf32Characters,
758 defaultFontDescription,
761 requestedNumberOfCharacters,
767 Vector<Character> mirroredUtf32Characters;
768 bool textMirrored = false;
769 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
770 if( NO_OPERATION != ( BIDI_INFO & operations ) )
772 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
773 bidirectionalInfo.Reserve( numberOfParagraphs );
775 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
776 SetBidirectionalInfo( utf32Characters,
780 requestedNumberOfCharacters,
782 mModel->mMatchSystemLanguageDirection,
785 if( 0u != bidirectionalInfo.Count() )
787 // Only set the character directions if there is right to left characters.
788 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
789 GetCharactersDirection( bidirectionalInfo,
792 requestedNumberOfCharacters,
795 // This paragraph has right to left text. Some characters may need to be mirrored.
796 // TODO: consider if the mirrored string can be stored as well.
798 textMirrored = GetMirroredText( utf32Characters,
802 requestedNumberOfCharacters,
803 mirroredUtf32Characters );
807 // There is no right to left characters. Clear the directions vector.
808 mModel->mLogicalModel->mCharacterDirections.Clear();
813 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
814 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
815 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
816 Vector<GlyphIndex> newParagraphGlyphs;
817 newParagraphGlyphs.Reserve( numberOfParagraphs );
819 const Length currentNumberOfGlyphs = glyphs.Count();
820 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
822 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
824 ShapeText( textToShape,
829 mTextUpdateInfo.mStartGlyphIndex,
830 requestedNumberOfCharacters,
832 glyphsToCharactersMap,
834 newParagraphGlyphs );
836 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
837 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
838 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
842 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
844 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
846 GlyphInfo* glyphsBuffer = glyphs.Begin();
847 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
849 // Update the width and advance of all new paragraph characters.
850 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
852 const GlyphIndex index = *it;
853 GlyphInfo& glyph = *( glyphsBuffer + index );
855 glyph.xBearing = 0.f;
862 if( ( NULL != mEventData ) &&
863 mEventData->mPreEditFlag &&
864 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
866 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
867 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
868 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
870 // Check the type of preedit and run it.
871 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
873 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
874 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
875 type = attrData.preeditType;
877 // Check the number of commit characters for the start position.
878 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
879 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
883 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
885 // Add the underline for the pre-edit text.
886 GlyphRun underlineRun;
887 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
888 underlineRun.numberOfGlyphs = numberOfIndices;
889 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
892 case Dali::InputMethodContext::PreeditStyle::REVERSE:
894 Vector4 textColor = mModel->mVisualModel->GetTextColor();
895 ColorRun backgroundColorRun;
896 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
897 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
898 backgroundColorRun.color = textColor;
899 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
901 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
902 Vector<ColorRun> colorRuns;
903 colorRuns.Resize( 1u );
904 ColorRun& colorRun = *( colorRuns.Begin() );
905 colorRun.color = backgroundColor;
906 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
907 colorRun.characterRun.numberOfCharacters = numberOfIndices;
909 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
912 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
914 ColorRun backgroundColorRun;
915 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
916 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
917 backgroundColorRun.color = LIGHT_BLUE;
918 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
921 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
923 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
924 ColorRun backgroundColorRun;
925 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
926 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
927 backgroundColorRun.color = BACKGROUND_SUB4;
928 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
930 GlyphRun underlineRun;
931 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
932 underlineRun.numberOfGlyphs = numberOfIndices;
933 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
936 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
938 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
939 ColorRun backgroundColorRun;
940 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
941 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
942 backgroundColorRun.color = BACKGROUND_SUB5;
943 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
945 GlyphRun underlineRun;
946 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
947 underlineRun.numberOfGlyphs = numberOfIndices;
948 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
951 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
953 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
954 ColorRun backgroundColorRun;
955 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
956 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
957 backgroundColorRun.color = BACKGROUND_SUB6;
958 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
960 GlyphRun underlineRun;
961 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
962 underlineRun.numberOfGlyphs = numberOfIndices;
963 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
966 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
968 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
969 ColorRun backgroundColorRun;
970 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
971 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
972 backgroundColorRun.color = BACKGROUND_SUB7;
973 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
975 GlyphRun underlineRun;
976 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
977 underlineRun.numberOfGlyphs = numberOfIndices;
978 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
981 case Dali::InputMethodContext::PreeditStyle::NONE:
992 if( NO_OPERATION != ( COLOR & operations ) )
994 // Set the color runs in glyphs.
995 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
996 mModel->mVisualModel->mCharactersToGlyph,
997 mModel->mVisualModel->mGlyphsPerCharacter,
999 mTextUpdateInfo.mStartGlyphIndex,
1000 requestedNumberOfCharacters,
1001 mModel->mVisualModel->mColors,
1002 mModel->mVisualModel->mColorIndices );
1004 // Set the background color runs in glyphs.
1005 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1006 mModel->mVisualModel->mCharactersToGlyph,
1007 mModel->mVisualModel->mGlyphsPerCharacter,
1009 mTextUpdateInfo.mStartGlyphIndex,
1010 requestedNumberOfCharacters,
1011 mModel->mVisualModel->mBackgroundColors,
1012 mModel->mVisualModel->mBackgroundColorIndices );
1018 // The estimated number of lines. Used to avoid reallocations when layouting.
1019 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1021 // Set the previous number of characters for the next time the text is updated.
1022 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1027 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1029 // Sets the default text's color.
1030 inputStyle.textColor = mTextColor;
1031 inputStyle.isDefaultColor = true;
1033 inputStyle.familyName.clear();
1034 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1035 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1036 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1037 inputStyle.size = 0.f;
1039 inputStyle.lineSpacing = 0.f;
1041 inputStyle.underlineProperties.clear();
1042 inputStyle.shadowProperties.clear();
1043 inputStyle.embossProperties.clear();
1044 inputStyle.outlineProperties.clear();
1046 inputStyle.isFamilyDefined = false;
1047 inputStyle.isWeightDefined = false;
1048 inputStyle.isWidthDefined = false;
1049 inputStyle.isSlantDefined = false;
1050 inputStyle.isSizeDefined = false;
1052 inputStyle.isLineSpacingDefined = false;
1054 inputStyle.isUnderlineDefined = false;
1055 inputStyle.isShadowDefined = false;
1056 inputStyle.isEmbossDefined = false;
1057 inputStyle.isOutlineDefined = false;
1059 // Sets the default font's family name, weight, width, slant and size.
1062 if( mFontDefaults->familyDefined )
1064 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1065 inputStyle.isFamilyDefined = true;
1068 if( mFontDefaults->weightDefined )
1070 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1071 inputStyle.isWeightDefined = true;
1074 if( mFontDefaults->widthDefined )
1076 inputStyle.width = mFontDefaults->mFontDescription.width;
1077 inputStyle.isWidthDefined = true;
1080 if( mFontDefaults->slantDefined )
1082 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1083 inputStyle.isSlantDefined = true;
1086 if( mFontDefaults->sizeDefined )
1088 inputStyle.size = mFontDefaults->mDefaultPointSize;
1089 inputStyle.isSizeDefined = true;
1094 float Controller::Impl::GetDefaultFontLineHeight()
1096 FontId defaultFontId = 0u;
1097 if( NULL == mFontDefaults )
1099 TextAbstraction::FontDescription fontDescription;
1100 defaultFontId = mFontClient.GetFontId( fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale );
1104 defaultFontId = mFontDefaults->GetFontId( mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale );
1107 Text::FontMetrics fontMetrics;
1108 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1110 return( fontMetrics.ascender - fontMetrics.descender );
1113 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
1115 if( nullptr == mEventData )
1117 // Nothing to do if there is no text.
1121 if( mEventData->mSelectionEnabled && (pStart || pEnd))
1123 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1127 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1131 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1134 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1136 ChangeState( EventData::EDITING );
1137 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1138 mEventData->mUpdateCursorPosition = true;
1142 ChangeState( EventData::SELECTING );
1143 mEventData->mUpdateHighlightBox = true;
1144 mEventData->mUpdateLeftSelectionPosition = true;
1145 mEventData->mUpdateRightSelectionPosition = true;
1150 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1152 if( nullptr == mEventData )
1156 return mEventData->mPrimaryCursorPosition;
1159 bool Controller::Impl::SetPrimaryCursorPosition( CharacterIndex index )
1161 if( nullptr == mEventData )
1163 // Nothing to do if there is no text.
1167 if( mEventData->mPrimaryCursorPosition == index )
1169 // Nothing for same cursor position.
1173 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1174 mEventData->mPrimaryCursorPosition = std::min(index, length);
1175 ChangeState( EventData::EDITING );
1176 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1177 mEventData->mUpdateCursorPosition = true;
1178 ScrollTextToMatchCursor();
1182 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1188 range.first = mEventData->mLeftSelectionPosition;
1189 range.second = mEventData->mRightSelectionPosition;
1195 bool Controller::Impl::IsEditable() const
1197 return mEventData && mEventData->mEditingEnabled;
1200 void Controller::Impl::SetEditable( bool editable )
1204 mEventData->mEditingEnabled = editable;
1208 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1210 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1212 // Nothing to select if handles are in the same place.
1213 selectedText.clear();
1217 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1219 //Get start and end position of selection
1220 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1221 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1223 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1224 const Length numberOfCharacters = utf32Characters.Count();
1226 // Validate the start and end selection points
1227 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1229 //Get text as a UTF8 string
1230 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1232 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1234 // Keep a copy of the current input style.
1235 InputStyle currentInputStyle;
1236 currentInputStyle.Copy( mEventData->mInputStyle );
1238 // Set as input style the style of the first deleted character.
1239 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1241 // Compare if the input style has changed.
1242 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1244 if( hasInputStyleChanged )
1246 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1247 // Queue the input style changed signal.
1248 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1251 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1253 // Mark the paragraphs to be updated.
1254 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1256 mTextUpdateInfo.mCharacterIndex = 0;
1257 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1258 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1259 mTextUpdateInfo.mClearAll = true;
1263 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1264 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1267 // Delete text between handles
1268 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1269 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1270 utf32Characters.Erase( first, last );
1272 // Will show the cursor at the first character of the selection.
1273 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1277 // Will show the cursor at the last character of the selection.
1278 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1281 mEventData->mDecoratorUpdated = true;
1285 void Controller::Impl::SetSelection( int start, int end )
1287 mEventData->mLeftSelectionPosition = start;
1288 mEventData->mRightSelectionPosition = end;
1289 mEventData->mUpdateCursorPosition = true;
1292 std::pair< int, int > Controller::Impl::GetSelectionIndexes() const
1294 return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition };
1297 void Controller::Impl::ShowClipboard()
1301 mClipboard.ShowClipboard();
1305 void Controller::Impl::HideClipboard()
1307 if( mClipboard && mClipboardHideEnabled )
1309 mClipboard.HideClipboard();
1313 void Controller::Impl::SetClipboardHideEnable(bool enable)
1315 mClipboardHideEnabled = enable;
1318 bool Controller::Impl::CopyStringToClipboard( const std::string& source )
1320 //Send string to clipboard
1321 return ( mClipboard && mClipboard.SetItem( source ) );
1324 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1326 std::string selectedText;
1327 RetrieveSelection( selectedText, deleteAfterSending );
1328 CopyStringToClipboard( selectedText );
1329 ChangeState( EventData::EDITING );
1332 void Controller::Impl::RequestGetTextFromClipboard()
1336 mClipboard.RequestItem();
1340 void Controller::Impl::RepositionSelectionHandles()
1342 SelectionHandleController::Reposition(*this);
1344 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
1346 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1349 void Controller::Impl::SetPopupButtons()
1352 * Sets the Popup buttons to be shown depending on State.
1354 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1356 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1359 bool isEditable = IsEditable();
1360 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1362 if( EventData::SELECTING == mEventData->mState )
1364 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
1367 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
1370 if( !IsClipboardEmpty() )
1374 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1376 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1379 if( !mEventData->mAllTextSelected )
1381 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1384 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1386 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1388 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1391 if( !IsClipboardEmpty() )
1395 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1397 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1400 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1402 if ( !IsClipboardEmpty() )
1406 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1408 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1412 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1415 void Controller::Impl::ChangeState( EventData::State newState )
1417 if( NULL == mEventData )
1419 // Nothing to do if there is no text input.
1423 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1425 if( mEventData->mState != newState )
1427 mEventData->mPreviousState = mEventData->mState;
1428 mEventData->mState = newState;
1430 switch( mEventData->mState )
1432 case EventData::INACTIVE:
1434 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1435 mEventData->mDecorator->StopCursorBlink();
1436 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1437 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1438 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1439 mEventData->mDecorator->SetHighlightActive( false );
1440 mEventData->mDecorator->SetPopupActive( false );
1441 mEventData->mDecoratorUpdated = true;
1444 case EventData::INTERRUPTED:
1446 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1447 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1448 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1449 mEventData->mDecorator->SetHighlightActive( false );
1450 mEventData->mDecorator->SetPopupActive( false );
1451 mEventData->mDecoratorUpdated = true;
1454 case EventData::SELECTING:
1456 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1457 mEventData->mDecorator->StopCursorBlink();
1458 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1459 if ( mEventData->mGrabHandleEnabled )
1461 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1462 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1464 mEventData->mDecorator->SetHighlightActive( true );
1465 if( mEventData->mGrabHandlePopupEnabled )
1468 mEventData->mDecorator->SetPopupActive( true );
1470 mEventData->mDecoratorUpdated = true;
1473 case EventData::EDITING:
1475 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1476 if( mEventData->mCursorBlinkEnabled )
1478 mEventData->mDecorator->StartCursorBlink();
1480 // Grab handle is not shown until a tap is received whilst EDITING
1481 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1482 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1483 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1484 mEventData->mDecorator->SetHighlightActive( false );
1485 if( mEventData->mGrabHandlePopupEnabled )
1487 mEventData->mDecorator->SetPopupActive( false );
1489 mEventData->mDecoratorUpdated = true;
1492 case EventData::EDITING_WITH_POPUP:
1494 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1496 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1497 if( mEventData->mCursorBlinkEnabled )
1499 mEventData->mDecorator->StartCursorBlink();
1501 if( mEventData->mSelectionEnabled )
1503 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1504 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1505 mEventData->mDecorator->SetHighlightActive( false );
1507 else if ( mEventData->mGrabHandleEnabled )
1509 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1511 if( mEventData->mGrabHandlePopupEnabled )
1514 mEventData->mDecorator->SetPopupActive( true );
1516 mEventData->mDecoratorUpdated = true;
1519 case EventData::EDITING_WITH_GRAB_HANDLE:
1521 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1523 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1524 if( mEventData->mCursorBlinkEnabled )
1526 mEventData->mDecorator->StartCursorBlink();
1528 // Grab handle is not shown until a tap is received whilst EDITING
1529 if ( mEventData->mGrabHandleEnabled )
1531 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1533 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1534 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1535 mEventData->mDecorator->SetHighlightActive( false );
1536 if( mEventData->mGrabHandlePopupEnabled )
1538 mEventData->mDecorator->SetPopupActive( false );
1540 mEventData->mDecoratorUpdated = true;
1543 case EventData::SELECTION_HANDLE_PANNING:
1545 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1546 mEventData->mDecorator->StopCursorBlink();
1547 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1548 if ( mEventData->mGrabHandleEnabled )
1550 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1551 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1553 mEventData->mDecorator->SetHighlightActive( true );
1554 if( mEventData->mGrabHandlePopupEnabled )
1556 mEventData->mDecorator->SetPopupActive( false );
1558 mEventData->mDecoratorUpdated = true;
1561 case EventData::GRAB_HANDLE_PANNING:
1563 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1565 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1566 if( mEventData->mCursorBlinkEnabled )
1568 mEventData->mDecorator->StartCursorBlink();
1570 if ( mEventData->mGrabHandleEnabled )
1572 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1574 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1575 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1576 mEventData->mDecorator->SetHighlightActive( false );
1577 if( mEventData->mGrabHandlePopupEnabled )
1579 mEventData->mDecorator->SetPopupActive( false );
1581 mEventData->mDecoratorUpdated = true;
1584 case EventData::EDITING_WITH_PASTE_POPUP:
1586 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1588 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1589 if( mEventData->mCursorBlinkEnabled )
1591 mEventData->mDecorator->StartCursorBlink();
1594 if ( mEventData->mGrabHandleEnabled )
1596 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1598 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1599 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1600 mEventData->mDecorator->SetHighlightActive( false );
1602 if( mEventData->mGrabHandlePopupEnabled )
1605 mEventData->mDecorator->SetPopupActive( true );
1607 mEventData->mDecoratorUpdated = true;
1610 case EventData::TEXT_PANNING:
1612 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1613 mEventData->mDecorator->StopCursorBlink();
1614 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1615 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
1616 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
1618 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1619 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1620 mEventData->mDecorator->SetHighlightActive( true );
1623 if( mEventData->mGrabHandlePopupEnabled )
1625 mEventData->mDecorator->SetPopupActive( false );
1628 mEventData->mDecoratorUpdated = true;
1635 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1636 CursorInfo& cursorInfo )
1638 if( !IsShowingRealText() )
1640 // Do not want to use the place-holder text to set the cursor position.
1642 // Use the line's height of the font's family set to set the cursor's size.
1643 // If there is no font's family set, use the default font.
1644 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1646 cursorInfo.lineOffset = 0.f;
1647 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1648 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1651 if( mModel->mMatchSystemLanguageDirection )
1653 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1656 switch( mModel->mHorizontalAlignment )
1658 case Text::HorizontalAlignment::BEGIN :
1662 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1666 cursorInfo.primaryPosition.x = 0.f;
1670 case Text::HorizontalAlignment::CENTER:
1672 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
1675 case Text::HorizontalAlignment::END:
1679 cursorInfo.primaryPosition.x = 0.f;
1683 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1689 // Nothing else to do.
1693 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
1694 GetCursorPositionParameters parameters;
1695 parameters.visualModel = mModel->mVisualModel;
1696 parameters.logicalModel = mModel->mLogicalModel;
1697 parameters.metrics = mMetrics;
1698 parameters.logical = logical;
1699 parameters.isMultiline = isMultiLine;
1701 Text::GetCursorPosition( parameters,
1704 // Adds Outline offset.
1705 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
1706 cursorInfo.primaryPosition.x += outlineWidth;
1707 cursorInfo.primaryPosition.y += outlineWidth;
1708 cursorInfo.secondaryPosition.x += outlineWidth;
1709 cursorInfo.secondaryPosition.y += outlineWidth;
1713 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1715 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1716 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1718 if( 0.f > cursorInfo.primaryPosition.x )
1720 cursorInfo.primaryPosition.x = 0.f;
1723 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
1724 if( cursorInfo.primaryPosition.x > edgeWidth )
1726 cursorInfo.primaryPosition.x = edgeWidth;
1731 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1733 if( NULL == mEventData )
1735 // Nothing to do if there is no text input.
1739 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1741 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1742 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1744 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1745 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1747 if( numberOfCharacters > 1u )
1749 const Script script = mModel->mLogicalModel->GetScript( index );
1750 if( HasLigatureMustBreak( script ) )
1752 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1753 numberOfCharacters = 1u;
1758 while( 0u == numberOfCharacters )
1761 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1765 if( index < mEventData->mPrimaryCursorPosition )
1767 cursorIndex -= numberOfCharacters;
1771 cursorIndex += numberOfCharacters;
1774 // Will update the cursor hook position.
1775 mEventData->mUpdateCursorHookPosition = true;
1780 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1782 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1783 if( NULL == mEventData )
1785 // Nothing to do if there is no text input.
1786 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1790 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1792 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
1794 // Sets the cursor position.
1795 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1798 cursorInfo.primaryCursorHeight,
1799 cursorInfo.lineHeight );
1800 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1802 if( mEventData->mUpdateGrabHandlePosition )
1804 // Sets the grab handle position.
1805 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1807 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1808 cursorInfo.lineHeight );
1811 if( cursorInfo.isSecondaryCursor )
1813 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1814 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1815 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1816 cursorInfo.secondaryCursorHeight,
1817 cursorInfo.lineHeight );
1818 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
1821 // Set which cursors are active according the state.
1822 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1824 if( cursorInfo.isSecondaryCursor )
1826 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1830 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1835 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1838 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1841 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
1842 const CursorInfo& cursorInfo )
1844 SelectionHandleController::Update(*this, handleType, cursorInfo);
1847 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
1849 // Clamp between -space & -alignment offset.
1851 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
1853 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
1854 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
1855 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1857 mEventData->mDecoratorUpdated = true;
1861 mModel->mScrollPosition.x = 0.f;
1865 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
1867 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1869 // Nothing to do if the text is single line.
1873 // Clamp between -space & 0.
1874 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
1876 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
1877 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
1878 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
1880 mEventData->mDecoratorUpdated = true;
1884 mModel->mScrollPosition.y = 0.f;
1888 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
1890 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
1892 // position is in actor's coords.
1893 const float positionEndX = position.x + cursorWidth;
1894 const float positionEndY = position.y + lineHeight;
1896 // Transform the position to decorator coords.
1897 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1898 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1900 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1901 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1903 if( decoratorPositionBeginX < 0.f )
1905 mModel->mScrollPosition.x = -position.x;
1907 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
1909 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1912 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
1914 if( decoratorPositionBeginY < 0.f )
1916 mModel->mScrollPosition.y = -position.y;
1918 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
1920 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1925 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
1927 // Get the current cursor position in decorator coords.
1928 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1930 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
1934 // Calculate the offset to match the cursor position before the character was deleted.
1935 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1937 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1938 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
1940 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
1941 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1945 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
1946 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
1948 // Makes the new cursor position visible if needed.
1949 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
1952 void Controller::Impl::ScrollTextToMatchCursor()
1954 CursorInfo cursorInfo;
1955 GetCursorPosition( mEventData->mPrimaryCursorPosition, cursorInfo );
1956 ScrollTextToMatchCursor(cursorInfo);
1959 void Controller::Impl::RequestRelayout()
1961 if( NULL != mControlInterface )
1963 mControlInterface->RequestTextRelayout();
1967 Actor Controller::Impl::CreateBackgroundActor()
1969 // NOTE: Currently we only support background color for one line left-to-right text,
1970 // so the following calculation is based on one line left-to-right text only!
1974 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1975 if( numberOfGlyphs > 0u )
1977 Vector<GlyphInfo> glyphs;
1978 glyphs.Resize( numberOfGlyphs );
1980 Vector<Vector2> positions;
1981 positions.Resize( numberOfGlyphs );
1983 // Get the line where the glyphs are laid-out.
1984 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1985 float alignmentOffset = lineRun->alignmentOffset;
1986 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
1992 glyphs.Resize( numberOfGlyphs );
1993 positions.Resize( numberOfGlyphs );
1995 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
1996 const Vector2* const positionsBuffer = positions.Begin();
1998 BackgroundMesh mesh;
1999 mesh.mVertices.Reserve( 4u * glyphs.Size() );
2000 mesh.mIndices.Reserve( 6u * glyphs.Size() );
2002 const Vector2 textSize = mView.GetLayoutSize();
2004 const float offsetX = textSize.width * 0.5f;
2005 const float offsetY = textSize.height * 0.5f;
2007 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2008 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2009 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2012 uint32_t numberOfQuads = 0u;
2014 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
2016 const GlyphInfo& glyph = *( glyphsBuffer + i );
2018 // Get the background color of the character.
2019 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2020 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
2021 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
2023 // Only create quads for glyphs with a background color
2024 if ( backgroundColor != Color::TRANSPARENT )
2026 const Vector2 position = *( positionsBuffer + i );
2028 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
2030 quad.x = position.x;
2032 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2033 quad.w = textSize.height;
2035 else if ( i == 0u ) // The first glyph in the whole text
2037 quad.x = position.x;
2039 quad.z = quad.x - glyph.xBearing + glyph.advance;
2040 quad.w = textSize.height;
2042 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
2044 quad.x = position.x - glyph.xBearing;
2046 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2047 quad.w = textSize.height;
2049 else // The glyph in the middle of the text
2051 quad.x = position.x - glyph.xBearing;
2053 quad.z = quad.x + glyph.advance;
2054 quad.w = textSize.height;
2057 BackgroundVertex vertex;
2060 vertex.mPosition.x = quad.x - offsetX;
2061 vertex.mPosition.y = quad.y - offsetY;
2062 vertex.mColor = backgroundColor;
2063 mesh.mVertices.PushBack( vertex );
2066 vertex.mPosition.x = quad.z - offsetX;
2067 vertex.mPosition.y = quad.y - offsetY;
2068 vertex.mColor = backgroundColor;
2069 mesh.mVertices.PushBack( vertex );
2072 vertex.mPosition.x = quad.x - offsetX;
2073 vertex.mPosition.y = quad.w - offsetY;
2074 vertex.mColor = backgroundColor;
2075 mesh.mVertices.PushBack( vertex );
2078 vertex.mPosition.x = quad.z - offsetX;
2079 vertex.mPosition.y = quad.w - offsetY;
2080 vertex.mColor = backgroundColor;
2081 mesh.mVertices.PushBack( vertex );
2083 // Six indices in counter clockwise winding
2084 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2085 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
2086 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2087 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2088 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
2089 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2095 // Only create the background actor if there are glyphs with background color
2096 if ( mesh.mVertices.Count() > 0u )
2098 Property::Map quadVertexFormat;
2099 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2100 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2102 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2103 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2105 Geometry quadGeometry = Geometry::New();
2106 quadGeometry.AddVertexBuffer( quadVertices );
2107 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2109 if( !mShaderBackground )
2111 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
2114 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2115 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2116 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2118 actor = Actor::New();
2119 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2120 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2121 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2122 actor.SetProperty( Actor::Property::SIZE, textSize );
2123 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2124 actor.AddRenderer( renderer );
2133 } // namespace Toolkit