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 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1427 if( nullptr == mEventData )
1431 return mEventData->mPrimaryCursorPosition;
1434 bool Controller::Impl::SetPrimaryCursorPosition( CharacterIndex index )
1436 if( nullptr == mEventData )
1438 // Nothing to do if there is no text.
1442 if( mEventData->mPrimaryCursorPosition == index )
1444 // Nothing for same cursor position.
1448 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1449 mEventData->mPrimaryCursorPosition = std::min(index, length);
1450 ChangeState( EventData::EDITING );
1451 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1452 mEventData->mUpdateCursorPosition = true;
1453 ScrollTextToMatchCursor();
1457 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1463 range.first = mEventData->mLeftSelectionPosition;
1464 range.second = mEventData->mRightSelectionPosition;
1470 bool Controller::Impl::IsEditable() const
1472 return mEventData && mEventData->mEditingEnabled;
1475 void Controller::Impl::SetEditable( bool editable )
1479 mEventData->mEditingEnabled = editable;
1483 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1485 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1487 // Nothing to select if handles are in the same place.
1488 selectedText.clear();
1492 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1494 //Get start and end position of selection
1495 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1496 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1498 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1499 const Length numberOfCharacters = utf32Characters.Count();
1501 // Validate the start and end selection points
1502 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1504 //Get text as a UTF8 string
1505 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1507 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1509 // Keep a copy of the current input style.
1510 InputStyle currentInputStyle;
1511 currentInputStyle.Copy( mEventData->mInputStyle );
1513 // Set as input style the style of the first deleted character.
1514 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1516 // Compare if the input style has changed.
1517 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1519 if( hasInputStyleChanged )
1521 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1522 // Queue the input style changed signal.
1523 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1526 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1528 // Mark the paragraphs to be updated.
1529 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1531 mTextUpdateInfo.mCharacterIndex = 0;
1532 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1533 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1534 mTextUpdateInfo.mClearAll = true;
1538 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1539 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1542 // Delete text between handles
1543 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1544 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1545 utf32Characters.Erase( first, last );
1547 // Will show the cursor at the first character of the selection.
1548 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1552 // Will show the cursor at the last character of the selection.
1553 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1556 mEventData->mDecoratorUpdated = true;
1560 void Controller::Impl::SetSelection( int start, int end )
1562 mEventData->mLeftSelectionPosition = start;
1563 mEventData->mRightSelectionPosition = end;
1564 mEventData->mUpdateCursorPosition = true;
1567 std::pair< int, int > Controller::Impl::GetSelectionIndexes() const
1569 return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition };
1572 void Controller::Impl::ShowClipboard()
1576 mClipboard.ShowClipboard();
1580 void Controller::Impl::HideClipboard()
1582 if( mClipboard && mClipboardHideEnabled )
1584 mClipboard.HideClipboard();
1588 void Controller::Impl::SetClipboardHideEnable(bool enable)
1590 mClipboardHideEnabled = enable;
1593 bool Controller::Impl::CopyStringToClipboard( const std::string& source )
1595 //Send string to clipboard
1596 return ( mClipboard && mClipboard.SetItem( source ) );
1599 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1601 std::string selectedText;
1602 RetrieveSelection( selectedText, deleteAfterSending );
1603 CopyStringToClipboard( selectedText );
1604 ChangeState( EventData::EDITING );
1607 void Controller::Impl::RequestGetTextFromClipboard()
1611 mClipboard.RequestItem();
1615 void Controller::Impl::RepositionSelectionHandles()
1617 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1618 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1620 if( selectionStart == selectionEnd )
1622 // Nothing to select if handles are in the same place.
1623 // So, deactive Highlight box.
1624 mEventData->mDecorator->SetHighlightActive( false );
1628 mEventData->mDecorator->ClearHighlights();
1630 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1631 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1632 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1633 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1634 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1635 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1636 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1638 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1639 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1640 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1642 // Swap the indices if the start is greater than the end.
1643 const bool indicesSwapped = selectionStart > selectionEnd;
1645 // Tell the decorator to flip the selection handles if needed.
1646 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1648 if( indicesSwapped )
1650 std::swap( selectionStart, selectionEnd );
1653 // Get the indices to the first and last selected glyphs.
1654 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1655 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1656 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1657 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1659 // Get the lines where the glyphs are laid-out.
1660 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1662 LineIndex lineIndex = 0u;
1663 Length numberOfLines = 0u;
1664 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1665 1u + glyphEnd - glyphStart,
1668 const LineIndex firstLineIndex = lineIndex;
1670 // Create the structure to store some selection box info.
1671 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1672 selectionBoxLinesInfo.Resize( numberOfLines );
1674 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1675 selectionBoxInfo->minX = MAX_FLOAT;
1676 selectionBoxInfo->maxX = MIN_FLOAT;
1678 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1679 float minHighlightX = std::numeric_limits<float>::max();
1680 float maxHighlightX = std::numeric_limits<float>::min();
1682 Vector2 highLightPosition; // The highlight position in decorator's coords.
1684 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1686 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1687 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1690 // Transform to decorator's (control) coords.
1691 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1693 lineRun += firstLineIndex;
1695 // The line height is the addition of the line ascender and the line descender.
1696 // However, the line descender has a negative value, hence the subtraction.
1697 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1699 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1701 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1702 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1703 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1705 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1706 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1707 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1709 // The number of quads of the selection box.
1710 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1711 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1713 // Count the actual number of quads.
1714 unsigned int actualNumberOfQuads = 0u;
1717 // Traverse the glyphs.
1718 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1720 const GlyphInfo& glyph = *( glyphsBuffer + index );
1721 const Vector2& position = *( positionsBuffer + index );
1723 if( splitStartGlyph )
1725 // 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.
1727 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1728 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1729 // Get the direction of the character.
1730 CharacterDirection isCurrentRightToLeft = false;
1731 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1733 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1736 // The end point could be in the middle of the ligature.
1737 // Calculate the number of characters selected.
1738 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1740 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1741 quad.y = selectionBoxInfo->lineOffset;
1742 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1743 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1745 // Store the min and max 'x' for each line.
1746 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1747 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1749 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1750 ++actualNumberOfQuads;
1752 splitStartGlyph = false;
1756 if( splitEndGlyph && ( index == glyphEnd ) )
1758 // 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.
1760 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1761 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1762 // Get the direction of the character.
1763 CharacterDirection isCurrentRightToLeft = false;
1764 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1766 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1769 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1771 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1772 quad.y = selectionBoxInfo->lineOffset;
1773 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1774 quad.w = quad.y + selectionBoxInfo->lineHeight;
1776 // Store the min and max 'x' for each line.
1777 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1778 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1780 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1782 ++actualNumberOfQuads;
1784 splitEndGlyph = false;
1788 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1789 quad.y = selectionBoxInfo->lineOffset;
1790 quad.z = quad.x + glyph.advance;
1791 quad.w = quad.y + selectionBoxInfo->lineHeight;
1793 // Store the min and max 'x' for each line.
1794 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1795 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1797 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1799 ++actualNumberOfQuads;
1801 // Whether to retrieve the next line.
1802 if( index == lastGlyphOfLine )
1805 if( lineIndex < firstLineIndex + numberOfLines )
1807 // Retrieve the next line.
1810 // Get the last glyph of the new line.
1811 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1813 // Keep the offset and height of the current selection box.
1814 const float currentLineOffset = selectionBoxInfo->lineOffset;
1815 const float currentLineHeight = selectionBoxInfo->lineHeight;
1817 // Get the selection box info for the next line.
1820 selectionBoxInfo->minX = MAX_FLOAT;
1821 selectionBoxInfo->maxX = MIN_FLOAT;
1823 // Update the line's vertical offset.
1824 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1826 // The line height is the addition of the line ascender and the line descender.
1827 // However, the line descender has a negative value, hence the subtraction.
1828 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1833 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1834 // The final width is calculated after 'boxifying' the selection.
1835 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1836 endIt = selectionBoxLinesInfo.End();
1840 const SelectionBoxInfo& info = *it;
1842 // Update the size of the highlighted text.
1843 highLightSize.height += info.lineHeight;
1844 minHighlightX = std::min( minHighlightX, info.minX );
1845 maxHighlightX = std::max( maxHighlightX, info.maxX );
1848 // Add extra geometry to 'boxify' the selection.
1850 if( 1u < numberOfLines )
1852 // Boxify the first line.
1853 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
1854 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1856 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1857 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1862 quad.y = firstSelectionBoxLineInfo.lineOffset;
1863 quad.z = firstSelectionBoxLineInfo.minX;
1864 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1866 // Boxify at the beginning of the line.
1867 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1869 ++actualNumberOfQuads;
1871 // Update the size of the highlighted text.
1872 minHighlightX = 0.f;
1877 quad.x = firstSelectionBoxLineInfo.maxX;
1878 quad.y = firstSelectionBoxLineInfo.lineOffset;
1879 quad.z = mModel->mVisualModel->mControlSize.width;
1880 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1882 // Boxify at the end of the line.
1883 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1885 ++actualNumberOfQuads;
1887 // Update the size of the highlighted text.
1888 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1891 // Boxify the central lines.
1892 if( 2u < numberOfLines )
1894 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1895 endIt = selectionBoxLinesInfo.End() - 1u;
1899 const SelectionBoxInfo& info = *it;
1902 quad.y = info.lineOffset;
1904 quad.w = info.lineOffset + info.lineHeight;
1906 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1908 ++actualNumberOfQuads;
1911 quad.y = info.lineOffset;
1912 quad.z = mModel->mVisualModel->mControlSize.width;
1913 quad.w = info.lineOffset + info.lineHeight;
1915 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1917 ++actualNumberOfQuads;
1920 // Update the size of the highlighted text.
1921 minHighlightX = 0.f;
1922 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1925 // Boxify the last line.
1926 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1927 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1929 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1930 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1935 quad.y = lastSelectionBoxLineInfo.lineOffset;
1936 quad.z = lastSelectionBoxLineInfo.minX;
1937 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1939 // Boxify at the beginning of the line.
1940 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1942 ++actualNumberOfQuads;
1944 // Update the size of the highlighted text.
1945 minHighlightX = 0.f;
1950 quad.x = lastSelectionBoxLineInfo.maxX;
1951 quad.y = lastSelectionBoxLineInfo.lineOffset;
1952 quad.z = mModel->mVisualModel->mControlSize.width;
1953 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1955 // Boxify at the end of the line.
1956 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1958 ++actualNumberOfQuads;
1960 // Update the size of the highlighted text.
1961 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1965 // Set the actual number of quads.
1966 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
1968 // Sets the highlight's size and position. In decorator's coords.
1969 // The highlight's height has been calculated above (before 'boxifying' the highlight).
1970 highLightSize.width = maxHighlightX - minHighlightX;
1972 highLightPosition.x = minHighlightX;
1973 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1974 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
1976 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
1978 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1980 CursorInfo primaryCursorInfo;
1981 GetCursorPosition( mEventData->mLeftSelectionPosition,
1982 primaryCursorInfo );
1984 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1986 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1988 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1989 primaryCursorInfo.lineHeight );
1991 CursorInfo secondaryCursorInfo;
1992 GetCursorPosition( mEventData->mRightSelectionPosition,
1993 secondaryCursorInfo );
1995 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1997 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1998 secondaryPosition.x,
1999 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2000 secondaryCursorInfo.lineHeight );
2003 // Set the flag to update the decorator.
2004 mEventData->mDecoratorUpdated = true;
2007 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2009 if( NULL == mEventData )
2011 // Nothing to do if there is no text input.
2015 if( IsShowingPlaceholderText() )
2017 // Nothing to do if there is the place-holder text.
2021 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2022 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2023 if( ( 0 == numberOfGlyphs ) ||
2024 ( 0 == numberOfLines ) )
2026 // Nothing to do if there is no text.
2030 // Find which word was selected
2031 CharacterIndex selectionStart( 0 );
2032 CharacterIndex selectionEnd( 0 );
2033 CharacterIndex noTextHitIndex( 0 );
2034 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2035 mModel->mLogicalModel,
2042 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2044 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2046 ChangeState( EventData::SELECTING );
2048 mEventData->mLeftSelectionPosition = selectionStart;
2049 mEventData->mRightSelectionPosition = selectionEnd;
2051 mEventData->mUpdateLeftSelectionPosition = true;
2052 mEventData->mUpdateRightSelectionPosition = true;
2053 mEventData->mUpdateHighlightBox = true;
2055 // It may happen an InputMethodContext commit event arrives before the selection event
2056 // if the InputMethodContext is in pre-edit state. The commit event will set the
2057 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2058 // to false, the highlight box won't be updated.
2059 mEventData->mUpdateCursorPosition = false;
2061 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2063 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2064 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2066 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2068 // Nothing to select. i.e. a white space, out of bounds
2069 ChangeState( EventData::EDITING_WITH_POPUP );
2071 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2073 mEventData->mUpdateCursorPosition = true;
2074 mEventData->mUpdateGrabHandlePosition = true;
2075 mEventData->mScrollAfterUpdatePosition = true;
2076 mEventData->mUpdateInputStyle = true;
2078 else if( Controller::NoTextTap::NO_ACTION == action )
2080 // Nothing to select. i.e. a white space, out of bounds
2081 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2083 mEventData->mUpdateCursorPosition = true;
2084 mEventData->mUpdateGrabHandlePosition = true;
2085 mEventData->mScrollAfterUpdatePosition = true;
2086 mEventData->mUpdateInputStyle = true;
2090 void Controller::Impl::SetPopupButtons()
2093 * Sets the Popup buttons to be shown depending on State.
2095 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2097 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2100 bool isEditable = IsEditable();
2101 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2103 if( EventData::SELECTING == mEventData->mState )
2105 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2108 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2111 if( !IsClipboardEmpty() )
2115 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2117 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2120 if( !mEventData->mAllTextSelected )
2122 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2125 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2127 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2129 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2132 if( !IsClipboardEmpty() )
2136 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2138 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2141 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2143 if ( !IsClipboardEmpty() )
2147 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2149 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2153 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2156 void Controller::Impl::ChangeState( EventData::State newState )
2158 if( NULL == mEventData )
2160 // Nothing to do if there is no text input.
2164 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2166 if( mEventData->mState != newState )
2168 mEventData->mPreviousState = mEventData->mState;
2169 mEventData->mState = newState;
2171 switch( mEventData->mState )
2173 case EventData::INACTIVE:
2175 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2176 mEventData->mDecorator->StopCursorBlink();
2177 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2178 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2179 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2180 mEventData->mDecorator->SetHighlightActive( false );
2181 mEventData->mDecorator->SetPopupActive( false );
2182 mEventData->mDecoratorUpdated = true;
2185 case EventData::INTERRUPTED:
2187 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2188 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2189 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2190 mEventData->mDecorator->SetHighlightActive( false );
2191 mEventData->mDecorator->SetPopupActive( false );
2192 mEventData->mDecoratorUpdated = true;
2195 case EventData::SELECTING:
2197 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2198 mEventData->mDecorator->StopCursorBlink();
2199 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2200 if ( mEventData->mGrabHandleEnabled )
2202 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2203 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2205 mEventData->mDecorator->SetHighlightActive( true );
2206 if( mEventData->mGrabHandlePopupEnabled )
2209 mEventData->mDecorator->SetPopupActive( true );
2211 mEventData->mDecoratorUpdated = true;
2214 case EventData::EDITING:
2216 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2217 if( mEventData->mCursorBlinkEnabled )
2219 mEventData->mDecorator->StartCursorBlink();
2221 // Grab handle is not shown until a tap is received whilst EDITING
2222 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2223 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2224 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2225 mEventData->mDecorator->SetHighlightActive( false );
2226 if( mEventData->mGrabHandlePopupEnabled )
2228 mEventData->mDecorator->SetPopupActive( false );
2230 mEventData->mDecoratorUpdated = true;
2233 case EventData::EDITING_WITH_POPUP:
2235 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2237 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2238 if( mEventData->mCursorBlinkEnabled )
2240 mEventData->mDecorator->StartCursorBlink();
2242 if( mEventData->mSelectionEnabled )
2244 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2245 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2246 mEventData->mDecorator->SetHighlightActive( false );
2248 else if ( mEventData->mGrabHandleEnabled )
2250 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2252 if( mEventData->mGrabHandlePopupEnabled )
2255 mEventData->mDecorator->SetPopupActive( true );
2257 mEventData->mDecoratorUpdated = true;
2260 case EventData::EDITING_WITH_GRAB_HANDLE:
2262 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2264 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2265 if( mEventData->mCursorBlinkEnabled )
2267 mEventData->mDecorator->StartCursorBlink();
2269 // Grab handle is not shown until a tap is received whilst EDITING
2270 if ( mEventData->mGrabHandleEnabled )
2272 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2274 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2275 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2276 mEventData->mDecorator->SetHighlightActive( false );
2277 if( mEventData->mGrabHandlePopupEnabled )
2279 mEventData->mDecorator->SetPopupActive( false );
2281 mEventData->mDecoratorUpdated = true;
2284 case EventData::SELECTION_HANDLE_PANNING:
2286 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2287 mEventData->mDecorator->StopCursorBlink();
2288 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2289 if ( mEventData->mGrabHandleEnabled )
2291 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2292 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2294 mEventData->mDecorator->SetHighlightActive( true );
2295 if( mEventData->mGrabHandlePopupEnabled )
2297 mEventData->mDecorator->SetPopupActive( false );
2299 mEventData->mDecoratorUpdated = true;
2302 case EventData::GRAB_HANDLE_PANNING:
2304 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2306 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2307 if( mEventData->mCursorBlinkEnabled )
2309 mEventData->mDecorator->StartCursorBlink();
2311 if ( mEventData->mGrabHandleEnabled )
2313 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2315 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2316 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2317 mEventData->mDecorator->SetHighlightActive( false );
2318 if( mEventData->mGrabHandlePopupEnabled )
2320 mEventData->mDecorator->SetPopupActive( false );
2322 mEventData->mDecoratorUpdated = true;
2325 case EventData::EDITING_WITH_PASTE_POPUP:
2327 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2329 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2330 if( mEventData->mCursorBlinkEnabled )
2332 mEventData->mDecorator->StartCursorBlink();
2335 if ( mEventData->mGrabHandleEnabled )
2337 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2339 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2340 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2341 mEventData->mDecorator->SetHighlightActive( false );
2343 if( mEventData->mGrabHandlePopupEnabled )
2346 mEventData->mDecorator->SetPopupActive( true );
2348 mEventData->mDecoratorUpdated = true;
2351 case EventData::TEXT_PANNING:
2353 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2354 mEventData->mDecorator->StopCursorBlink();
2355 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2356 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2357 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2359 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2360 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2361 mEventData->mDecorator->SetHighlightActive( true );
2364 if( mEventData->mGrabHandlePopupEnabled )
2366 mEventData->mDecorator->SetPopupActive( false );
2369 mEventData->mDecoratorUpdated = true;
2376 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2377 CursorInfo& cursorInfo )
2379 if( !IsShowingRealText() )
2381 // Do not want to use the place-holder text to set the cursor position.
2383 // Use the line's height of the font's family set to set the cursor's size.
2384 // If there is no font's family set, use the default font.
2385 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2387 cursorInfo.lineOffset = 0.f;
2388 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2389 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2392 if( mModel->mMatchSystemLanguageDirection )
2394 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2397 switch( mModel->mHorizontalAlignment )
2399 case Text::HorizontalAlignment::BEGIN :
2403 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2407 cursorInfo.primaryPosition.x = 0.f;
2411 case Text::HorizontalAlignment::CENTER:
2413 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2416 case Text::HorizontalAlignment::END:
2420 cursorInfo.primaryPosition.x = 0.f;
2424 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2430 // Nothing else to do.
2434 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2435 GetCursorPositionParameters parameters;
2436 parameters.visualModel = mModel->mVisualModel;
2437 parameters.logicalModel = mModel->mLogicalModel;
2438 parameters.metrics = mMetrics;
2439 parameters.logical = logical;
2440 parameters.isMultiline = isMultiLine;
2442 Text::GetCursorPosition( parameters,
2445 // Adds Outline offset.
2446 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2447 cursorInfo.primaryPosition.x += outlineWidth;
2448 cursorInfo.primaryPosition.y += outlineWidth;
2449 cursorInfo.secondaryPosition.x += outlineWidth;
2450 cursorInfo.secondaryPosition.y += outlineWidth;
2454 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2456 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2457 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2459 if( 0.f > cursorInfo.primaryPosition.x )
2461 cursorInfo.primaryPosition.x = 0.f;
2464 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2465 if( cursorInfo.primaryPosition.x > edgeWidth )
2467 cursorInfo.primaryPosition.x = edgeWidth;
2472 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2474 if( NULL == mEventData )
2476 // Nothing to do if there is no text input.
2480 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2482 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2483 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2485 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2486 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2488 if( numberOfCharacters > 1u )
2490 const Script script = mModel->mLogicalModel->GetScript( index );
2491 if( HasLigatureMustBreak( script ) )
2493 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2494 numberOfCharacters = 1u;
2499 while( 0u == numberOfCharacters )
2502 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2506 if( index < mEventData->mPrimaryCursorPosition )
2508 cursorIndex -= numberOfCharacters;
2512 cursorIndex += numberOfCharacters;
2515 // Will update the cursor hook position.
2516 mEventData->mUpdateCursorHookPosition = true;
2521 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2523 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2524 if( NULL == mEventData )
2526 // Nothing to do if there is no text input.
2527 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2531 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2533 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2535 // Sets the cursor position.
2536 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2539 cursorInfo.primaryCursorHeight,
2540 cursorInfo.lineHeight );
2541 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2543 if( mEventData->mUpdateGrabHandlePosition )
2545 // Sets the grab handle position.
2546 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2548 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2549 cursorInfo.lineHeight );
2552 if( cursorInfo.isSecondaryCursor )
2554 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2555 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2556 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2557 cursorInfo.secondaryCursorHeight,
2558 cursorInfo.lineHeight );
2559 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2562 // Set which cursors are active according the state.
2563 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2565 if( cursorInfo.isSecondaryCursor )
2567 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2571 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2576 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2579 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2582 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2583 const CursorInfo& cursorInfo )
2585 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2586 ( RIGHT_SELECTION_HANDLE != handleType ) )
2591 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2593 // Sets the handle's position.
2594 mEventData->mDecorator->SetPosition( handleType,
2596 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2597 cursorInfo.lineHeight );
2599 // If selection handle at start of the text and other at end of the text then all text is selected.
2600 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2601 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2602 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2605 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2607 // Clamp between -space & -alignment offset.
2609 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2611 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2612 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2613 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2615 mEventData->mDecoratorUpdated = true;
2619 mModel->mScrollPosition.x = 0.f;
2623 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2625 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2627 // Nothing to do if the text is single line.
2631 // Clamp between -space & 0.
2632 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2634 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2635 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2636 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2638 mEventData->mDecoratorUpdated = true;
2642 mModel->mScrollPosition.y = 0.f;
2646 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2648 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2650 // position is in actor's coords.
2651 const float positionEndX = position.x + cursorWidth;
2652 const float positionEndY = position.y + lineHeight;
2654 // Transform the position to decorator coords.
2655 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2656 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2658 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2659 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2661 if( decoratorPositionBeginX < 0.f )
2663 mModel->mScrollPosition.x = -position.x;
2665 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2667 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2670 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2672 if( decoratorPositionBeginY < 0.f )
2674 mModel->mScrollPosition.y = -position.y;
2676 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2678 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2683 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2685 // Get the current cursor position in decorator coords.
2686 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2688 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2692 // Calculate the offset to match the cursor position before the character was deleted.
2693 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2695 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2696 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2698 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2699 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2703 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2704 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2706 // Makes the new cursor position visible if needed.
2707 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2710 void Controller::Impl::ScrollTextToMatchCursor()
2712 CursorInfo cursorInfo;
2713 GetCursorPosition( mEventData->mPrimaryCursorPosition, cursorInfo );
2714 ScrollTextToMatchCursor(cursorInfo);
2717 void Controller::Impl::RequestRelayout()
2719 if( NULL != mControlInterface )
2721 mControlInterface->RequestTextRelayout();
2725 Actor Controller::Impl::CreateBackgroundActor()
2727 // NOTE: Currently we only support background color for one line left-to-right text,
2728 // so the following calculation is based on one line left-to-right text only!
2732 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2733 if( numberOfGlyphs > 0u )
2735 Vector<GlyphInfo> glyphs;
2736 glyphs.Resize( numberOfGlyphs );
2738 Vector<Vector2> positions;
2739 positions.Resize( numberOfGlyphs );
2741 // Get the line where the glyphs are laid-out.
2742 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2743 float alignmentOffset = lineRun->alignmentOffset;
2744 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
2750 glyphs.Resize( numberOfGlyphs );
2751 positions.Resize( numberOfGlyphs );
2753 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2754 const Vector2* const positionsBuffer = positions.Begin();
2756 BackgroundMesh mesh;
2757 mesh.mVertices.Reserve( 4u * glyphs.Size() );
2758 mesh.mIndices.Reserve( 6u * glyphs.Size() );
2760 const Vector2 textSize = mView.GetLayoutSize();
2762 const float offsetX = textSize.width * 0.5f;
2763 const float offsetY = textSize.height * 0.5f;
2765 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2766 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2767 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2770 uint32_t numberOfQuads = 0u;
2772 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
2774 const GlyphInfo& glyph = *( glyphsBuffer + i );
2776 // Get the background color of the character.
2777 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2778 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
2779 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
2781 // Only create quads for glyphs with a background color
2782 if ( backgroundColor != Color::TRANSPARENT )
2784 const Vector2 position = *( positionsBuffer + i );
2786 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
2788 quad.x = position.x;
2790 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2791 quad.w = textSize.height;
2793 else if ( i == 0u ) // The first glyph in the whole text
2795 quad.x = position.x;
2797 quad.z = quad.x - glyph.xBearing + glyph.advance;
2798 quad.w = textSize.height;
2800 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
2802 quad.x = position.x - glyph.xBearing;
2804 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2805 quad.w = textSize.height;
2807 else // The glyph in the middle of the text
2809 quad.x = position.x - glyph.xBearing;
2811 quad.z = quad.x + glyph.advance;
2812 quad.w = textSize.height;
2815 BackgroundVertex vertex;
2818 vertex.mPosition.x = quad.x - offsetX;
2819 vertex.mPosition.y = quad.y - offsetY;
2820 vertex.mColor = backgroundColor;
2821 mesh.mVertices.PushBack( vertex );
2824 vertex.mPosition.x = quad.z - offsetX;
2825 vertex.mPosition.y = quad.y - offsetY;
2826 vertex.mColor = backgroundColor;
2827 mesh.mVertices.PushBack( vertex );
2830 vertex.mPosition.x = quad.x - offsetX;
2831 vertex.mPosition.y = quad.w - offsetY;
2832 vertex.mColor = backgroundColor;
2833 mesh.mVertices.PushBack( vertex );
2836 vertex.mPosition.x = quad.z - offsetX;
2837 vertex.mPosition.y = quad.w - offsetY;
2838 vertex.mColor = backgroundColor;
2839 mesh.mVertices.PushBack( vertex );
2841 // Six indices in counter clockwise winding
2842 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2843 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
2844 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2845 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2846 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
2847 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2853 // Only create the background actor if there are glyphs with background color
2854 if ( mesh.mVertices.Count() > 0u )
2856 Property::Map quadVertexFormat;
2857 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2858 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2860 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2861 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2863 Geometry quadGeometry = Geometry::New();
2864 quadGeometry.AddVertexBuffer( quadVertices );
2865 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2867 if( !mShaderBackground )
2869 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
2872 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2873 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2874 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2876 actor = Actor::New();
2877 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2878 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2879 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2880 actor.SetProperty( Actor::Property::SIZE, textSize );
2881 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2882 actor.AddRenderer( renderer );
2891 } // namespace Toolkit