2 * Copyright (c) 2017 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/adaptor-framework/key.h>
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/color-segmentation.h>
32 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
45 * @brief Struct used to calculate the selection box.
47 struct SelectionBoxInfo
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
59 const float MAX_FLOAT = std::numeric_limits<float>::max();
60 const float MIN_FLOAT = std::numeric_limits<float>::min();
61 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
63 #define MAKE_SHADER(A)#A
65 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
66 attribute mediump vec2 aPosition;
67 attribute mediump vec4 aColor;
68 varying mediump vec4 vColor;
69 uniform highp mat4 uMvpMatrix;
73 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
74 gl_Position = uMvpMatrix * position;
79 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
80 varying mediump vec4 vColor;
81 uniform lowp vec4 uColor;
85 gl_FragColor = vColor * uColor;
89 struct BackgroundVertex
91 Vector2 mPosition; ///< Vertex posiiton
92 Vector4 mColor; ///< Vertex color
97 Vector< BackgroundVertex > mVertices; ///< container of vertices
98 Vector< unsigned short > mIndices; ///< container of indices
112 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
113 : mDecorator( decorator ),
114 mInputMethodContext( inputMethodContext ),
115 mPlaceholderFont( NULL ),
116 mPlaceholderTextActive(),
117 mPlaceholderTextInactive(),
118 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
120 mInputStyleChangedQueue(),
121 mPreviousState( INACTIVE ),
123 mPrimaryCursorPosition( 0u ),
124 mLeftSelectionPosition( 0u ),
125 mRightSelectionPosition( 0u ),
126 mPreEditStartPosition( 0u ),
127 mPreEditLength( 0u ),
128 mCursorHookPositionX( 0.f ),
129 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
130 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
131 mIsShowingPlaceholderText( false ),
132 mPreEditFlag( false ),
133 mDecoratorUpdated( false ),
134 mCursorBlinkEnabled( true ),
135 mGrabHandleEnabled( true ),
136 mGrabHandlePopupEnabled( true ),
137 mSelectionEnabled( true ),
138 mUpdateCursorHookPosition( false ),
139 mUpdateCursorPosition( false ),
140 mUpdateGrabHandlePosition( false ),
141 mUpdateLeftSelectionPosition( false ),
142 mUpdateRightSelectionPosition( false ),
143 mIsLeftHandleSelected( false ),
144 mIsRightHandleSelected( false ),
145 mUpdateHighlightBox( false ),
146 mScrollAfterUpdatePosition( false ),
147 mScrollAfterDelete( false ),
148 mAllTextSelected( false ),
149 mUpdateInputStyle( false ),
150 mPasswordInput( false ),
151 mCheckScrollAmount( false ),
152 mIsPlaceholderPixelSize( false ),
153 mIsPlaceholderElideEnabled( false ),
154 mPlaceholderEllipsisFlag( false ),
155 mShiftSelectionFlag( true ),
156 mUpdateAlignment( false )
160 EventData::~EventData()
163 bool Controller::Impl::ProcessInputEvents()
165 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
166 if( NULL == mEventData )
168 // Nothing to do if there is no text input.
169 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
173 if( mEventData->mDecorator )
175 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
176 iter != mEventData->mEventQueue.end();
181 case Event::CURSOR_KEY_EVENT:
183 OnCursorKeyEvent( *iter );
186 case Event::TAP_EVENT:
191 case Event::LONG_PRESS_EVENT:
193 OnLongPressEvent( *iter );
196 case Event::PAN_EVENT:
201 case Event::GRAB_HANDLE_EVENT:
202 case Event::LEFT_SELECTION_HANDLE_EVENT:
203 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
205 OnHandleEvent( *iter );
210 OnSelectEvent( *iter );
213 case Event::SELECT_ALL:
222 if( mEventData->mUpdateCursorPosition ||
223 mEventData->mUpdateHighlightBox )
225 NotifyInputMethodContext();
228 // The cursor must also be repositioned after inserts into the model
229 if( mEventData->mUpdateCursorPosition )
231 // Updates the cursor position and scrolls the text to make it visible.
232 CursorInfo cursorInfo;
233 // Calculate the cursor position from the new cursor index.
234 GetCursorPosition( mEventData->mPrimaryCursorPosition,
237 if( mEventData->mUpdateCursorHookPosition )
239 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
240 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
241 mEventData->mUpdateCursorHookPosition = false;
244 // Scroll first the text after delete ...
245 if( mEventData->mScrollAfterDelete )
247 ScrollTextToMatchCursor( cursorInfo );
250 // ... then, text can be scrolled to make the cursor visible.
251 if( mEventData->mScrollAfterUpdatePosition )
253 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
254 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
256 mEventData->mScrollAfterUpdatePosition = false;
257 mEventData->mScrollAfterDelete = false;
259 UpdateCursorPosition( cursorInfo );
261 mEventData->mDecoratorUpdated = true;
262 mEventData->mUpdateCursorPosition = false;
263 mEventData->mUpdateGrabHandlePosition = false;
267 CursorInfo leftHandleInfo;
268 CursorInfo rightHandleInfo;
270 if( mEventData->mUpdateHighlightBox )
272 GetCursorPosition( mEventData->mLeftSelectionPosition,
275 GetCursorPosition( mEventData->mRightSelectionPosition,
278 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
280 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
282 CursorInfo& infoLeft = leftHandleInfo;
284 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
285 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
287 CursorInfo& infoRight = rightHandleInfo;
289 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
290 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
294 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
296 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
297 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
302 if( mEventData->mUpdateLeftSelectionPosition )
304 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
308 mEventData->mDecoratorUpdated = true;
309 mEventData->mUpdateLeftSelectionPosition = false;
312 if( mEventData->mUpdateRightSelectionPosition )
314 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
318 mEventData->mDecoratorUpdated = true;
319 mEventData->mUpdateRightSelectionPosition = false;
322 if( mEventData->mUpdateHighlightBox )
324 RepositionSelectionHandles();
326 mEventData->mUpdateLeftSelectionPosition = false;
327 mEventData->mUpdateRightSelectionPosition = false;
328 mEventData->mUpdateHighlightBox = false;
329 mEventData->mIsLeftHandleSelected = false;
330 mEventData->mIsRightHandleSelected = false;
333 mEventData->mScrollAfterUpdatePosition = false;
336 if( mEventData->mUpdateInputStyle )
338 // Keep a copy of the current input style.
339 InputStyle currentInputStyle;
340 currentInputStyle.Copy( mEventData->mInputStyle );
342 // Set the default style first.
343 RetrieveDefaultInputStyle( mEventData->mInputStyle );
345 // Get the character index from the cursor index.
346 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
348 // Retrieve the style from the style runs stored in the logical model.
349 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
351 // Compare if the input style has changed.
352 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
354 if( hasInputStyleChanged )
356 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
357 // Queue the input style changed signal.
358 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
361 mEventData->mUpdateInputStyle = false;
364 mEventData->mEventQueue.clear();
366 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
368 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
369 mEventData->mDecoratorUpdated = false;
371 return decoratorUpdated;
374 void Controller::Impl::NotifyInputMethodContext()
376 if( mEventData && mEventData->mInputMethodContext )
378 CharacterIndex cursorPosition = GetLogicalCursorPosition();
380 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
382 // Update the cursor position by removing the initial white spaces.
383 if( cursorPosition < numberOfWhiteSpaces )
389 cursorPosition -= numberOfWhiteSpaces;
392 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
393 mEventData->mInputMethodContext.NotifyCursorPosition();
397 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
399 if ( mEventData && mEventData->mInputMethodContext )
401 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
402 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
406 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
408 CharacterIndex cursorPosition = 0u;
412 if( ( EventData::SELECTING == mEventData->mState ) ||
413 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
415 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
419 cursorPosition = mEventData->mPrimaryCursorPosition;
423 return cursorPosition;
426 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
428 Length numberOfWhiteSpaces = 0u;
430 // Get the buffer to the text.
431 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
433 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
434 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
436 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
442 return numberOfWhiteSpaces;
445 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
447 // Get the total number of characters.
448 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
450 // Retrieve the text.
451 if( 0u != numberOfCharacters )
453 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
457 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
459 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
460 mTextUpdateInfo.mStartGlyphIndex = 0u;
461 mTextUpdateInfo.mStartLineIndex = 0u;
462 numberOfCharacters = 0u;
464 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
465 if( 0u == numberOfParagraphs )
467 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
468 numberOfCharacters = 0u;
470 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
472 // Nothing else to do if there are no paragraphs.
476 // Find the paragraphs to be updated.
477 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
478 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
480 // Text is being added at the end of the current text.
481 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
483 // Text is being added in a new paragraph after the last character of the text.
484 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
485 numberOfCharacters = 0u;
486 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
488 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
489 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
491 // Nothing else to do;
495 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
499 Length numberOfCharactersToUpdate = 0u;
500 if( mTextUpdateInfo.mFullRelayoutNeeded )
502 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
506 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
508 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
509 numberOfCharactersToUpdate,
510 paragraphsToBeUpdated );
513 if( 0u != paragraphsToBeUpdated.Count() )
515 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
516 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
517 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
519 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
520 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
522 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
523 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
524 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
525 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
527 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
528 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
530 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
534 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
538 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
539 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
542 void Controller::Impl::ClearFullModelData( OperationsMask operations )
544 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
546 mModel->mLogicalModel->mLineBreakInfo.Clear();
547 mModel->mLogicalModel->mParagraphInfo.Clear();
550 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
552 mModel->mLogicalModel->mScriptRuns.Clear();
555 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
557 mModel->mLogicalModel->mFontRuns.Clear();
560 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
562 if( NO_OPERATION != ( BIDI_INFO & operations ) )
564 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
565 mModel->mLogicalModel->mCharacterDirections.Clear();
568 if( NO_OPERATION != ( REORDER & operations ) )
570 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
571 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
572 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
576 BidirectionalLineInfoRun& bidiLineInfo = *it;
578 free( bidiLineInfo.visualToLogicalMap );
579 bidiLineInfo.visualToLogicalMap = NULL;
581 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
585 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
587 mModel->mVisualModel->mGlyphs.Clear();
588 mModel->mVisualModel->mGlyphsToCharacters.Clear();
589 mModel->mVisualModel->mCharactersToGlyph.Clear();
590 mModel->mVisualModel->mCharactersPerGlyph.Clear();
591 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
592 mModel->mVisualModel->mGlyphPositions.Clear();
595 if( NO_OPERATION != ( LAYOUT & operations ) )
597 mModel->mVisualModel->mLines.Clear();
600 if( NO_OPERATION != ( COLOR & operations ) )
602 mModel->mVisualModel->mColorIndices.Clear();
603 mModel->mVisualModel->mBackgroundColorIndices.Clear();
607 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
609 const CharacterIndex endIndexPlusOne = endIndex + 1u;
611 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
613 // Clear the line break info.
614 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
616 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
617 lineBreakInfoBuffer + endIndexPlusOne );
619 // Clear the paragraphs.
620 ClearCharacterRuns( startIndex,
622 mModel->mLogicalModel->mParagraphInfo );
625 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
627 // Clear the scripts.
628 ClearCharacterRuns( startIndex,
630 mModel->mLogicalModel->mScriptRuns );
633 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
636 ClearCharacterRuns( startIndex,
638 mModel->mLogicalModel->mFontRuns );
641 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
643 if( NO_OPERATION != ( BIDI_INFO & operations ) )
645 // Clear the bidirectional paragraph info.
646 ClearCharacterRuns( startIndex,
648 mModel->mLogicalModel->mBidirectionalParagraphInfo );
650 // Clear the character's directions.
651 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
653 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
654 characterDirectionsBuffer + endIndexPlusOne );
657 if( NO_OPERATION != ( REORDER & operations ) )
659 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
660 uint32_t endRemoveIndex = startRemoveIndex;
661 ClearCharacterRuns( startIndex,
663 mModel->mLogicalModel->mBidirectionalLineInfo,
667 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
669 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
670 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
671 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
675 BidirectionalLineInfoRun& bidiLineInfo = *it;
677 free( bidiLineInfo.visualToLogicalMap );
678 bidiLineInfo.visualToLogicalMap = NULL;
681 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
682 bidirectionalLineInfoBuffer + endRemoveIndex );
687 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
689 const CharacterIndex endIndexPlusOne = endIndex + 1u;
690 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
692 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
693 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
694 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
696 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
697 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
699 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
701 // Update the character to glyph indices.
702 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
703 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
707 CharacterIndex& index = *it;
708 index -= numberOfGlyphsRemoved;
711 // Clear the character to glyph conversion table.
712 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
713 charactersToGlyphBuffer + endIndexPlusOne );
715 // Clear the glyphs per character table.
716 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
717 glyphsPerCharacterBuffer + endIndexPlusOne );
719 // Clear the glyphs buffer.
720 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
721 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
722 glyphsBuffer + endGlyphIndexPlusOne );
724 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
726 // Update the glyph to character indices.
727 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
728 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
732 CharacterIndex& index = *it;
733 index -= numberOfCharactersRemoved;
736 // Clear the glyphs to characters buffer.
737 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
738 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
740 // Clear the characters per glyph buffer.
741 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
742 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
743 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
745 // Clear the positions buffer.
746 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
747 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
748 positionsBuffer + endGlyphIndexPlusOne );
751 if( NO_OPERATION != ( LAYOUT & operations ) )
754 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
755 uint32_t endRemoveIndex = startRemoveIndex;
756 ClearCharacterRuns( startIndex,
758 mModel->mVisualModel->mLines,
762 // Will update the glyph runs.
763 startRemoveIndex = mModel->mVisualModel->mLines.Count();
764 endRemoveIndex = startRemoveIndex;
765 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
766 endGlyphIndexPlusOne - 1u,
767 mModel->mVisualModel->mLines,
771 // Set the line index from where to insert the new laid-out lines.
772 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
774 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
775 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
776 linesBuffer + endRemoveIndex );
779 if( NO_OPERATION != ( COLOR & operations ) )
781 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
783 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
784 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
785 colorIndexBuffer + endGlyphIndexPlusOne );
788 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
790 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
791 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
792 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
797 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
799 if( mTextUpdateInfo.mClearAll ||
800 ( ( 0u == startIndex ) &&
801 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
803 ClearFullModelData( operations );
807 // Clear the model data related with characters.
808 ClearCharacterModelData( startIndex, endIndex, operations );
810 // Clear the model data related with glyphs.
811 ClearGlyphModelData( startIndex, endIndex, operations );
814 // The estimated number of lines. Used to avoid reallocations when layouting.
815 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
817 mModel->mVisualModel->ClearCaches();
820 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
822 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
824 // Calculate the operations to be done.
825 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
827 if( NO_OPERATION == operations )
829 // Nothing to do if no operations are pending and required.
833 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
834 Vector<Character> displayCharacters;
835 bool useHiddenText = false;
836 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
838 mHiddenInput->Substitute( srcCharacters,displayCharacters );
839 useHiddenText = true;
842 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
843 const Length numberOfCharacters = utf32Characters.Count();
845 // Index to the first character of the first paragraph to be updated.
846 CharacterIndex startIndex = 0u;
847 // Number of characters of the paragraphs to be removed.
848 Length paragraphCharacters = 0u;
850 CalculateTextUpdateIndices( paragraphCharacters );
852 // Check whether the indices for updating the text is valid
853 if ( numberOfCharacters > 0u &&
854 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
855 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
857 std::string currentText;
858 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
860 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
861 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
863 // Dump mTextUpdateInfo
864 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
865 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
866 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
867 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
868 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
869 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
870 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
871 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
872 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
873 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
874 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
875 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
876 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
881 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
883 if( mTextUpdateInfo.mClearAll ||
884 ( 0u != paragraphCharacters ) )
886 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
889 mTextUpdateInfo.mClearAll = false;
891 // Whether the model is updated.
892 bool updated = false;
894 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
895 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
897 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
899 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
900 // calculate the bidirectional info for each 'paragraph'.
901 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
902 // is not shaped together).
903 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
905 SetLineBreakInfo( utf32Characters,
907 requestedNumberOfCharacters,
910 // Create the paragraph info.
911 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
912 requestedNumberOfCharacters );
916 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
917 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
919 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
920 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
922 if( getScripts || validateFonts )
924 // Validates the fonts assigned by the application or assigns default ones.
925 // It makes sure all the characters are going to be rendered by the correct font.
926 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
930 // Retrieves the scripts used in the text.
931 multilanguageSupport.SetScripts( utf32Characters,
933 requestedNumberOfCharacters,
939 // Validate the fonts set through the mark-up string.
940 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
942 // Get the default font's description.
943 TextAbstraction::FontDescription defaultFontDescription;
944 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
946 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
948 // If the placeholder font is set specifically, only placeholder font is changed.
949 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
950 if( mEventData->mPlaceholderFont->sizeDefined )
952 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
955 else if( NULL != mFontDefaults )
957 // Set the normal font and the placeholder font.
958 defaultFontDescription = mFontDefaults->mFontDescription;
960 if( mTextFitEnabled )
962 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
966 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
970 // Validates the fonts. If there is a character with no assigned font it sets a default one.
971 // After this call, fonts are validated.
972 multilanguageSupport.ValidateFonts( utf32Characters,
975 defaultFontDescription,
978 requestedNumberOfCharacters,
984 Vector<Character> mirroredUtf32Characters;
985 bool textMirrored = false;
986 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
987 if( NO_OPERATION != ( BIDI_INFO & operations ) )
989 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
990 bidirectionalInfo.Reserve( numberOfParagraphs );
992 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
993 SetBidirectionalInfo( utf32Characters,
997 requestedNumberOfCharacters,
999 mModel->mMatchSystemLanguageDirection,
1002 if( 0u != bidirectionalInfo.Count() )
1004 // Only set the character directions if there is right to left characters.
1005 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1006 GetCharactersDirection( bidirectionalInfo,
1009 requestedNumberOfCharacters,
1012 // This paragraph has right to left text. Some characters may need to be mirrored.
1013 // TODO: consider if the mirrored string can be stored as well.
1015 textMirrored = GetMirroredText( utf32Characters,
1019 requestedNumberOfCharacters,
1020 mirroredUtf32Characters );
1024 // There is no right to left characters. Clear the directions vector.
1025 mModel->mLogicalModel->mCharacterDirections.Clear();
1030 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1031 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1032 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1033 Vector<GlyphIndex> newParagraphGlyphs;
1034 newParagraphGlyphs.Reserve( numberOfParagraphs );
1036 const Length currentNumberOfGlyphs = glyphs.Count();
1037 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1039 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1041 ShapeText( textToShape,
1046 mTextUpdateInfo.mStartGlyphIndex,
1047 requestedNumberOfCharacters,
1049 glyphsToCharactersMap,
1051 newParagraphGlyphs );
1053 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1054 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1055 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1059 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1061 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1063 GlyphInfo* glyphsBuffer = glyphs.Begin();
1064 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1066 // Update the width and advance of all new paragraph characters.
1067 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1069 const GlyphIndex index = *it;
1070 GlyphInfo& glyph = *( glyphsBuffer + index );
1072 glyph.xBearing = 0.f;
1074 glyph.advance = 0.f;
1079 if( ( NULL != mEventData ) &&
1080 mEventData->mPreEditFlag &&
1081 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1083 Dali::InputMethodContext::PreeditStyle type = mEventData->mInputMethodContext.GetPreeditStyle();
1087 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1089 // Add the underline for the pre-edit text.
1090 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1091 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1093 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1094 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1095 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1096 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1098 GlyphRun underlineRun;
1099 underlineRun.glyphIndex = glyphStart;
1100 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1102 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1105 // TODO : At this moment, other styles for preedit are not implemented yet.
1106 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1107 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1108 case Dali::InputMethodContext::PreeditStyle::NONE:
1114 if( NO_OPERATION != ( COLOR & operations ) )
1116 // Set the color runs in glyphs.
1117 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1118 mModel->mVisualModel->mCharactersToGlyph,
1119 mModel->mVisualModel->mGlyphsPerCharacter,
1121 mTextUpdateInfo.mStartGlyphIndex,
1122 requestedNumberOfCharacters,
1123 mModel->mVisualModel->mColors,
1124 mModel->mVisualModel->mColorIndices );
1126 // Set the background color runs in glyphs.
1127 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1128 mModel->mVisualModel->mCharactersToGlyph,
1129 mModel->mVisualModel->mGlyphsPerCharacter,
1131 mTextUpdateInfo.mStartGlyphIndex,
1132 requestedNumberOfCharacters,
1133 mModel->mVisualModel->mBackgroundColors,
1134 mModel->mVisualModel->mBackgroundColorIndices );
1140 // The estimated number of lines. Used to avoid reallocations when layouting.
1141 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1143 // Set the previous number of characters for the next time the text is updated.
1144 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1149 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1151 // Sets the default text's color.
1152 inputStyle.textColor = mTextColor;
1153 inputStyle.isDefaultColor = true;
1155 inputStyle.familyName.clear();
1156 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1157 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1158 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1159 inputStyle.size = 0.f;
1161 inputStyle.lineSpacing = 0.f;
1163 inputStyle.underlineProperties.clear();
1164 inputStyle.shadowProperties.clear();
1165 inputStyle.embossProperties.clear();
1166 inputStyle.outlineProperties.clear();
1168 inputStyle.isFamilyDefined = false;
1169 inputStyle.isWeightDefined = false;
1170 inputStyle.isWidthDefined = false;
1171 inputStyle.isSlantDefined = false;
1172 inputStyle.isSizeDefined = false;
1174 inputStyle.isLineSpacingDefined = false;
1176 inputStyle.isUnderlineDefined = false;
1177 inputStyle.isShadowDefined = false;
1178 inputStyle.isEmbossDefined = false;
1179 inputStyle.isOutlineDefined = false;
1181 // Sets the default font's family name, weight, width, slant and size.
1184 if( mFontDefaults->familyDefined )
1186 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1187 inputStyle.isFamilyDefined = true;
1190 if( mFontDefaults->weightDefined )
1192 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1193 inputStyle.isWeightDefined = true;
1196 if( mFontDefaults->widthDefined )
1198 inputStyle.width = mFontDefaults->mFontDescription.width;
1199 inputStyle.isWidthDefined = true;
1202 if( mFontDefaults->slantDefined )
1204 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1205 inputStyle.isSlantDefined = true;
1208 if( mFontDefaults->sizeDefined )
1210 inputStyle.size = mFontDefaults->mDefaultPointSize;
1211 inputStyle.isSizeDefined = true;
1216 float Controller::Impl::GetDefaultFontLineHeight()
1218 FontId defaultFontId = 0u;
1219 if( NULL == mFontDefaults )
1221 TextAbstraction::FontDescription fontDescription;
1222 defaultFontId = mFontClient.GetFontId( fontDescription );
1226 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1229 Text::FontMetrics fontMetrics;
1230 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1232 return( fontMetrics.ascender - fontMetrics.descender );
1235 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1237 if( NULL == mEventData || !IsShowingRealText() )
1239 // Nothing to do if there is no text input.
1243 int keyCode = event.p1.mInt;
1244 bool isShiftModifier = event.p2.mBool;
1246 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1248 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1250 if( mEventData->mPrimaryCursorPosition > 0u )
1252 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1254 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1258 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1262 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1264 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1266 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1268 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1272 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1276 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1278 // Ignore Shift-Up for text selection for now.
1280 // Get first the line index of the current cursor position index.
1281 CharacterIndex characterIndex = 0u;
1283 if( mEventData->mPrimaryCursorPosition > 0u )
1285 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1288 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1289 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1291 // Retrieve the cursor position info.
1292 CursorInfo cursorInfo;
1293 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1296 // Get the line above.
1297 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1299 // Get the next hit 'y' point.
1300 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1302 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1303 bool matchedCharacter = false;
1304 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1305 mModel->mLogicalModel,
1307 mEventData->mCursorHookPositionX,
1309 CharacterHitTest::TAP,
1312 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1314 // Ignore Shift-Down for text selection for now.
1316 // Get first the line index of the current cursor position index.
1317 CharacterIndex characterIndex = 0u;
1319 if( mEventData->mPrimaryCursorPosition > 0u )
1321 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1324 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1326 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1328 // Retrieve the cursor position info.
1329 CursorInfo cursorInfo;
1330 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1333 // Get the line below.
1334 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1336 // Get the next hit 'y' point.
1337 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1339 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1340 bool matchedCharacter = false;
1341 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1342 mModel->mLogicalModel,
1344 mEventData->mCursorHookPositionX,
1346 CharacterHitTest::TAP,
1351 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1353 // Update selection position after moving the cursor
1354 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1355 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1358 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1360 // Handle text selection
1361 bool selecting = false;
1363 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1365 // Shift-Left/Right to select the text
1366 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1367 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1369 mEventData->mRightSelectionPosition += cursorPositionDelta;
1373 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1375 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1381 // Notify the cursor position to the InputMethodContext.
1382 if( mEventData->mInputMethodContext )
1384 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1385 mEventData->mInputMethodContext.NotifyCursorPosition();
1388 ChangeState( EventData::SELECTING );
1390 mEventData->mUpdateLeftSelectionPosition = true;
1391 mEventData->mUpdateRightSelectionPosition = true;
1392 mEventData->mUpdateGrabHandlePosition = true;
1393 mEventData->mUpdateHighlightBox = true;
1395 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1396 if( mEventData->mGrabHandlePopupEnabled )
1398 mEventData->mDecorator->SetPopupActive( false );
1404 // Handle normal cursor move
1405 ChangeState( EventData::EDITING );
1406 mEventData->mUpdateCursorPosition = true;
1409 mEventData->mUpdateInputStyle = true;
1410 mEventData->mScrollAfterUpdatePosition = true;
1413 void Controller::Impl::OnTapEvent( const Event& event )
1415 if( NULL != mEventData )
1417 const unsigned int tapCount = event.p1.mUint;
1419 if( 1u == tapCount )
1421 if( IsShowingRealText() )
1423 // Convert from control's coords to text's coords.
1424 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1425 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1427 // Keep the tap 'x' position. Used to move the cursor.
1428 mEventData->mCursorHookPositionX = xPosition;
1430 // Whether to touch point hits on a glyph.
1431 bool matchedCharacter = false;
1432 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1433 mModel->mLogicalModel,
1437 CharacterHitTest::TAP,
1440 // When the cursor position is changing, delay cursor blinking
1441 mEventData->mDecorator->DelayCursorBlink();
1445 mEventData->mPrimaryCursorPosition = 0u;
1448 // Update selection position after tapping
1449 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1450 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1452 mEventData->mUpdateCursorPosition = true;
1453 mEventData->mUpdateGrabHandlePosition = true;
1454 mEventData->mScrollAfterUpdatePosition = true;
1455 mEventData->mUpdateInputStyle = true;
1457 // Notify the cursor position to the InputMethodContext.
1458 if( mEventData->mInputMethodContext )
1460 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1461 mEventData->mInputMethodContext.NotifyCursorPosition();
1464 else if( 2u == tapCount )
1466 if( mEventData->mSelectionEnabled )
1468 // Convert from control's coords to text's coords.
1469 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1470 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1472 // Calculates the logical position from the x,y coords.
1473 RepositionSelectionHandles( xPosition,
1475 mEventData->mDoubleTapAction );
1481 void Controller::Impl::OnPanEvent( const Event& event )
1483 if( NULL == mEventData )
1485 // Nothing to do if there is no text input.
1489 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1490 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1492 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1494 // Nothing to do if scrolling is not enabled.
1498 const int state = event.p1.mInt;
1502 case Gesture::Started:
1504 // Will remove the cursor, handles or text's popup, ...
1505 ChangeState( EventData::TEXT_PANNING );
1508 case Gesture::Continuing:
1510 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1511 const Vector2 currentScroll = mModel->mScrollPosition;
1513 if( isHorizontalScrollEnabled )
1515 const float displacementX = event.p2.mFloat;
1516 mModel->mScrollPosition.x += displacementX;
1518 ClampHorizontalScroll( layoutSize );
1521 if( isVerticalScrollEnabled )
1523 const float displacementY = event.p3.mFloat;
1524 mModel->mScrollPosition.y += displacementY;
1526 ClampVerticalScroll( layoutSize );
1529 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1532 case Gesture::Finished:
1533 case Gesture::Cancelled: // FALLTHROUGH
1535 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1536 ChangeState( mEventData->mPreviousState );
1544 void Controller::Impl::OnLongPressEvent( const Event& event )
1546 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1548 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1550 ChangeState( EventData::EDITING_WITH_POPUP );
1551 mEventData->mDecoratorUpdated = true;
1552 mEventData->mUpdateInputStyle = true;
1556 if( mEventData->mSelectionEnabled )
1558 // Convert from control's coords to text's coords.
1559 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1560 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1562 // Calculates the logical position from the x,y coords.
1563 RepositionSelectionHandles( xPosition,
1565 mEventData->mLongPressAction );
1570 void Controller::Impl::OnHandleEvent( const Event& event )
1572 if( NULL == mEventData )
1574 // Nothing to do if there is no text input.
1578 const unsigned int state = event.p1.mUint;
1579 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1580 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1582 if( HANDLE_PRESSED == state )
1584 // Convert from decorator's coords to text's coords.
1585 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1586 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1588 // Need to calculate the handle's new position.
1589 bool matchedCharacter = false;
1590 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1591 mModel->mLogicalModel,
1595 CharacterHitTest::SCROLL,
1598 if( Event::GRAB_HANDLE_EVENT == event.type )
1600 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1602 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1604 // Updates the cursor position if the handle's new position is different than the current one.
1605 mEventData->mUpdateCursorPosition = true;
1606 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1607 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1608 mEventData->mPrimaryCursorPosition = handleNewPosition;
1611 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1612 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1614 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1616 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1618 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1619 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1621 // Updates the highlight box if the handle's new position is different than the current one.
1622 mEventData->mUpdateHighlightBox = true;
1623 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1624 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1625 mEventData->mLeftSelectionPosition = handleNewPosition;
1628 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1629 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1631 // Will define the order to scroll the text to match the handle position.
1632 mEventData->mIsLeftHandleSelected = true;
1633 mEventData->mIsRightHandleSelected = false;
1635 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1637 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1639 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1640 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1642 // Updates the highlight box if the handle's new position is different than the current one.
1643 mEventData->mUpdateHighlightBox = true;
1644 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1645 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1646 mEventData->mRightSelectionPosition = handleNewPosition;
1649 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1650 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1652 // Will define the order to scroll the text to match the handle position.
1653 mEventData->mIsLeftHandleSelected = false;
1654 mEventData->mIsRightHandleSelected = true;
1656 } // end ( HANDLE_PRESSED == state )
1657 else if( ( HANDLE_RELEASED == state ) ||
1658 handleStopScrolling )
1660 CharacterIndex handlePosition = 0u;
1661 if( handleStopScrolling || isSmoothHandlePanEnabled )
1663 // Convert from decorator's coords to text's coords.
1664 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1665 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1667 bool matchedCharacter = false;
1668 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1669 mModel->mLogicalModel,
1673 CharacterHitTest::SCROLL,
1677 if( Event::GRAB_HANDLE_EVENT == event.type )
1679 mEventData->mUpdateCursorPosition = true;
1680 mEventData->mUpdateGrabHandlePosition = true;
1681 mEventData->mUpdateInputStyle = true;
1683 if( !IsClipboardEmpty() )
1685 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1688 if( handleStopScrolling || isSmoothHandlePanEnabled )
1690 mEventData->mScrollAfterUpdatePosition = true;
1691 mEventData->mPrimaryCursorPosition = handlePosition;
1694 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1696 ChangeState( EventData::SELECTING );
1698 mEventData->mUpdateHighlightBox = true;
1699 mEventData->mUpdateLeftSelectionPosition = true;
1700 mEventData->mUpdateRightSelectionPosition = true;
1702 if( handleStopScrolling || isSmoothHandlePanEnabled )
1704 mEventData->mScrollAfterUpdatePosition = true;
1706 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1707 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1709 mEventData->mLeftSelectionPosition = handlePosition;
1713 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1715 ChangeState( EventData::SELECTING );
1717 mEventData->mUpdateHighlightBox = true;
1718 mEventData->mUpdateRightSelectionPosition = true;
1719 mEventData->mUpdateLeftSelectionPosition = true;
1721 if( handleStopScrolling || isSmoothHandlePanEnabled )
1723 mEventData->mScrollAfterUpdatePosition = true;
1724 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1725 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1727 mEventData->mRightSelectionPosition = handlePosition;
1732 mEventData->mDecoratorUpdated = true;
1733 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1734 else if( HANDLE_SCROLLING == state )
1736 const float xSpeed = event.p2.mFloat;
1737 const float ySpeed = event.p3.mFloat;
1738 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1739 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1741 mModel->mScrollPosition.x += xSpeed;
1742 mModel->mScrollPosition.y += ySpeed;
1744 ClampHorizontalScroll( layoutSize );
1745 ClampVerticalScroll( layoutSize );
1747 bool endOfScroll = false;
1748 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1750 // Notify the decorator there is no more text to scroll.
1751 // The decorator won't send more scroll events.
1752 mEventData->mDecorator->NotifyEndOfScroll();
1753 // Still need to set the position of the handle.
1757 // Set the position of the handle.
1758 const bool scrollRightDirection = xSpeed > 0.f;
1759 const bool scrollBottomDirection = ySpeed > 0.f;
1760 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1761 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1763 if( Event::GRAB_HANDLE_EVENT == event.type )
1765 ChangeState( EventData::GRAB_HANDLE_PANNING );
1767 // Get the grab handle position in decorator coords.
1768 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1770 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1772 // Position the grag handle close to either the left or right edge.
1773 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1776 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1778 position.x = mEventData->mCursorHookPositionX;
1780 // Position the grag handle close to either the top or bottom edge.
1781 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1784 // Get the new handle position.
1785 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1786 bool matchedCharacter = false;
1787 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1788 mModel->mLogicalModel,
1790 position.x - mModel->mScrollPosition.x,
1791 position.y - mModel->mScrollPosition.y,
1792 CharacterHitTest::SCROLL,
1795 if( mEventData->mPrimaryCursorPosition != handlePosition )
1797 mEventData->mUpdateCursorPosition = true;
1798 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1799 mEventData->mScrollAfterUpdatePosition = true;
1800 mEventData->mPrimaryCursorPosition = handlePosition;
1802 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1804 // Updates the decorator if the soft handle panning is enabled.
1805 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1807 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1809 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1811 // Get the selection handle position in decorator coords.
1812 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1814 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1816 // Position the selection handle close to either the left or right edge.
1817 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1820 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1822 position.x = mEventData->mCursorHookPositionX;
1824 // Position the grag handle close to either the top or bottom edge.
1825 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1828 // Get the new handle position.
1829 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1830 bool matchedCharacter = false;
1831 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1832 mModel->mLogicalModel,
1834 position.x - mModel->mScrollPosition.x,
1835 position.y - mModel->mScrollPosition.y,
1836 CharacterHitTest::SCROLL,
1839 if( leftSelectionHandleEvent )
1841 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1843 if( differentHandles || endOfScroll )
1845 mEventData->mUpdateHighlightBox = true;
1846 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1847 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1848 mEventData->mLeftSelectionPosition = handlePosition;
1853 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1854 if( differentHandles || endOfScroll )
1856 mEventData->mUpdateHighlightBox = true;
1857 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1858 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1859 mEventData->mRightSelectionPosition = handlePosition;
1863 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1865 RepositionSelectionHandles();
1867 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1870 mEventData->mDecoratorUpdated = true;
1871 } // end ( HANDLE_SCROLLING == state )
1874 void Controller::Impl::OnSelectEvent( const Event& event )
1876 if( NULL == mEventData )
1878 // Nothing to do if there is no text.
1882 if( mEventData->mSelectionEnabled )
1884 // Convert from control's coords to text's coords.
1885 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1886 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1888 // Calculates the logical position from the x,y coords.
1889 RepositionSelectionHandles( xPosition,
1891 Controller::NoTextTap::HIGHLIGHT );
1895 void Controller::Impl::OnSelectAllEvent()
1897 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1899 if( NULL == mEventData )
1901 // Nothing to do if there is no text.
1905 if( mEventData->mSelectionEnabled )
1907 // Calculates the logical position from the start.
1908 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
1909 0.f - mModel->mScrollPosition.y,
1910 Controller::NoTextTap::HIGHLIGHT );
1912 mEventData->mLeftSelectionPosition = 0u;
1913 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1917 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1919 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1921 // Nothing to select if handles are in the same place.
1922 selectedText.clear();
1926 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1928 //Get start and end position of selection
1929 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1930 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1932 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1933 const Length numberOfCharacters = utf32Characters.Count();
1935 // Validate the start and end selection points
1936 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1938 //Get text as a UTF8 string
1939 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1941 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1943 // Keep a copy of the current input style.
1944 InputStyle currentInputStyle;
1945 currentInputStyle.Copy( mEventData->mInputStyle );
1947 // Set as input style the style of the first deleted character.
1948 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1950 // Compare if the input style has changed.
1951 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1953 if( hasInputStyleChanged )
1955 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1956 // Queue the input style changed signal.
1957 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1960 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1962 // Mark the paragraphs to be updated.
1963 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1965 mTextUpdateInfo.mCharacterIndex = 0;
1966 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1967 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1968 mTextUpdateInfo.mClearAll = true;
1972 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1973 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1976 // Delete text between handles
1977 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1978 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1979 utf32Characters.Erase( first, last );
1981 // Will show the cursor at the first character of the selection.
1982 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1986 // Will show the cursor at the last character of the selection.
1987 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1990 mEventData->mDecoratorUpdated = true;
1994 void Controller::Impl::ShowClipboard()
1998 mClipboard.ShowClipboard();
2002 void Controller::Impl::HideClipboard()
2004 if( mClipboard && mClipboardHideEnabled )
2006 mClipboard.HideClipboard();
2010 void Controller::Impl::SetClipboardHideEnable(bool enable)
2012 mClipboardHideEnabled = enable;
2015 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2017 //Send string to clipboard
2018 return ( mClipboard && mClipboard.SetItem( source ) );
2021 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2023 std::string selectedText;
2024 RetrieveSelection( selectedText, deleteAfterSending );
2025 CopyStringToClipboard( selectedText );
2026 ChangeState( EventData::EDITING );
2029 void Controller::Impl::RequestGetTextFromClipboard()
2033 mClipboard.RequestItem();
2037 void Controller::Impl::RepositionSelectionHandles()
2039 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2040 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2042 if( selectionStart == selectionEnd )
2044 // Nothing to select if handles are in the same place.
2045 // So, deactive Highlight box.
2046 mEventData->mDecorator->SetHighlightActive( false );
2050 mEventData->mDecorator->ClearHighlights();
2052 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2053 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2054 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2055 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2056 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2057 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2058 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2060 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2061 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2062 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2064 // Swap the indices if the start is greater than the end.
2065 const bool indicesSwapped = selectionStart > selectionEnd;
2067 // Tell the decorator to flip the selection handles if needed.
2068 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2070 if( indicesSwapped )
2072 std::swap( selectionStart, selectionEnd );
2075 // Get the indices to the first and last selected glyphs.
2076 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2077 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2078 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2079 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2081 // Get the lines where the glyphs are laid-out.
2082 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2084 LineIndex lineIndex = 0u;
2085 Length numberOfLines = 0u;
2086 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2087 1u + glyphEnd - glyphStart,
2090 const LineIndex firstLineIndex = lineIndex;
2092 // Create the structure to store some selection box info.
2093 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2094 selectionBoxLinesInfo.Resize( numberOfLines );
2096 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2097 selectionBoxInfo->minX = MAX_FLOAT;
2098 selectionBoxInfo->maxX = MIN_FLOAT;
2100 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2101 float minHighlightX = std::numeric_limits<float>::max();
2102 float maxHighlightX = std::numeric_limits<float>::min();
2104 Vector2 highLightPosition; // The highlight position in decorator's coords.
2106 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2108 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2109 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2112 // Transform to decorator's (control) coords.
2113 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2115 lineRun += firstLineIndex;
2117 // The line height is the addition of the line ascender and the line descender.
2118 // However, the line descender has a negative value, hence the subtraction.
2119 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2121 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2123 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2124 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2125 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2127 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2128 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2129 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2131 // The number of quads of the selection box.
2132 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2133 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2135 // Count the actual number of quads.
2136 unsigned int actualNumberOfQuads = 0u;
2139 // Traverse the glyphs.
2140 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2142 const GlyphInfo& glyph = *( glyphsBuffer + index );
2143 const Vector2& position = *( positionsBuffer + index );
2145 if( splitStartGlyph )
2147 // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
2149 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2150 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2151 // Get the direction of the character.
2152 CharacterDirection isCurrentRightToLeft = false;
2153 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2155 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2158 // The end point could be in the middle of the ligature.
2159 // Calculate the number of characters selected.
2160 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2162 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2163 quad.y = selectionBoxInfo->lineOffset;
2164 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2165 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2167 // Store the min and max 'x' for each line.
2168 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2169 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2171 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2172 ++actualNumberOfQuads;
2174 splitStartGlyph = false;
2178 if( splitEndGlyph && ( index == glyphEnd ) )
2180 // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
2182 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2183 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2184 // Get the direction of the character.
2185 CharacterDirection isCurrentRightToLeft = false;
2186 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2188 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2191 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2193 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2194 quad.y = selectionBoxInfo->lineOffset;
2195 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2196 quad.w = quad.y + selectionBoxInfo->lineHeight;
2198 // Store the min and max 'x' for each line.
2199 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2200 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2202 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2204 ++actualNumberOfQuads;
2206 splitEndGlyph = false;
2210 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2211 quad.y = selectionBoxInfo->lineOffset;
2212 quad.z = quad.x + glyph.advance;
2213 quad.w = quad.y + selectionBoxInfo->lineHeight;
2215 // Store the min and max 'x' for each line.
2216 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2217 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2219 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2221 ++actualNumberOfQuads;
2223 // Whether to retrieve the next line.
2224 if( index == lastGlyphOfLine )
2227 if( lineIndex < firstLineIndex + numberOfLines )
2229 // Retrieve the next line.
2232 // Get the last glyph of the new line.
2233 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2235 // Keep the offset and height of the current selection box.
2236 const float currentLineOffset = selectionBoxInfo->lineOffset;
2237 const float currentLineHeight = selectionBoxInfo->lineHeight;
2239 // Get the selection box info for the next line.
2242 selectionBoxInfo->minX = MAX_FLOAT;
2243 selectionBoxInfo->maxX = MIN_FLOAT;
2245 // Update the line's vertical offset.
2246 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2248 // The line height is the addition of the line ascender and the line descender.
2249 // However, the line descender has a negative value, hence the subtraction.
2250 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2255 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2256 // The final width is calculated after 'boxifying' the selection.
2257 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2258 endIt = selectionBoxLinesInfo.End();
2262 const SelectionBoxInfo& info = *it;
2264 // Update the size of the highlighted text.
2265 highLightSize.height += info.lineHeight;
2266 minHighlightX = std::min( minHighlightX, info.minX );
2267 maxHighlightX = std::max( maxHighlightX, info.maxX );
2270 // Add extra geometry to 'boxify' the selection.
2272 if( 1u < numberOfLines )
2274 // Boxify the first line.
2275 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2276 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2278 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2279 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2284 quad.y = firstSelectionBoxLineInfo.lineOffset;
2285 quad.z = firstSelectionBoxLineInfo.minX;
2286 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2288 // Boxify at the beginning of the line.
2289 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2291 ++actualNumberOfQuads;
2293 // Update the size of the highlighted text.
2294 minHighlightX = 0.f;
2299 quad.x = firstSelectionBoxLineInfo.maxX;
2300 quad.y = firstSelectionBoxLineInfo.lineOffset;
2301 quad.z = mModel->mVisualModel->mControlSize.width;
2302 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2304 // Boxify at the end of the line.
2305 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2307 ++actualNumberOfQuads;
2309 // Update the size of the highlighted text.
2310 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2313 // Boxify the central lines.
2314 if( 2u < numberOfLines )
2316 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2317 endIt = selectionBoxLinesInfo.End() - 1u;
2321 const SelectionBoxInfo& info = *it;
2324 quad.y = info.lineOffset;
2326 quad.w = info.lineOffset + info.lineHeight;
2328 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2330 ++actualNumberOfQuads;
2333 quad.y = info.lineOffset;
2334 quad.z = mModel->mVisualModel->mControlSize.width;
2335 quad.w = info.lineOffset + info.lineHeight;
2337 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2339 ++actualNumberOfQuads;
2342 // Update the size of the highlighted text.
2343 minHighlightX = 0.f;
2344 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2347 // Boxify the last line.
2348 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2349 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2351 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2352 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2357 quad.y = lastSelectionBoxLineInfo.lineOffset;
2358 quad.z = lastSelectionBoxLineInfo.minX;
2359 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2361 // Boxify at the beginning of the line.
2362 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2364 ++actualNumberOfQuads;
2366 // Update the size of the highlighted text.
2367 minHighlightX = 0.f;
2372 quad.x = lastSelectionBoxLineInfo.maxX;
2373 quad.y = lastSelectionBoxLineInfo.lineOffset;
2374 quad.z = mModel->mVisualModel->mControlSize.width;
2375 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2377 // Boxify at the end of the line.
2378 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2380 ++actualNumberOfQuads;
2382 // Update the size of the highlighted text.
2383 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2387 // Set the actual number of quads.
2388 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2390 // Sets the highlight's size and position. In decorator's coords.
2391 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2392 highLightSize.width = maxHighlightX - minHighlightX;
2394 highLightPosition.x = minHighlightX;
2395 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2396 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2398 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2400 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2402 CursorInfo primaryCursorInfo;
2403 GetCursorPosition( mEventData->mLeftSelectionPosition,
2404 primaryCursorInfo );
2406 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2408 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2410 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2411 primaryCursorInfo.lineHeight );
2413 CursorInfo secondaryCursorInfo;
2414 GetCursorPosition( mEventData->mRightSelectionPosition,
2415 secondaryCursorInfo );
2417 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2419 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2420 secondaryPosition.x,
2421 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2422 secondaryCursorInfo.lineHeight );
2425 // Set the flag to update the decorator.
2426 mEventData->mDecoratorUpdated = true;
2429 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2431 if( NULL == mEventData )
2433 // Nothing to do if there is no text input.
2437 if( IsShowingPlaceholderText() )
2439 // Nothing to do if there is the place-holder text.
2443 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2444 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2445 if( ( 0 == numberOfGlyphs ) ||
2446 ( 0 == numberOfLines ) )
2448 // Nothing to do if there is no text.
2452 // Find which word was selected
2453 CharacterIndex selectionStart( 0 );
2454 CharacterIndex selectionEnd( 0 );
2455 CharacterIndex noTextHitIndex( 0 );
2456 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2457 mModel->mLogicalModel,
2464 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2466 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2468 ChangeState( EventData::SELECTING );
2470 mEventData->mLeftSelectionPosition = selectionStart;
2471 mEventData->mRightSelectionPosition = selectionEnd;
2473 mEventData->mUpdateLeftSelectionPosition = true;
2474 mEventData->mUpdateRightSelectionPosition = true;
2475 mEventData->mUpdateHighlightBox = true;
2477 // It may happen an InputMethodContext commit event arrives before the selection event
2478 // if the InputMethodContext is in pre-edit state. The commit event will set the
2479 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2480 // to false, the highlight box won't be updated.
2481 mEventData->mUpdateCursorPosition = false;
2483 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2485 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2486 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2488 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2490 // Nothing to select. i.e. a white space, out of bounds
2491 ChangeState( EventData::EDITING_WITH_POPUP );
2493 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2495 mEventData->mUpdateCursorPosition = true;
2496 mEventData->mUpdateGrabHandlePosition = true;
2497 mEventData->mScrollAfterUpdatePosition = true;
2498 mEventData->mUpdateInputStyle = true;
2500 else if( Controller::NoTextTap::NO_ACTION == action )
2502 // Nothing to select. i.e. a white space, out of bounds
2503 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2505 mEventData->mUpdateCursorPosition = true;
2506 mEventData->mUpdateGrabHandlePosition = true;
2507 mEventData->mScrollAfterUpdatePosition = true;
2508 mEventData->mUpdateInputStyle = true;
2512 void Controller::Impl::SetPopupButtons()
2515 * Sets the Popup buttons to be shown depending on State.
2517 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2519 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2522 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2524 if( EventData::SELECTING == mEventData->mState )
2526 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2528 if( !IsClipboardEmpty() )
2530 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2531 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2534 if( !mEventData->mAllTextSelected )
2536 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2539 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2541 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2543 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2546 if( !IsClipboardEmpty() )
2548 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2549 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2552 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2554 if ( !IsClipboardEmpty() )
2556 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2557 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2561 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2564 void Controller::Impl::ChangeState( EventData::State newState )
2566 if( NULL == mEventData )
2568 // Nothing to do if there is no text input.
2572 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2574 if( mEventData->mState != newState )
2576 mEventData->mPreviousState = mEventData->mState;
2577 mEventData->mState = newState;
2579 switch( mEventData->mState )
2581 case EventData::INACTIVE:
2583 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2584 mEventData->mDecorator->StopCursorBlink();
2585 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2586 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2587 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2588 mEventData->mDecorator->SetHighlightActive( false );
2589 mEventData->mDecorator->SetPopupActive( false );
2590 mEventData->mDecoratorUpdated = true;
2593 case EventData::INTERRUPTED:
2595 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2596 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2597 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2598 mEventData->mDecorator->SetHighlightActive( false );
2599 mEventData->mDecorator->SetPopupActive( false );
2600 mEventData->mDecoratorUpdated = true;
2603 case EventData::SELECTING:
2605 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2606 mEventData->mDecorator->StopCursorBlink();
2607 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2608 if ( mEventData->mGrabHandleEnabled )
2610 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2611 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2613 mEventData->mDecorator->SetHighlightActive( true );
2614 if( mEventData->mGrabHandlePopupEnabled )
2617 mEventData->mDecorator->SetPopupActive( true );
2619 mEventData->mDecoratorUpdated = true;
2622 case EventData::EDITING:
2624 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2625 if( mEventData->mCursorBlinkEnabled )
2627 mEventData->mDecorator->StartCursorBlink();
2629 // Grab handle is not shown until a tap is received whilst EDITING
2630 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2631 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2632 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2633 mEventData->mDecorator->SetHighlightActive( false );
2634 if( mEventData->mGrabHandlePopupEnabled )
2636 mEventData->mDecorator->SetPopupActive( false );
2638 mEventData->mDecoratorUpdated = true;
2641 case EventData::EDITING_WITH_POPUP:
2643 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2645 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2646 if( mEventData->mCursorBlinkEnabled )
2648 mEventData->mDecorator->StartCursorBlink();
2650 if( mEventData->mSelectionEnabled )
2652 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2653 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2654 mEventData->mDecorator->SetHighlightActive( false );
2656 else if ( mEventData->mGrabHandleEnabled )
2658 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2660 if( mEventData->mGrabHandlePopupEnabled )
2663 mEventData->mDecorator->SetPopupActive( true );
2665 mEventData->mDecoratorUpdated = true;
2668 case EventData::EDITING_WITH_GRAB_HANDLE:
2670 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2672 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2673 if( mEventData->mCursorBlinkEnabled )
2675 mEventData->mDecorator->StartCursorBlink();
2677 // Grab handle is not shown until a tap is received whilst EDITING
2678 if ( mEventData->mGrabHandleEnabled )
2680 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2682 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2683 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2684 mEventData->mDecorator->SetHighlightActive( false );
2685 if( mEventData->mGrabHandlePopupEnabled )
2687 mEventData->mDecorator->SetPopupActive( false );
2689 mEventData->mDecoratorUpdated = true;
2692 case EventData::SELECTION_HANDLE_PANNING:
2694 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2695 mEventData->mDecorator->StopCursorBlink();
2696 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2697 if ( mEventData->mGrabHandleEnabled )
2699 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2700 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2702 mEventData->mDecorator->SetHighlightActive( true );
2703 if( mEventData->mGrabHandlePopupEnabled )
2705 mEventData->mDecorator->SetPopupActive( false );
2707 mEventData->mDecoratorUpdated = true;
2710 case EventData::GRAB_HANDLE_PANNING:
2712 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2714 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2715 if( mEventData->mCursorBlinkEnabled )
2717 mEventData->mDecorator->StartCursorBlink();
2719 if ( mEventData->mGrabHandleEnabled )
2721 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2723 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2724 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2725 mEventData->mDecorator->SetHighlightActive( false );
2726 if( mEventData->mGrabHandlePopupEnabled )
2728 mEventData->mDecorator->SetPopupActive( false );
2730 mEventData->mDecoratorUpdated = true;
2733 case EventData::EDITING_WITH_PASTE_POPUP:
2735 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2737 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2738 if( mEventData->mCursorBlinkEnabled )
2740 mEventData->mDecorator->StartCursorBlink();
2743 if ( mEventData->mGrabHandleEnabled )
2745 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2747 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2748 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2749 mEventData->mDecorator->SetHighlightActive( false );
2751 if( mEventData->mGrabHandlePopupEnabled )
2754 mEventData->mDecorator->SetPopupActive( true );
2756 mEventData->mDecoratorUpdated = true;
2759 case EventData::TEXT_PANNING:
2761 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2762 mEventData->mDecorator->StopCursorBlink();
2763 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2764 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2765 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2767 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2768 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2769 mEventData->mDecorator->SetHighlightActive( true );
2772 if( mEventData->mGrabHandlePopupEnabled )
2774 mEventData->mDecorator->SetPopupActive( false );
2777 mEventData->mDecoratorUpdated = true;
2784 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2785 CursorInfo& cursorInfo )
2787 if( !IsShowingRealText() )
2789 // Do not want to use the place-holder text to set the cursor position.
2791 // Use the line's height of the font's family set to set the cursor's size.
2792 // If there is no font's family set, use the default font.
2793 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2795 cursorInfo.lineOffset = 0.f;
2796 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2797 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2800 if( mModel->mMatchSystemLanguageDirection )
2802 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2805 switch( mModel->mHorizontalAlignment )
2807 case Text::HorizontalAlignment::BEGIN :
2811 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2815 cursorInfo.primaryPosition.x = 0.f;
2819 case Text::HorizontalAlignment::CENTER:
2821 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2824 case Text::HorizontalAlignment::END:
2828 cursorInfo.primaryPosition.x = 0.f;
2832 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2838 // Nothing else to do.
2842 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2843 GetCursorPositionParameters parameters;
2844 parameters.visualModel = mModel->mVisualModel;
2845 parameters.logicalModel = mModel->mLogicalModel;
2846 parameters.metrics = mMetrics;
2847 parameters.logical = logical;
2848 parameters.isMultiline = isMultiLine;
2850 Text::GetCursorPosition( parameters,
2853 // Adds Outline offset.
2854 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2855 cursorInfo.primaryPosition.x += outlineWidth;
2856 cursorInfo.primaryPosition.y += outlineWidth;
2857 cursorInfo.secondaryPosition.x += outlineWidth;
2858 cursorInfo.secondaryPosition.y += outlineWidth;
2862 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2864 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2865 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2867 if( 0.f > cursorInfo.primaryPosition.x )
2869 cursorInfo.primaryPosition.x = 0.f;
2872 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2873 if( cursorInfo.primaryPosition.x > edgeWidth )
2875 cursorInfo.primaryPosition.x = edgeWidth;
2880 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2882 if( NULL == mEventData )
2884 // Nothing to do if there is no text input.
2888 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2890 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2891 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2893 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2894 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2896 if( numberOfCharacters > 1u )
2898 const Script script = mModel->mLogicalModel->GetScript( index );
2899 if( HasLigatureMustBreak( script ) )
2901 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2902 numberOfCharacters = 1u;
2907 while( 0u == numberOfCharacters )
2910 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2914 if( index < mEventData->mPrimaryCursorPosition )
2916 cursorIndex -= numberOfCharacters;
2920 cursorIndex += numberOfCharacters;
2923 // Will update the cursor hook position.
2924 mEventData->mUpdateCursorHookPosition = true;
2929 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2931 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2932 if( NULL == mEventData )
2934 // Nothing to do if there is no text input.
2935 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2939 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2941 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2943 // Sets the cursor position.
2944 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2947 cursorInfo.primaryCursorHeight,
2948 cursorInfo.lineHeight );
2949 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2951 if( mEventData->mUpdateGrabHandlePosition )
2953 // Sets the grab handle position.
2954 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2956 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2957 cursorInfo.lineHeight );
2960 if( cursorInfo.isSecondaryCursor )
2962 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2963 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2964 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2965 cursorInfo.secondaryCursorHeight,
2966 cursorInfo.lineHeight );
2967 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2970 // Set which cursors are active according the state.
2971 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2973 if( cursorInfo.isSecondaryCursor )
2975 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2979 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2984 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2987 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2990 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2991 const CursorInfo& cursorInfo )
2993 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2994 ( RIGHT_SELECTION_HANDLE != handleType ) )
2999 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3001 // Sets the handle's position.
3002 mEventData->mDecorator->SetPosition( handleType,
3004 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3005 cursorInfo.lineHeight );
3007 // If selection handle at start of the text and other at end of the text then all text is selected.
3008 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3009 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3010 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3013 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3015 // Clamp between -space & -alignment offset.
3017 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3019 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3020 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3021 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3023 mEventData->mDecoratorUpdated = true;
3027 mModel->mScrollPosition.x = 0.f;
3031 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3033 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3035 // Nothing to do if the text is single line.
3039 // Clamp between -space & 0.
3040 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3042 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3043 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3044 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3046 mEventData->mDecoratorUpdated = true;
3050 mModel->mScrollPosition.y = 0.f;
3054 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3056 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3058 // position is in actor's coords.
3059 const float positionEndX = position.x + cursorWidth;
3060 const float positionEndY = position.y + lineHeight;
3062 // Transform the position to decorator coords.
3063 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3064 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3066 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3067 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3069 if( decoratorPositionBeginX < 0.f )
3071 mModel->mScrollPosition.x = -position.x;
3073 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3075 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3078 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3080 if( decoratorPositionBeginY < 0.f )
3082 mModel->mScrollPosition.y = -position.y;
3084 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3086 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3091 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3093 // Get the current cursor position in decorator coords.
3094 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3096 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3100 // Calculate the offset to match the cursor position before the character was deleted.
3101 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3103 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3104 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3106 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3107 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3111 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3112 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3114 // Makes the new cursor position visible if needed.
3115 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3118 void Controller::Impl::RequestRelayout()
3120 if( NULL != mControlInterface )
3122 mControlInterface->RequestTextRelayout();
3126 Actor Controller::Impl::CreateBackgroundActor()
3128 // NOTE: Currently we only support background color for one line left-to-right text,
3129 // so the following calculation is based on one line left-to-right text only!
3133 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3134 if( numberOfGlyphs > 0u )
3136 Vector<GlyphInfo> glyphs;
3137 glyphs.Resize( numberOfGlyphs );
3139 Vector<Vector2> positions;
3140 positions.Resize( numberOfGlyphs );
3142 // Get the line where the glyphs are laid-out.
3143 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3144 float alignmentOffset = lineRun->alignmentOffset;
3145 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3151 glyphs.Resize( numberOfGlyphs );
3152 positions.Resize( numberOfGlyphs );
3154 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3155 const Vector2* const positionsBuffer = positions.Begin();
3157 BackgroundMesh mesh;
3158 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3159 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3161 const Vector2 textSize = mView.GetLayoutSize();
3163 const float offsetX = textSize.width * 0.5f;
3164 const float offsetY = textSize.height * 0.5f;
3166 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3167 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3170 uint32_t numberOfQuads = 0u;
3172 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3174 const GlyphInfo& glyph = *( glyphsBuffer + i );
3176 // Get the background color of the character.
3177 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3178 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3179 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? Color::TRANSPARENT : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3181 // Only create quads for glyphs with a background color
3182 if ( backgroundColor != Color::TRANSPARENT )
3184 const Vector2 position = *( positionsBuffer + i );
3186 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3188 quad.x = position.x;
3190 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3191 quad.w = textSize.height;
3193 else if ( i == 0u ) // The first glyph in the whole text
3195 quad.x = position.x;
3197 quad.z = quad.x - glyph.xBearing + glyph.advance;
3198 quad.w = textSize.height;
3200 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3202 quad.x = position.x - glyph.xBearing;
3204 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3205 quad.w = textSize.height;
3207 else // The glyph in the middle of the text
3209 quad.x = position.x - glyph.xBearing;
3211 quad.z = quad.x + glyph.advance;
3212 quad.w = textSize.height;
3215 BackgroundVertex vertex;
3218 vertex.mPosition.x = quad.x - offsetX;
3219 vertex.mPosition.y = quad.y - offsetY;
3220 vertex.mColor = backgroundColor;
3221 mesh.mVertices.PushBack( vertex );
3224 vertex.mPosition.x = quad.z - offsetX;
3225 vertex.mPosition.y = quad.y - offsetY;
3226 vertex.mColor = backgroundColor;
3227 mesh.mVertices.PushBack( vertex );
3230 vertex.mPosition.x = quad.x - offsetX;
3231 vertex.mPosition.y = quad.w - offsetY;
3232 vertex.mColor = backgroundColor;
3233 mesh.mVertices.PushBack( vertex );
3236 vertex.mPosition.x = quad.z - offsetX;
3237 vertex.mPosition.y = quad.w - offsetY;
3238 vertex.mColor = backgroundColor;
3239 mesh.mVertices.PushBack( vertex );
3241 // Six indices in counter clockwise winding
3242 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3243 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3244 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3245 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3246 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3247 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3253 // Only create the background actor if there are glyphs with background color
3254 if ( mesh.mVertices.Count() > 0u )
3256 Property::Map quadVertexFormat;
3257 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3258 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3260 PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
3261 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3263 Geometry quadGeometry = Geometry::New();
3264 quadGeometry.AddVertexBuffer( quadVertices );
3265 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3267 if( !mShaderBackground )
3269 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3272 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3273 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3274 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3276 actor = Actor::New();
3277 actor.SetName( "TextBackgroundColorActor" );
3278 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
3279 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3280 actor.SetSize( textSize );
3281 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
3282 actor.AddRenderer( renderer );
3291 } // namespace Toolkit