2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/rendering/renderer.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/color-segmentation.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/multi-language-support.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-control-interface.h>
36 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
38 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
46 * @brief Struct used to calculate the selection box.
48 struct SelectionBoxInfo
56 #if defined(DEBUG_ENABLED)
57 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
60 const float MAX_FLOAT = std::numeric_limits<float>::max();
61 const float MIN_FLOAT = std::numeric_limits<float>::min();
62 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
64 #define MAKE_SHADER(A)#A
66 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
67 attribute mediump vec2 aPosition;
68 attribute mediump vec4 aColor;
69 varying mediump vec4 vColor;
70 uniform highp mat4 uMvpMatrix;
74 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
75 gl_Position = uMvpMatrix * position;
80 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
81 varying mediump vec4 vColor;
82 uniform lowp vec4 uColor;
86 gl_FragColor = vColor * uColor;
90 struct BackgroundVertex
92 Vector2 mPosition; ///< Vertex posiiton
93 Vector4 mColor; ///< Vertex color
98 Vector< BackgroundVertex > mVertices; ///< container of vertices
99 Vector< unsigned short > mIndices; ///< container of indices
102 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
103 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
104 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
105 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
106 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
119 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
120 : mDecorator( decorator ),
121 mInputMethodContext( inputMethodContext ),
122 mPlaceholderFont( NULL ),
123 mPlaceholderTextActive(),
124 mPlaceholderTextInactive(),
125 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
127 mInputStyleChangedQueue(),
128 mPreviousState( INACTIVE ),
130 mPrimaryCursorPosition( 0u ),
131 mLeftSelectionPosition( 0u ),
132 mRightSelectionPosition( 0u ),
133 mPreEditStartPosition( 0u ),
134 mPreEditLength( 0u ),
135 mCursorHookPositionX( 0.f ),
136 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
137 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
138 mIsShowingPlaceholderText( false ),
139 mPreEditFlag( false ),
140 mDecoratorUpdated( false ),
141 mCursorBlinkEnabled( true ),
142 mGrabHandleEnabled( true ),
143 mGrabHandlePopupEnabled( true ),
144 mSelectionEnabled( true ),
145 mUpdateCursorHookPosition( false ),
146 mUpdateCursorPosition( false ),
147 mUpdateGrabHandlePosition( false ),
148 mUpdateLeftSelectionPosition( false ),
149 mUpdateRightSelectionPosition( false ),
150 mIsLeftHandleSelected( false ),
151 mIsRightHandleSelected( false ),
152 mUpdateHighlightBox( false ),
153 mScrollAfterUpdatePosition( false ),
154 mScrollAfterDelete( false ),
155 mAllTextSelected( false ),
156 mUpdateInputStyle( false ),
157 mPasswordInput( false ),
158 mCheckScrollAmount( false ),
159 mIsPlaceholderPixelSize( false ),
160 mIsPlaceholderElideEnabled( false ),
161 mPlaceholderEllipsisFlag( false ),
162 mShiftSelectionFlag( true ),
163 mUpdateAlignment( false ),
164 mEditingEnabled( true )
168 EventData::~EventData()
171 bool Controller::Impl::ProcessInputEvents()
173 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
174 if( NULL == mEventData )
176 // Nothing to do if there is no text input.
177 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
181 if( mEventData->mDecorator )
183 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
184 iter != mEventData->mEventQueue.end();
189 case Event::CURSOR_KEY_EVENT:
191 OnCursorKeyEvent( *iter );
194 case Event::TAP_EVENT:
199 case Event::LONG_PRESS_EVENT:
201 OnLongPressEvent( *iter );
204 case Event::PAN_EVENT:
209 case Event::GRAB_HANDLE_EVENT:
210 case Event::LEFT_SELECTION_HANDLE_EVENT:
211 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
213 OnHandleEvent( *iter );
218 OnSelectEvent( *iter );
221 case Event::SELECT_ALL:
226 case Event::SELECT_NONE:
235 if( mEventData->mUpdateCursorPosition ||
236 mEventData->mUpdateHighlightBox )
238 NotifyInputMethodContext();
241 // The cursor must also be repositioned after inserts into the model
242 if( mEventData->mUpdateCursorPosition )
244 // Updates the cursor position and scrolls the text to make it visible.
245 CursorInfo cursorInfo;
246 // Calculate the cursor position from the new cursor index.
247 GetCursorPosition( mEventData->mPrimaryCursorPosition,
250 if( NULL != mEditableControlInterface )
252 mEditableControlInterface->CaretMoved( mEventData->mPrimaryCursorPosition );
255 if( mEventData->mUpdateCursorHookPosition )
257 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
258 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
259 mEventData->mUpdateCursorHookPosition = false;
262 // Scroll first the text after delete ...
263 if( mEventData->mScrollAfterDelete )
265 ScrollTextToMatchCursor( cursorInfo );
268 // ... then, text can be scrolled to make the cursor visible.
269 if( mEventData->mScrollAfterUpdatePosition )
271 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
272 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
274 mEventData->mScrollAfterUpdatePosition = false;
275 mEventData->mScrollAfterDelete = false;
277 UpdateCursorPosition( cursorInfo );
279 mEventData->mDecoratorUpdated = true;
280 mEventData->mUpdateCursorPosition = false;
281 mEventData->mUpdateGrabHandlePosition = false;
285 CursorInfo leftHandleInfo;
286 CursorInfo rightHandleInfo;
288 if( mEventData->mUpdateHighlightBox )
290 GetCursorPosition( mEventData->mLeftSelectionPosition,
293 GetCursorPosition( mEventData->mRightSelectionPosition,
296 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
298 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
300 CursorInfo& infoLeft = leftHandleInfo;
302 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
303 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
305 CursorInfo& infoRight = rightHandleInfo;
307 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
308 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
312 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
314 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
315 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
320 if( mEventData->mUpdateLeftSelectionPosition )
322 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
326 mEventData->mDecoratorUpdated = true;
327 mEventData->mUpdateLeftSelectionPosition = false;
330 if( mEventData->mUpdateRightSelectionPosition )
332 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
336 mEventData->mDecoratorUpdated = true;
337 mEventData->mUpdateRightSelectionPosition = false;
340 if( mEventData->mUpdateHighlightBox )
342 RepositionSelectionHandles();
344 mEventData->mUpdateLeftSelectionPosition = false;
345 mEventData->mUpdateRightSelectionPosition = false;
346 mEventData->mUpdateHighlightBox = false;
347 mEventData->mIsLeftHandleSelected = false;
348 mEventData->mIsRightHandleSelected = false;
351 mEventData->mScrollAfterUpdatePosition = false;
354 if( mEventData->mUpdateInputStyle )
356 // Keep a copy of the current input style.
357 InputStyle currentInputStyle;
358 currentInputStyle.Copy( mEventData->mInputStyle );
360 // Set the default style first.
361 RetrieveDefaultInputStyle( mEventData->mInputStyle );
363 // Get the character index from the cursor index.
364 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
366 // Retrieve the style from the style runs stored in the logical model.
367 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
369 // Compare if the input style has changed.
370 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
372 if( hasInputStyleChanged )
374 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
375 // Queue the input style changed signal.
376 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
379 mEventData->mUpdateInputStyle = false;
382 mEventData->mEventQueue.clear();
384 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
386 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
387 mEventData->mDecoratorUpdated = false;
389 return decoratorUpdated;
392 void Controller::Impl::NotifyInputMethodContext()
394 if( mEventData && mEventData->mInputMethodContext )
396 CharacterIndex cursorPosition = GetLogicalCursorPosition();
398 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
400 // Update the cursor position by removing the initial white spaces.
401 if( cursorPosition < numberOfWhiteSpaces )
407 cursorPosition -= numberOfWhiteSpaces;
410 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
411 mEventData->mInputMethodContext.NotifyCursorPosition();
415 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
417 if ( mEventData && mEventData->mInputMethodContext )
419 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
420 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
424 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
426 CharacterIndex cursorPosition = 0u;
430 if( ( EventData::SELECTING == mEventData->mState ) ||
431 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
433 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
437 cursorPosition = mEventData->mPrimaryCursorPosition;
441 return cursorPosition;
444 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
446 Length numberOfWhiteSpaces = 0u;
448 // Get the buffer to the text.
449 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
451 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
452 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
454 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
460 return numberOfWhiteSpaces;
463 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
465 // Get the total number of characters.
466 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
468 // Retrieve the text.
469 if( 0u != numberOfCharacters )
471 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
475 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
477 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
478 mTextUpdateInfo.mStartGlyphIndex = 0u;
479 mTextUpdateInfo.mStartLineIndex = 0u;
480 numberOfCharacters = 0u;
482 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
483 if( 0u == numberOfParagraphs )
485 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
486 numberOfCharacters = 0u;
488 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
490 // Nothing else to do if there are no paragraphs.
494 // Find the paragraphs to be updated.
495 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
496 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
498 // Text is being added at the end of the current text.
499 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
501 // Text is being added in a new paragraph after the last character of the text.
502 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
503 numberOfCharacters = 0u;
504 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
506 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
507 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
509 // Nothing else to do;
513 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
517 Length numberOfCharactersToUpdate = 0u;
518 if( mTextUpdateInfo.mFullRelayoutNeeded )
520 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
524 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
526 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
527 numberOfCharactersToUpdate,
528 paragraphsToBeUpdated );
531 if( 0u != paragraphsToBeUpdated.Count() )
533 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
534 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
535 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
537 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
538 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
540 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
541 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
542 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
543 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
545 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
546 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
548 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
552 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
556 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
557 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
560 void Controller::Impl::ClearFullModelData( OperationsMask operations )
562 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
564 mModel->mLogicalModel->mLineBreakInfo.Clear();
565 mModel->mLogicalModel->mParagraphInfo.Clear();
568 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
570 mModel->mLogicalModel->mScriptRuns.Clear();
573 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
575 mModel->mLogicalModel->mFontRuns.Clear();
578 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
580 if( NO_OPERATION != ( BIDI_INFO & operations ) )
582 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
583 mModel->mLogicalModel->mCharacterDirections.Clear();
586 if( NO_OPERATION != ( REORDER & operations ) )
588 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
589 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
590 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
594 BidirectionalLineInfoRun& bidiLineInfo = *it;
596 free( bidiLineInfo.visualToLogicalMap );
597 bidiLineInfo.visualToLogicalMap = NULL;
599 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
603 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
605 mModel->mVisualModel->mGlyphs.Clear();
606 mModel->mVisualModel->mGlyphsToCharacters.Clear();
607 mModel->mVisualModel->mCharactersToGlyph.Clear();
608 mModel->mVisualModel->mCharactersPerGlyph.Clear();
609 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
610 mModel->mVisualModel->mGlyphPositions.Clear();
613 if( NO_OPERATION != ( LAYOUT & operations ) )
615 mModel->mVisualModel->mLines.Clear();
618 if( NO_OPERATION != ( COLOR & operations ) )
620 mModel->mVisualModel->mColorIndices.Clear();
621 mModel->mVisualModel->mBackgroundColorIndices.Clear();
625 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
627 const CharacterIndex endIndexPlusOne = endIndex + 1u;
629 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
631 // Clear the line break info.
632 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
634 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
635 lineBreakInfoBuffer + endIndexPlusOne );
637 // Clear the paragraphs.
638 ClearCharacterRuns( startIndex,
640 mModel->mLogicalModel->mParagraphInfo );
643 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
645 // Clear the scripts.
646 ClearCharacterRuns( startIndex,
648 mModel->mLogicalModel->mScriptRuns );
651 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
654 ClearCharacterRuns( startIndex,
656 mModel->mLogicalModel->mFontRuns );
659 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
661 if( NO_OPERATION != ( BIDI_INFO & operations ) )
663 // Clear the bidirectional paragraph info.
664 ClearCharacterRuns( startIndex,
666 mModel->mLogicalModel->mBidirectionalParagraphInfo );
668 // Clear the character's directions.
669 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
671 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
672 characterDirectionsBuffer + endIndexPlusOne );
675 if( NO_OPERATION != ( REORDER & operations ) )
677 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
678 uint32_t endRemoveIndex = startRemoveIndex;
679 ClearCharacterRuns( startIndex,
681 mModel->mLogicalModel->mBidirectionalLineInfo,
685 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
687 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
688 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
689 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
693 BidirectionalLineInfoRun& bidiLineInfo = *it;
695 free( bidiLineInfo.visualToLogicalMap );
696 bidiLineInfo.visualToLogicalMap = NULL;
699 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
700 bidirectionalLineInfoBuffer + endRemoveIndex );
705 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
707 const CharacterIndex endIndexPlusOne = endIndex + 1u;
708 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
710 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
711 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
712 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
714 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
715 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
717 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
719 // Update the character to glyph indices.
720 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
721 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
725 CharacterIndex& index = *it;
726 index -= numberOfGlyphsRemoved;
729 // Clear the character to glyph conversion table.
730 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
731 charactersToGlyphBuffer + endIndexPlusOne );
733 // Clear the glyphs per character table.
734 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
735 glyphsPerCharacterBuffer + endIndexPlusOne );
737 // Clear the glyphs buffer.
738 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
739 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
740 glyphsBuffer + endGlyphIndexPlusOne );
742 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
744 // Update the glyph to character indices.
745 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
746 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
750 CharacterIndex& index = *it;
751 index -= numberOfCharactersRemoved;
754 // Clear the glyphs to characters buffer.
755 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
756 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
758 // Clear the characters per glyph buffer.
759 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
760 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
761 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
763 // Clear the positions buffer.
764 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
765 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
766 positionsBuffer + endGlyphIndexPlusOne );
769 if( NO_OPERATION != ( LAYOUT & operations ) )
772 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
773 uint32_t endRemoveIndex = startRemoveIndex;
774 ClearCharacterRuns( startIndex,
776 mModel->mVisualModel->mLines,
780 // Will update the glyph runs.
781 startRemoveIndex = mModel->mVisualModel->mLines.Count();
782 endRemoveIndex = startRemoveIndex;
783 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
784 endGlyphIndexPlusOne - 1u,
785 mModel->mVisualModel->mLines,
789 // Set the line index from where to insert the new laid-out lines.
790 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
792 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
793 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
794 linesBuffer + endRemoveIndex );
797 if( NO_OPERATION != ( COLOR & operations ) )
799 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
801 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
802 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
803 colorIndexBuffer + endGlyphIndexPlusOne );
806 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
808 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
809 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
810 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
815 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
817 if( mTextUpdateInfo.mClearAll ||
818 ( ( 0u == startIndex ) &&
819 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
821 ClearFullModelData( operations );
825 // Clear the model data related with characters.
826 ClearCharacterModelData( startIndex, endIndex, operations );
828 // Clear the model data related with glyphs.
829 ClearGlyphModelData( startIndex, endIndex, operations );
832 // The estimated number of lines. Used to avoid reallocations when layouting.
833 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
835 mModel->mVisualModel->ClearCaches();
838 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
840 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
842 // Calculate the operations to be done.
843 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
845 if( NO_OPERATION == operations )
847 // Nothing to do if no operations are pending and required.
851 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
852 Vector<Character> displayCharacters;
853 bool useHiddenText = false;
854 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
856 mHiddenInput->Substitute( srcCharacters,displayCharacters );
857 useHiddenText = true;
860 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
861 const Length numberOfCharacters = utf32Characters.Count();
863 // Index to the first character of the first paragraph to be updated.
864 CharacterIndex startIndex = 0u;
865 // Number of characters of the paragraphs to be removed.
866 Length paragraphCharacters = 0u;
868 CalculateTextUpdateIndices( paragraphCharacters );
870 // Check whether the indices for updating the text is valid
871 if ( numberOfCharacters > 0u &&
872 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
873 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
875 std::string currentText;
876 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
878 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
879 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
881 // Dump mTextUpdateInfo
882 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
883 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
884 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
885 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
886 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
887 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
888 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
889 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
890 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
891 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
892 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
893 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
894 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
899 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
901 if( mTextUpdateInfo.mClearAll ||
902 ( 0u != paragraphCharacters ) )
904 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
907 mTextUpdateInfo.mClearAll = false;
909 // Whether the model is updated.
910 bool updated = false;
912 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
913 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
915 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
917 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
918 // calculate the bidirectional info for each 'paragraph'.
919 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
920 // is not shaped together).
921 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
923 SetLineBreakInfo( utf32Characters,
925 requestedNumberOfCharacters,
928 // Create the paragraph info.
929 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
930 requestedNumberOfCharacters );
934 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
935 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
937 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
938 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
940 if( getScripts || validateFonts )
942 // Validates the fonts assigned by the application or assigns default ones.
943 // It makes sure all the characters are going to be rendered by the correct font.
944 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
948 // Retrieves the scripts used in the text.
949 multilanguageSupport.SetScripts( utf32Characters,
951 requestedNumberOfCharacters,
957 // Validate the fonts set through the mark-up string.
958 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
960 // Get the default font's description.
961 TextAbstraction::FontDescription defaultFontDescription;
962 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
964 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
966 // If the placeholder font is set specifically, only placeholder font is changed.
967 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
968 if( mEventData->mPlaceholderFont->sizeDefined )
970 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u;
973 else if( NULL != mFontDefaults )
975 // Set the normal font and the placeholder font.
976 defaultFontDescription = mFontDefaults->mFontDescription;
978 if( mTextFitEnabled )
980 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
984 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * 64u;
988 // Validates the fonts. If there is a character with no assigned font it sets a default one.
989 // After this call, fonts are validated.
990 multilanguageSupport.ValidateFonts( utf32Characters,
993 defaultFontDescription,
996 requestedNumberOfCharacters,
1002 Vector<Character> mirroredUtf32Characters;
1003 bool textMirrored = false;
1004 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
1005 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1007 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1008 bidirectionalInfo.Reserve( numberOfParagraphs );
1010 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1011 SetBidirectionalInfo( utf32Characters,
1015 requestedNumberOfCharacters,
1017 mModel->mMatchSystemLanguageDirection,
1020 if( 0u != bidirectionalInfo.Count() )
1022 // Only set the character directions if there is right to left characters.
1023 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1024 GetCharactersDirection( bidirectionalInfo,
1027 requestedNumberOfCharacters,
1030 // This paragraph has right to left text. Some characters may need to be mirrored.
1031 // TODO: consider if the mirrored string can be stored as well.
1033 textMirrored = GetMirroredText( utf32Characters,
1037 requestedNumberOfCharacters,
1038 mirroredUtf32Characters );
1042 // There is no right to left characters. Clear the directions vector.
1043 mModel->mLogicalModel->mCharacterDirections.Clear();
1048 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1049 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1050 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1051 Vector<GlyphIndex> newParagraphGlyphs;
1052 newParagraphGlyphs.Reserve( numberOfParagraphs );
1054 const Length currentNumberOfGlyphs = glyphs.Count();
1055 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1057 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1059 ShapeText( textToShape,
1064 mTextUpdateInfo.mStartGlyphIndex,
1065 requestedNumberOfCharacters,
1067 glyphsToCharactersMap,
1069 newParagraphGlyphs );
1071 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1072 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1073 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1077 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1079 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1081 GlyphInfo* glyphsBuffer = glyphs.Begin();
1082 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1084 // Update the width and advance of all new paragraph characters.
1085 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1087 const GlyphIndex index = *it;
1088 GlyphInfo& glyph = *( glyphsBuffer + index );
1090 glyph.xBearing = 0.f;
1092 glyph.advance = 0.f;
1097 if( ( NULL != mEventData ) &&
1098 mEventData->mPreEditFlag &&
1099 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1101 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1102 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1103 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1105 // Check the type of preedit and run it.
1106 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1108 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1109 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1110 type = attrData.preeditType;
1112 // Check the number of commit characters for the start position.
1113 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1114 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1118 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1120 // Add the underline for the pre-edit text.
1121 GlyphRun underlineRun;
1122 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1123 underlineRun.numberOfGlyphs = numberOfIndices;
1124 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1127 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1129 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1130 ColorRun backgroundColorRun;
1131 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1132 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1133 backgroundColorRun.color = textColor;
1134 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1136 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1137 Vector<ColorRun> colorRuns;
1138 colorRuns.Resize( 1u );
1139 ColorRun& colorRun = *( colorRuns.Begin() );
1140 colorRun.color = backgroundColor;
1141 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1142 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1144 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1147 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1149 ColorRun backgroundColorRun;
1150 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1151 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1152 backgroundColorRun.color = LIGHT_BLUE;
1153 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1156 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1158 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1159 ColorRun backgroundColorRun;
1160 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1161 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1162 backgroundColorRun.color = BACKGROUND_SUB4;
1163 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1165 GlyphRun underlineRun;
1166 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1167 underlineRun.numberOfGlyphs = numberOfIndices;
1168 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1171 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1173 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1174 ColorRun backgroundColorRun;
1175 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1176 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1177 backgroundColorRun.color = BACKGROUND_SUB5;
1178 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1180 GlyphRun underlineRun;
1181 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1182 underlineRun.numberOfGlyphs = numberOfIndices;
1183 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1186 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1188 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1189 ColorRun backgroundColorRun;
1190 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1191 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1192 backgroundColorRun.color = BACKGROUND_SUB6;
1193 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1195 GlyphRun underlineRun;
1196 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1197 underlineRun.numberOfGlyphs = numberOfIndices;
1198 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1201 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1203 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1204 ColorRun backgroundColorRun;
1205 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1206 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1207 backgroundColorRun.color = BACKGROUND_SUB7;
1208 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1210 GlyphRun underlineRun;
1211 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1212 underlineRun.numberOfGlyphs = numberOfIndices;
1213 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1216 case Dali::InputMethodContext::PreeditStyle::NONE:
1227 if( NO_OPERATION != ( COLOR & operations ) )
1229 // Set the color runs in glyphs.
1230 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1231 mModel->mVisualModel->mCharactersToGlyph,
1232 mModel->mVisualModel->mGlyphsPerCharacter,
1234 mTextUpdateInfo.mStartGlyphIndex,
1235 requestedNumberOfCharacters,
1236 mModel->mVisualModel->mColors,
1237 mModel->mVisualModel->mColorIndices );
1239 // Set the background color runs in glyphs.
1240 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1241 mModel->mVisualModel->mCharactersToGlyph,
1242 mModel->mVisualModel->mGlyphsPerCharacter,
1244 mTextUpdateInfo.mStartGlyphIndex,
1245 requestedNumberOfCharacters,
1246 mModel->mVisualModel->mBackgroundColors,
1247 mModel->mVisualModel->mBackgroundColorIndices );
1253 // The estimated number of lines. Used to avoid reallocations when layouting.
1254 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1256 // Set the previous number of characters for the next time the text is updated.
1257 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1262 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1264 // Sets the default text's color.
1265 inputStyle.textColor = mTextColor;
1266 inputStyle.isDefaultColor = true;
1268 inputStyle.familyName.clear();
1269 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1270 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1271 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1272 inputStyle.size = 0.f;
1274 inputStyle.lineSpacing = 0.f;
1276 inputStyle.underlineProperties.clear();
1277 inputStyle.shadowProperties.clear();
1278 inputStyle.embossProperties.clear();
1279 inputStyle.outlineProperties.clear();
1281 inputStyle.isFamilyDefined = false;
1282 inputStyle.isWeightDefined = false;
1283 inputStyle.isWidthDefined = false;
1284 inputStyle.isSlantDefined = false;
1285 inputStyle.isSizeDefined = false;
1287 inputStyle.isLineSpacingDefined = false;
1289 inputStyle.isUnderlineDefined = false;
1290 inputStyle.isShadowDefined = false;
1291 inputStyle.isEmbossDefined = false;
1292 inputStyle.isOutlineDefined = false;
1294 // Sets the default font's family name, weight, width, slant and size.
1297 if( mFontDefaults->familyDefined )
1299 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1300 inputStyle.isFamilyDefined = true;
1303 if( mFontDefaults->weightDefined )
1305 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1306 inputStyle.isWeightDefined = true;
1309 if( mFontDefaults->widthDefined )
1311 inputStyle.width = mFontDefaults->mFontDescription.width;
1312 inputStyle.isWidthDefined = true;
1315 if( mFontDefaults->slantDefined )
1317 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1318 inputStyle.isSlantDefined = true;
1321 if( mFontDefaults->sizeDefined )
1323 inputStyle.size = mFontDefaults->mDefaultPointSize;
1324 inputStyle.isSizeDefined = true;
1329 float Controller::Impl::GetDefaultFontLineHeight()
1331 FontId defaultFontId = 0u;
1332 if( NULL == mFontDefaults )
1334 TextAbstraction::FontDescription fontDescription;
1335 defaultFontId = mFontClient.GetFontId( fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale );
1339 defaultFontId = mFontDefaults->GetFontId( mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale );
1342 Text::FontMetrics fontMetrics;
1343 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1345 return( fontMetrics.ascender - fontMetrics.descender );
1348 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1350 ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
1353 void Controller::Impl::OnTapEvent( const Event& event )
1355 ControllerImplEventHandler::OnTapEvent(*this, event);
1358 void Controller::Impl::OnPanEvent( const Event& event )
1360 ControllerImplEventHandler::OnPanEvent(*this, event);
1363 void Controller::Impl::OnLongPressEvent( const Event& event )
1365 ControllerImplEventHandler::OnLongPressEvent(*this, event);
1368 void Controller::Impl::OnHandleEvent( const Event& event )
1370 ControllerImplEventHandler::OnHandleEvent(*this, event);
1373 void Controller::Impl::OnSelectEvent( const Event& event )
1375 ControllerImplEventHandler::OnSelectEvent(*this, event);
1378 void Controller::Impl::OnSelectAllEvent()
1380 ControllerImplEventHandler::OnSelectAllEvent(*this);
1383 void Controller::Impl::OnSelectNoneEvent()
1385 ControllerImplEventHandler::OnSelectNoneEvent(*this);
1388 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
1390 if( nullptr == mEventData )
1392 // Nothing to do if there is no text.
1396 if( mEventData->mSelectionEnabled && (pStart || pEnd))
1398 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1402 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1406 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1409 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1411 ChangeState( EventData::EDITING );
1412 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1413 mEventData->mUpdateCursorPosition = true;
1417 ChangeState( EventData::SELECTING );
1418 mEventData->mUpdateHighlightBox = true;
1419 mEventData->mUpdateLeftSelectionPosition = true;
1420 mEventData->mUpdateRightSelectionPosition = true;
1425 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1431 range.first = mEventData->mLeftSelectionPosition;
1432 range.second = mEventData->mRightSelectionPosition;
1438 bool Controller::Impl::IsEditable() const
1440 return mEventData && mEventData->mEditingEnabled;
1443 void Controller::Impl::SetEditable( bool editable )
1447 mEventData->mEditingEnabled = editable;
1451 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1453 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1455 // Nothing to select if handles are in the same place.
1456 selectedText.clear();
1460 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1462 //Get start and end position of selection
1463 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1464 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1466 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1467 const Length numberOfCharacters = utf32Characters.Count();
1469 // Validate the start and end selection points
1470 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1472 //Get text as a UTF8 string
1473 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1475 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1477 // Keep a copy of the current input style.
1478 InputStyle currentInputStyle;
1479 currentInputStyle.Copy( mEventData->mInputStyle );
1481 // Set as input style the style of the first deleted character.
1482 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1484 // Compare if the input style has changed.
1485 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1487 if( hasInputStyleChanged )
1489 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1490 // Queue the input style changed signal.
1491 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1494 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1496 // Mark the paragraphs to be updated.
1497 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1499 mTextUpdateInfo.mCharacterIndex = 0;
1500 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1501 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1502 mTextUpdateInfo.mClearAll = true;
1506 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1507 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1510 // Delete text between handles
1511 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1512 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1513 utf32Characters.Erase( first, last );
1515 // Will show the cursor at the first character of the selection.
1516 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1520 // Will show the cursor at the last character of the selection.
1521 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1524 mEventData->mDecoratorUpdated = true;
1528 void Controller::Impl::SetSelection( int start, int end )
1530 mEventData->mLeftSelectionPosition = start;
1531 mEventData->mRightSelectionPosition = end;
1532 mEventData->mUpdateCursorPosition = true;
1535 std::pair< int, int > Controller::Impl::GetSelectionIndexes() const
1537 return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition };
1540 void Controller::Impl::ShowClipboard()
1544 mClipboard.ShowClipboard();
1548 void Controller::Impl::HideClipboard()
1550 if( mClipboard && mClipboardHideEnabled )
1552 mClipboard.HideClipboard();
1556 void Controller::Impl::SetClipboardHideEnable(bool enable)
1558 mClipboardHideEnabled = enable;
1561 bool Controller::Impl::CopyStringToClipboard( const std::string& source )
1563 //Send string to clipboard
1564 return ( mClipboard && mClipboard.SetItem( source ) );
1567 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1569 std::string selectedText;
1570 RetrieveSelection( selectedText, deleteAfterSending );
1571 CopyStringToClipboard( selectedText );
1572 ChangeState( EventData::EDITING );
1575 void Controller::Impl::RequestGetTextFromClipboard()
1579 mClipboard.RequestItem();
1583 void Controller::Impl::RepositionSelectionHandles()
1585 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1586 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1588 if( selectionStart == selectionEnd )
1590 // Nothing to select if handles are in the same place.
1591 // So, deactive Highlight box.
1592 mEventData->mDecorator->SetHighlightActive( false );
1596 mEventData->mDecorator->ClearHighlights();
1598 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1599 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1600 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1601 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1602 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1603 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1604 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1606 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1607 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1608 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1610 // Swap the indices if the start is greater than the end.
1611 const bool indicesSwapped = selectionStart > selectionEnd;
1613 // Tell the decorator to flip the selection handles if needed.
1614 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1616 if( indicesSwapped )
1618 std::swap( selectionStart, selectionEnd );
1621 // Get the indices to the first and last selected glyphs.
1622 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1623 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1624 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1625 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1627 // Get the lines where the glyphs are laid-out.
1628 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1630 LineIndex lineIndex = 0u;
1631 Length numberOfLines = 0u;
1632 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1633 1u + glyphEnd - glyphStart,
1636 const LineIndex firstLineIndex = lineIndex;
1638 // Create the structure to store some selection box info.
1639 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1640 selectionBoxLinesInfo.Resize( numberOfLines );
1642 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1643 selectionBoxInfo->minX = MAX_FLOAT;
1644 selectionBoxInfo->maxX = MIN_FLOAT;
1646 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1647 float minHighlightX = std::numeric_limits<float>::max();
1648 float maxHighlightX = std::numeric_limits<float>::min();
1650 Vector2 highLightPosition; // The highlight position in decorator's coords.
1652 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1654 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1655 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1658 // Transform to decorator's (control) coords.
1659 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1661 lineRun += firstLineIndex;
1663 // The line height is the addition of the line ascender and the line descender.
1664 // However, the line descender has a negative value, hence the subtraction.
1665 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1667 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1669 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1670 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1671 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1673 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1674 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1675 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1677 // The number of quads of the selection box.
1678 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1679 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1681 // Count the actual number of quads.
1682 unsigned int actualNumberOfQuads = 0u;
1685 // Traverse the glyphs.
1686 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1688 const GlyphInfo& glyph = *( glyphsBuffer + index );
1689 const Vector2& position = *( positionsBuffer + index );
1691 if( splitStartGlyph )
1693 // 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.
1695 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1696 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1697 // Get the direction of the character.
1698 CharacterDirection isCurrentRightToLeft = false;
1699 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1701 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1704 // The end point could be in the middle of the ligature.
1705 // Calculate the number of characters selected.
1706 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1708 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1709 quad.y = selectionBoxInfo->lineOffset;
1710 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1711 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1713 // Store the min and max 'x' for each line.
1714 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1715 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1717 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1718 ++actualNumberOfQuads;
1720 splitStartGlyph = false;
1724 if( splitEndGlyph && ( index == glyphEnd ) )
1726 // 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.
1728 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1729 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1730 // Get the direction of the character.
1731 CharacterDirection isCurrentRightToLeft = false;
1732 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1734 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1737 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1739 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1740 quad.y = selectionBoxInfo->lineOffset;
1741 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1742 quad.w = quad.y + selectionBoxInfo->lineHeight;
1744 // Store the min and max 'x' for each line.
1745 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1746 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1748 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1750 ++actualNumberOfQuads;
1752 splitEndGlyph = false;
1756 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1757 quad.y = selectionBoxInfo->lineOffset;
1758 quad.z = quad.x + glyph.advance;
1759 quad.w = quad.y + selectionBoxInfo->lineHeight;
1761 // Store the min and max 'x' for each line.
1762 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1763 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1765 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1767 ++actualNumberOfQuads;
1769 // Whether to retrieve the next line.
1770 if( index == lastGlyphOfLine )
1773 if( lineIndex < firstLineIndex + numberOfLines )
1775 // Retrieve the next line.
1778 // Get the last glyph of the new line.
1779 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1781 // Keep the offset and height of the current selection box.
1782 const float currentLineOffset = selectionBoxInfo->lineOffset;
1783 const float currentLineHeight = selectionBoxInfo->lineHeight;
1785 // Get the selection box info for the next line.
1788 selectionBoxInfo->minX = MAX_FLOAT;
1789 selectionBoxInfo->maxX = MIN_FLOAT;
1791 // Update the line's vertical offset.
1792 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1794 // The line height is the addition of the line ascender and the line descender.
1795 // However, the line descender has a negative value, hence the subtraction.
1796 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1801 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1802 // The final width is calculated after 'boxifying' the selection.
1803 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1804 endIt = selectionBoxLinesInfo.End();
1808 const SelectionBoxInfo& info = *it;
1810 // Update the size of the highlighted text.
1811 highLightSize.height += info.lineHeight;
1812 minHighlightX = std::min( minHighlightX, info.minX );
1813 maxHighlightX = std::max( maxHighlightX, info.maxX );
1816 // Add extra geometry to 'boxify' the selection.
1818 if( 1u < numberOfLines )
1820 // Boxify the first line.
1821 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
1822 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1824 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1825 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1830 quad.y = firstSelectionBoxLineInfo.lineOffset;
1831 quad.z = firstSelectionBoxLineInfo.minX;
1832 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1834 // Boxify at the beginning of the line.
1835 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1837 ++actualNumberOfQuads;
1839 // Update the size of the highlighted text.
1840 minHighlightX = 0.f;
1845 quad.x = firstSelectionBoxLineInfo.maxX;
1846 quad.y = firstSelectionBoxLineInfo.lineOffset;
1847 quad.z = mModel->mVisualModel->mControlSize.width;
1848 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1850 // Boxify at the end of the line.
1851 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1853 ++actualNumberOfQuads;
1855 // Update the size of the highlighted text.
1856 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1859 // Boxify the central lines.
1860 if( 2u < numberOfLines )
1862 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1863 endIt = selectionBoxLinesInfo.End() - 1u;
1867 const SelectionBoxInfo& info = *it;
1870 quad.y = info.lineOffset;
1872 quad.w = info.lineOffset + info.lineHeight;
1874 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1876 ++actualNumberOfQuads;
1879 quad.y = info.lineOffset;
1880 quad.z = mModel->mVisualModel->mControlSize.width;
1881 quad.w = info.lineOffset + info.lineHeight;
1883 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1885 ++actualNumberOfQuads;
1888 // Update the size of the highlighted text.
1889 minHighlightX = 0.f;
1890 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1893 // Boxify the last line.
1894 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1895 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1897 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1898 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1903 quad.y = lastSelectionBoxLineInfo.lineOffset;
1904 quad.z = lastSelectionBoxLineInfo.minX;
1905 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1907 // Boxify at the beginning of the line.
1908 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1910 ++actualNumberOfQuads;
1912 // Update the size of the highlighted text.
1913 minHighlightX = 0.f;
1918 quad.x = lastSelectionBoxLineInfo.maxX;
1919 quad.y = lastSelectionBoxLineInfo.lineOffset;
1920 quad.z = mModel->mVisualModel->mControlSize.width;
1921 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1923 // Boxify at the end of the line.
1924 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1926 ++actualNumberOfQuads;
1928 // Update the size of the highlighted text.
1929 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1933 // Set the actual number of quads.
1934 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
1936 // Sets the highlight's size and position. In decorator's coords.
1937 // The highlight's height has been calculated above (before 'boxifying' the highlight).
1938 highLightSize.width = maxHighlightX - minHighlightX;
1940 highLightPosition.x = minHighlightX;
1941 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1942 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
1944 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
1946 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1948 CursorInfo primaryCursorInfo;
1949 GetCursorPosition( mEventData->mLeftSelectionPosition,
1950 primaryCursorInfo );
1952 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1954 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1956 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1957 primaryCursorInfo.lineHeight );
1959 CursorInfo secondaryCursorInfo;
1960 GetCursorPosition( mEventData->mRightSelectionPosition,
1961 secondaryCursorInfo );
1963 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1965 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1966 secondaryPosition.x,
1967 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1968 secondaryCursorInfo.lineHeight );
1971 // Set the flag to update the decorator.
1972 mEventData->mDecoratorUpdated = true;
1975 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
1977 if( NULL == mEventData )
1979 // Nothing to do if there is no text input.
1983 if( IsShowingPlaceholderText() )
1985 // Nothing to do if there is the place-holder text.
1989 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
1990 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
1991 if( ( 0 == numberOfGlyphs ) ||
1992 ( 0 == numberOfLines ) )
1994 // Nothing to do if there is no text.
1998 // Find which word was selected
1999 CharacterIndex selectionStart( 0 );
2000 CharacterIndex selectionEnd( 0 );
2001 CharacterIndex noTextHitIndex( 0 );
2002 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2003 mModel->mLogicalModel,
2010 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2012 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2014 ChangeState( EventData::SELECTING );
2016 mEventData->mLeftSelectionPosition = selectionStart;
2017 mEventData->mRightSelectionPosition = selectionEnd;
2019 mEventData->mUpdateLeftSelectionPosition = true;
2020 mEventData->mUpdateRightSelectionPosition = true;
2021 mEventData->mUpdateHighlightBox = true;
2023 // It may happen an InputMethodContext commit event arrives before the selection event
2024 // if the InputMethodContext is in pre-edit state. The commit event will set the
2025 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2026 // to false, the highlight box won't be updated.
2027 mEventData->mUpdateCursorPosition = false;
2029 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2031 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2032 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2034 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2036 // Nothing to select. i.e. a white space, out of bounds
2037 ChangeState( EventData::EDITING_WITH_POPUP );
2039 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2041 mEventData->mUpdateCursorPosition = true;
2042 mEventData->mUpdateGrabHandlePosition = true;
2043 mEventData->mScrollAfterUpdatePosition = true;
2044 mEventData->mUpdateInputStyle = true;
2046 else if( Controller::NoTextTap::NO_ACTION == action )
2048 // Nothing to select. i.e. a white space, out of bounds
2049 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2051 mEventData->mUpdateCursorPosition = true;
2052 mEventData->mUpdateGrabHandlePosition = true;
2053 mEventData->mScrollAfterUpdatePosition = true;
2054 mEventData->mUpdateInputStyle = true;
2058 void Controller::Impl::SetPopupButtons()
2061 * Sets the Popup buttons to be shown depending on State.
2063 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2065 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2068 bool isEditable = IsEditable();
2069 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2071 if( EventData::SELECTING == mEventData->mState )
2073 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2076 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2079 if( !IsClipboardEmpty() )
2083 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2085 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2088 if( !mEventData->mAllTextSelected )
2090 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2093 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2095 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2097 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2100 if( !IsClipboardEmpty() )
2104 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2106 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2109 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2111 if ( !IsClipboardEmpty() )
2115 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2117 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2121 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2124 void Controller::Impl::ChangeState( EventData::State newState )
2126 if( NULL == mEventData )
2128 // Nothing to do if there is no text input.
2132 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2134 if( mEventData->mState != newState )
2136 mEventData->mPreviousState = mEventData->mState;
2137 mEventData->mState = newState;
2139 switch( mEventData->mState )
2141 case EventData::INACTIVE:
2143 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2144 mEventData->mDecorator->StopCursorBlink();
2145 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2146 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2147 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2148 mEventData->mDecorator->SetHighlightActive( false );
2149 mEventData->mDecorator->SetPopupActive( false );
2150 mEventData->mDecoratorUpdated = true;
2153 case EventData::INTERRUPTED:
2155 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2156 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2157 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2158 mEventData->mDecorator->SetHighlightActive( false );
2159 mEventData->mDecorator->SetPopupActive( false );
2160 mEventData->mDecoratorUpdated = true;
2163 case EventData::SELECTING:
2165 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2166 mEventData->mDecorator->StopCursorBlink();
2167 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2168 if ( mEventData->mGrabHandleEnabled )
2170 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2171 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2173 mEventData->mDecorator->SetHighlightActive( true );
2174 if( mEventData->mGrabHandlePopupEnabled )
2177 mEventData->mDecorator->SetPopupActive( true );
2179 mEventData->mDecoratorUpdated = true;
2182 case EventData::EDITING:
2184 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2185 if( mEventData->mCursorBlinkEnabled )
2187 mEventData->mDecorator->StartCursorBlink();
2189 // Grab handle is not shown until a tap is received whilst EDITING
2190 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2191 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2192 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2193 mEventData->mDecorator->SetHighlightActive( false );
2194 if( mEventData->mGrabHandlePopupEnabled )
2196 mEventData->mDecorator->SetPopupActive( false );
2198 mEventData->mDecoratorUpdated = true;
2201 case EventData::EDITING_WITH_POPUP:
2203 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2205 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2206 if( mEventData->mCursorBlinkEnabled )
2208 mEventData->mDecorator->StartCursorBlink();
2210 if( mEventData->mSelectionEnabled )
2212 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2213 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2214 mEventData->mDecorator->SetHighlightActive( false );
2216 else if ( mEventData->mGrabHandleEnabled )
2218 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2220 if( mEventData->mGrabHandlePopupEnabled )
2223 mEventData->mDecorator->SetPopupActive( true );
2225 mEventData->mDecoratorUpdated = true;
2228 case EventData::EDITING_WITH_GRAB_HANDLE:
2230 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2232 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2233 if( mEventData->mCursorBlinkEnabled )
2235 mEventData->mDecorator->StartCursorBlink();
2237 // Grab handle is not shown until a tap is received whilst EDITING
2238 if ( mEventData->mGrabHandleEnabled )
2240 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2242 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2243 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2244 mEventData->mDecorator->SetHighlightActive( false );
2245 if( mEventData->mGrabHandlePopupEnabled )
2247 mEventData->mDecorator->SetPopupActive( false );
2249 mEventData->mDecoratorUpdated = true;
2252 case EventData::SELECTION_HANDLE_PANNING:
2254 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2255 mEventData->mDecorator->StopCursorBlink();
2256 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2257 if ( mEventData->mGrabHandleEnabled )
2259 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2260 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2262 mEventData->mDecorator->SetHighlightActive( true );
2263 if( mEventData->mGrabHandlePopupEnabled )
2265 mEventData->mDecorator->SetPopupActive( false );
2267 mEventData->mDecoratorUpdated = true;
2270 case EventData::GRAB_HANDLE_PANNING:
2272 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2274 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2275 if( mEventData->mCursorBlinkEnabled )
2277 mEventData->mDecorator->StartCursorBlink();
2279 if ( mEventData->mGrabHandleEnabled )
2281 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2283 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2284 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2285 mEventData->mDecorator->SetHighlightActive( false );
2286 if( mEventData->mGrabHandlePopupEnabled )
2288 mEventData->mDecorator->SetPopupActive( false );
2290 mEventData->mDecoratorUpdated = true;
2293 case EventData::EDITING_WITH_PASTE_POPUP:
2295 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2297 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2298 if( mEventData->mCursorBlinkEnabled )
2300 mEventData->mDecorator->StartCursorBlink();
2303 if ( mEventData->mGrabHandleEnabled )
2305 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2307 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2308 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2309 mEventData->mDecorator->SetHighlightActive( false );
2311 if( mEventData->mGrabHandlePopupEnabled )
2314 mEventData->mDecorator->SetPopupActive( true );
2316 mEventData->mDecoratorUpdated = true;
2319 case EventData::TEXT_PANNING:
2321 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2322 mEventData->mDecorator->StopCursorBlink();
2323 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2324 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2325 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2327 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2328 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2329 mEventData->mDecorator->SetHighlightActive( true );
2332 if( mEventData->mGrabHandlePopupEnabled )
2334 mEventData->mDecorator->SetPopupActive( false );
2337 mEventData->mDecoratorUpdated = true;
2344 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2345 CursorInfo& cursorInfo )
2347 if( !IsShowingRealText() )
2349 // Do not want to use the place-holder text to set the cursor position.
2351 // Use the line's height of the font's family set to set the cursor's size.
2352 // If there is no font's family set, use the default font.
2353 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2355 cursorInfo.lineOffset = 0.f;
2356 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2357 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2360 if( mModel->mMatchSystemLanguageDirection )
2362 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2365 switch( mModel->mHorizontalAlignment )
2367 case Text::HorizontalAlignment::BEGIN :
2371 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2375 cursorInfo.primaryPosition.x = 0.f;
2379 case Text::HorizontalAlignment::CENTER:
2381 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2384 case Text::HorizontalAlignment::END:
2388 cursorInfo.primaryPosition.x = 0.f;
2392 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2398 // Nothing else to do.
2402 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2403 GetCursorPositionParameters parameters;
2404 parameters.visualModel = mModel->mVisualModel;
2405 parameters.logicalModel = mModel->mLogicalModel;
2406 parameters.metrics = mMetrics;
2407 parameters.logical = logical;
2408 parameters.isMultiline = isMultiLine;
2410 Text::GetCursorPosition( parameters,
2413 // Adds Outline offset.
2414 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2415 cursorInfo.primaryPosition.x += outlineWidth;
2416 cursorInfo.primaryPosition.y += outlineWidth;
2417 cursorInfo.secondaryPosition.x += outlineWidth;
2418 cursorInfo.secondaryPosition.y += outlineWidth;
2422 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2424 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2425 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2427 if( 0.f > cursorInfo.primaryPosition.x )
2429 cursorInfo.primaryPosition.x = 0.f;
2432 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2433 if( cursorInfo.primaryPosition.x > edgeWidth )
2435 cursorInfo.primaryPosition.x = edgeWidth;
2440 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2442 if( NULL == mEventData )
2444 // Nothing to do if there is no text input.
2448 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2450 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2451 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2453 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2454 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2456 if( numberOfCharacters > 1u )
2458 const Script script = mModel->mLogicalModel->GetScript( index );
2459 if( HasLigatureMustBreak( script ) )
2461 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2462 numberOfCharacters = 1u;
2467 while( 0u == numberOfCharacters )
2470 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2474 if( index < mEventData->mPrimaryCursorPosition )
2476 cursorIndex -= numberOfCharacters;
2480 cursorIndex += numberOfCharacters;
2483 // Will update the cursor hook position.
2484 mEventData->mUpdateCursorHookPosition = true;
2489 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2491 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2492 if( NULL == mEventData )
2494 // Nothing to do if there is no text input.
2495 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2499 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2501 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2503 // Sets the cursor position.
2504 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2507 cursorInfo.primaryCursorHeight,
2508 cursorInfo.lineHeight );
2509 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2511 if( mEventData->mUpdateGrabHandlePosition )
2513 // Sets the grab handle position.
2514 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2516 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2517 cursorInfo.lineHeight );
2520 if( cursorInfo.isSecondaryCursor )
2522 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2523 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2524 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2525 cursorInfo.secondaryCursorHeight,
2526 cursorInfo.lineHeight );
2527 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2530 // Set which cursors are active according the state.
2531 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2533 if( cursorInfo.isSecondaryCursor )
2535 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2539 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2544 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2547 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2550 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2551 const CursorInfo& cursorInfo )
2553 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2554 ( RIGHT_SELECTION_HANDLE != handleType ) )
2559 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2561 // Sets the handle's position.
2562 mEventData->mDecorator->SetPosition( handleType,
2564 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2565 cursorInfo.lineHeight );
2567 // If selection handle at start of the text and other at end of the text then all text is selected.
2568 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2569 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2570 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2573 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2575 // Clamp between -space & -alignment offset.
2577 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2579 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2580 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2581 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2583 mEventData->mDecoratorUpdated = true;
2587 mModel->mScrollPosition.x = 0.f;
2591 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2593 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2595 // Nothing to do if the text is single line.
2599 // Clamp between -space & 0.
2600 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2602 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2603 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2604 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2606 mEventData->mDecoratorUpdated = true;
2610 mModel->mScrollPosition.y = 0.f;
2614 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2616 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2618 // position is in actor's coords.
2619 const float positionEndX = position.x + cursorWidth;
2620 const float positionEndY = position.y + lineHeight;
2622 // Transform the position to decorator coords.
2623 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2624 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2626 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2627 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2629 if( decoratorPositionBeginX < 0.f )
2631 mModel->mScrollPosition.x = -position.x;
2633 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2635 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2638 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2640 if( decoratorPositionBeginY < 0.f )
2642 mModel->mScrollPosition.y = -position.y;
2644 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2646 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2651 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2653 // Get the current cursor position in decorator coords.
2654 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2656 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2660 // Calculate the offset to match the cursor position before the character was deleted.
2661 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2663 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2664 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2666 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2667 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2671 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2672 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2674 // Makes the new cursor position visible if needed.
2675 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2678 void Controller::Impl::RequestRelayout()
2680 if( NULL != mControlInterface )
2682 mControlInterface->RequestTextRelayout();
2686 Actor Controller::Impl::CreateBackgroundActor()
2688 // NOTE: Currently we only support background color for one line left-to-right text,
2689 // so the following calculation is based on one line left-to-right text only!
2693 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2694 if( numberOfGlyphs > 0u )
2696 Vector<GlyphInfo> glyphs;
2697 glyphs.Resize( numberOfGlyphs );
2699 Vector<Vector2> positions;
2700 positions.Resize( numberOfGlyphs );
2702 // Get the line where the glyphs are laid-out.
2703 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2704 float alignmentOffset = lineRun->alignmentOffset;
2705 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
2711 glyphs.Resize( numberOfGlyphs );
2712 positions.Resize( numberOfGlyphs );
2714 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2715 const Vector2* const positionsBuffer = positions.Begin();
2717 BackgroundMesh mesh;
2718 mesh.mVertices.Reserve( 4u * glyphs.Size() );
2719 mesh.mIndices.Reserve( 6u * glyphs.Size() );
2721 const Vector2 textSize = mView.GetLayoutSize();
2723 const float offsetX = textSize.width * 0.5f;
2724 const float offsetY = textSize.height * 0.5f;
2726 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2727 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2728 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2731 uint32_t numberOfQuads = 0u;
2733 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
2735 const GlyphInfo& glyph = *( glyphsBuffer + i );
2737 // Get the background color of the character.
2738 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2739 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
2740 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
2742 // Only create quads for glyphs with a background color
2743 if ( backgroundColor != Color::TRANSPARENT )
2745 const Vector2 position = *( positionsBuffer + i );
2747 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
2749 quad.x = position.x;
2751 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2752 quad.w = textSize.height;
2754 else if ( i == 0u ) // The first glyph in the whole text
2756 quad.x = position.x;
2758 quad.z = quad.x - glyph.xBearing + glyph.advance;
2759 quad.w = textSize.height;
2761 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
2763 quad.x = position.x - glyph.xBearing;
2765 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2766 quad.w = textSize.height;
2768 else // The glyph in the middle of the text
2770 quad.x = position.x - glyph.xBearing;
2772 quad.z = quad.x + glyph.advance;
2773 quad.w = textSize.height;
2776 BackgroundVertex vertex;
2779 vertex.mPosition.x = quad.x - offsetX;
2780 vertex.mPosition.y = quad.y - offsetY;
2781 vertex.mColor = backgroundColor;
2782 mesh.mVertices.PushBack( vertex );
2785 vertex.mPosition.x = quad.z - offsetX;
2786 vertex.mPosition.y = quad.y - offsetY;
2787 vertex.mColor = backgroundColor;
2788 mesh.mVertices.PushBack( vertex );
2791 vertex.mPosition.x = quad.x - offsetX;
2792 vertex.mPosition.y = quad.w - offsetY;
2793 vertex.mColor = backgroundColor;
2794 mesh.mVertices.PushBack( vertex );
2797 vertex.mPosition.x = quad.z - offsetX;
2798 vertex.mPosition.y = quad.w - offsetY;
2799 vertex.mColor = backgroundColor;
2800 mesh.mVertices.PushBack( vertex );
2802 // Six indices in counter clockwise winding
2803 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2804 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
2805 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2806 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2807 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
2808 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2814 // Only create the background actor if there are glyphs with background color
2815 if ( mesh.mVertices.Count() > 0u )
2817 Property::Map quadVertexFormat;
2818 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2819 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2821 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2822 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2824 Geometry quadGeometry = Geometry::New();
2825 quadGeometry.AddVertexBuffer( quadVertices );
2826 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2828 if( !mShaderBackground )
2830 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
2833 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2834 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2835 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2837 actor = Actor::New();
2838 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2839 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2840 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2841 actor.SetProperty( Actor::Property::SIZE, textSize );
2842 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2843 actor.AddRenderer( renderer );
2852 } // namespace Toolkit