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-editable-control-interface.h>
38 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
39 #include <dali-toolkit/internal/text/text-run-container.h>
47 * @brief Struct used to calculate the selection box.
49 struct SelectionBoxInfo
57 #if defined(DEBUG_ENABLED)
58 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
61 const float MAX_FLOAT = std::numeric_limits<float>::max();
62 const float MIN_FLOAT = std::numeric_limits<float>::min();
63 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
65 #define MAKE_SHADER(A)#A
67 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
68 attribute mediump vec2 aPosition;
69 attribute mediump vec4 aColor;
70 varying mediump vec4 vColor;
71 uniform highp mat4 uMvpMatrix;
75 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
76 gl_Position = uMvpMatrix * position;
81 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
82 varying mediump vec4 vColor;
83 uniform lowp vec4 uColor;
87 gl_FragColor = vColor * uColor;
91 struct BackgroundVertex
93 Vector2 mPosition; ///< Vertex posiiton
94 Vector4 mColor; ///< Vertex color
99 Vector< BackgroundVertex > mVertices; ///< container of vertices
100 Vector< unsigned short > mIndices; ///< container of indices
103 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
104 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
105 const float BRIGHTNESS_THRESHOLD = 0.179f;
106 const float CONSTANT_R = 0.2126f;
107 const float CONSTANT_G = 0.7152f;
108 const float CONSTANT_B = 0.0722f;
109 const Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
110 const Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
111 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
112 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
113 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
114 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
115 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
128 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
129 : mDecorator( decorator ),
130 mInputMethodContext( inputMethodContext ),
131 mPlaceholderFont( NULL ),
132 mPlaceholderTextActive(),
133 mPlaceholderTextInactive(),
134 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
136 mInputStyleChangedQueue(),
137 mPreviousState( INACTIVE ),
139 mPrimaryCursorPosition( 0u ),
140 mLeftSelectionPosition( 0u ),
141 mRightSelectionPosition( 0u ),
142 mPreEditStartPosition( 0u ),
143 mPreEditLength( 0u ),
144 mPreEditTextLength ( 0u ),
145 mCursorHookPositionX( 0.f ),
146 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
147 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
148 mIsShowingPlaceholderText( false ),
149 mPreEditFlag( false ),
150 mPreEditToCommitFlag ( false ),
151 mDecoratorUpdated( false ),
152 mCursorBlinkEnabled( true ),
153 mGrabHandleEnabled( true ),
154 mGrabHandlePopupEnabled( true ),
155 mSelectionEnabled( true ),
156 mUpdateCursorHookPosition( false ),
157 mUpdateCursorPosition( false ),
158 mUpdateGrabHandlePosition( false ),
159 mUpdateLeftSelectionPosition( false ),
160 mUpdateRightSelectionPosition( false ),
161 mIsLeftHandleSelected( false ),
162 mIsRightHandleSelected( false ),
163 mUpdateHighlightBox( false ),
164 mScrollAfterUpdatePosition( false ),
165 mScrollAfterDelete( false ),
166 mAllTextSelected( false ),
167 mUpdateInputStyle( false ),
168 mPasswordInput( false ),
169 mCheckScrollAmount( false ),
170 mIsPlaceholderPixelSize( false ),
171 mIsPlaceholderElideEnabled( false ),
172 mPlaceholderEllipsisFlag( false ),
173 mShiftSelectionFlag( true ),
174 mUpdateAlignment( false ),
175 mEditingEnabled( true )
179 EventData::~EventData()
182 bool Controller::Impl::ProcessInputEvents()
184 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
185 if( NULL == mEventData )
187 // Nothing to do if there is no text input.
188 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
192 if( mEventData->mDecorator )
194 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
195 iter != mEventData->mEventQueue.end();
200 case Event::CURSOR_KEY_EVENT:
202 OnCursorKeyEvent( *iter );
205 case Event::TAP_EVENT:
210 case Event::LONG_PRESS_EVENT:
212 OnLongPressEvent( *iter );
215 case Event::PAN_EVENT:
220 case Event::GRAB_HANDLE_EVENT:
221 case Event::LEFT_SELECTION_HANDLE_EVENT:
222 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
224 OnHandleEvent( *iter );
229 OnSelectEvent( *iter );
232 case Event::SELECT_ALL:
237 case Event::SELECT_NONE:
246 if( mEventData->mUpdateCursorPosition ||
247 mEventData->mUpdateHighlightBox )
249 NotifyInputMethodContext();
252 // The cursor must also be repositioned after inserts into the model
253 if( mEventData->mUpdateCursorPosition )
255 // Updates the cursor position and scrolls the text to make it visible.
256 CursorInfo cursorInfo;
257 // Calculate the cursor position from the new cursor index.
258 GetCursorPosition( mEventData->mPrimaryCursorPosition,
261 if( mEventData->mUpdateCursorHookPosition )
263 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
264 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
265 mEventData->mUpdateCursorHookPosition = false;
268 // Scroll first the text after delete ...
269 if( mEventData->mScrollAfterDelete )
271 ScrollTextToMatchCursor( cursorInfo );
274 // ... then, text can be scrolled to make the cursor visible.
275 if( mEventData->mScrollAfterUpdatePosition )
277 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
278 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
280 mEventData->mScrollAfterUpdatePosition = false;
281 mEventData->mScrollAfterDelete = false;
283 UpdateCursorPosition( cursorInfo );
285 mEventData->mDecoratorUpdated = true;
286 mEventData->mUpdateCursorPosition = false;
287 mEventData->mUpdateGrabHandlePosition = false;
291 CursorInfo leftHandleInfo;
292 CursorInfo rightHandleInfo;
294 if( mEventData->mUpdateHighlightBox )
296 GetCursorPosition( mEventData->mLeftSelectionPosition,
299 GetCursorPosition( mEventData->mRightSelectionPosition,
302 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
304 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
306 CursorInfo& infoLeft = leftHandleInfo;
308 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
309 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
311 CursorInfo& infoRight = rightHandleInfo;
313 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
314 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
318 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
320 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
321 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
326 if( mEventData->mUpdateLeftSelectionPosition )
328 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
332 mEventData->mDecoratorUpdated = true;
333 mEventData->mUpdateLeftSelectionPosition = false;
336 if( mEventData->mUpdateRightSelectionPosition )
338 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
342 mEventData->mDecoratorUpdated = true;
343 mEventData->mUpdateRightSelectionPosition = false;
346 if( mEventData->mUpdateHighlightBox )
348 RepositionSelectionHandles();
350 mEventData->mUpdateLeftSelectionPosition = false;
351 mEventData->mUpdateRightSelectionPosition = false;
352 mEventData->mUpdateHighlightBox = false;
353 mEventData->mIsLeftHandleSelected = false;
354 mEventData->mIsRightHandleSelected = false;
357 mEventData->mScrollAfterUpdatePosition = false;
360 if( mEventData->mUpdateInputStyle )
362 // Keep a copy of the current input style.
363 InputStyle currentInputStyle;
364 currentInputStyle.Copy( mEventData->mInputStyle );
366 // Set the default style first.
367 RetrieveDefaultInputStyle( mEventData->mInputStyle );
369 // Get the character index from the cursor index.
370 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
372 // Retrieve the style from the style runs stored in the logical model.
373 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
375 // Compare if the input style has changed.
376 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
378 if( hasInputStyleChanged )
380 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
381 // Queue the input style changed signal.
382 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
385 mEventData->mUpdateInputStyle = false;
388 mEventData->mEventQueue.clear();
390 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
392 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
393 mEventData->mDecoratorUpdated = false;
395 return decoratorUpdated;
398 void Controller::Impl::NotifyInputMethodContext()
400 if( mEventData && mEventData->mInputMethodContext )
402 CharacterIndex cursorPosition = GetLogicalCursorPosition();
404 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
406 // Update the cursor position by removing the initial white spaces.
407 if( cursorPosition < numberOfWhiteSpaces )
413 cursorPosition -= numberOfWhiteSpaces;
416 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
417 mEventData->mInputMethodContext.NotifyCursorPosition();
421 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
423 if ( mEventData && mEventData->mInputMethodContext )
425 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
426 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
430 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
432 CharacterIndex cursorPosition = 0u;
436 if( ( EventData::SELECTING == mEventData->mState ) ||
437 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
439 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
443 cursorPosition = mEventData->mPrimaryCursorPosition;
447 return cursorPosition;
450 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
452 Length numberOfWhiteSpaces = 0u;
454 // Get the buffer to the text.
455 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
457 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
458 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
460 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
466 return numberOfWhiteSpaces;
469 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
471 // Get the total number of characters.
472 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
474 // Retrieve the text.
475 if( 0u != numberOfCharacters )
477 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
481 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
483 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
484 mTextUpdateInfo.mStartGlyphIndex = 0u;
485 mTextUpdateInfo.mStartLineIndex = 0u;
486 numberOfCharacters = 0u;
488 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
489 if( 0u == numberOfParagraphs )
491 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
492 numberOfCharacters = 0u;
494 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
496 // Nothing else to do if there are no paragraphs.
500 // Find the paragraphs to be updated.
501 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
502 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
504 // Text is being added at the end of the current text.
505 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
507 // Text is being added in a new paragraph after the last character of the text.
508 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
509 numberOfCharacters = 0u;
510 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
512 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
513 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
515 // Nothing else to do;
519 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
523 Length numberOfCharactersToUpdate = 0u;
524 if( mTextUpdateInfo.mFullRelayoutNeeded )
526 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
530 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
532 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
533 numberOfCharactersToUpdate,
534 paragraphsToBeUpdated );
537 if( 0u != paragraphsToBeUpdated.Count() )
539 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
540 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
541 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
543 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
544 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
546 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
547 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
548 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
549 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
551 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
552 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
554 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
558 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
562 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
563 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
566 void Controller::Impl::ClearFullModelData( OperationsMask operations )
568 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
570 mModel->mLogicalModel->mLineBreakInfo.Clear();
571 mModel->mLogicalModel->mParagraphInfo.Clear();
574 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
576 mModel->mLogicalModel->mScriptRuns.Clear();
579 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
581 mModel->mLogicalModel->mFontRuns.Clear();
584 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
586 if( NO_OPERATION != ( BIDI_INFO & operations ) )
588 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
589 mModel->mLogicalModel->mCharacterDirections.Clear();
592 if( NO_OPERATION != ( REORDER & operations ) )
594 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
595 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
596 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
600 BidirectionalLineInfoRun& bidiLineInfo = *it;
602 free( bidiLineInfo.visualToLogicalMap );
603 bidiLineInfo.visualToLogicalMap = NULL;
605 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
609 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
611 mModel->mVisualModel->mGlyphs.Clear();
612 mModel->mVisualModel->mGlyphsToCharacters.Clear();
613 mModel->mVisualModel->mCharactersToGlyph.Clear();
614 mModel->mVisualModel->mCharactersPerGlyph.Clear();
615 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
616 mModel->mVisualModel->mGlyphPositions.Clear();
619 if( NO_OPERATION != ( LAYOUT & operations ) )
621 mModel->mVisualModel->mLines.Clear();
624 if( NO_OPERATION != ( COLOR & operations ) )
626 mModel->mVisualModel->mColorIndices.Clear();
627 mModel->mVisualModel->mBackgroundColorIndices.Clear();
631 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
633 const CharacterIndex endIndexPlusOne = endIndex + 1u;
635 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
637 // Clear the line break info.
638 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
640 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
641 lineBreakInfoBuffer + endIndexPlusOne );
643 // Clear the paragraphs.
644 ClearCharacterRuns( startIndex,
646 mModel->mLogicalModel->mParagraphInfo );
649 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
651 // Clear the scripts.
652 ClearCharacterRuns( startIndex,
654 mModel->mLogicalModel->mScriptRuns );
657 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
660 ClearCharacterRuns( startIndex,
662 mModel->mLogicalModel->mFontRuns );
665 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
667 if( NO_OPERATION != ( BIDI_INFO & operations ) )
669 // Clear the bidirectional paragraph info.
670 ClearCharacterRuns( startIndex,
672 mModel->mLogicalModel->mBidirectionalParagraphInfo );
674 // Clear the character's directions.
675 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
677 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
678 characterDirectionsBuffer + endIndexPlusOne );
681 if( NO_OPERATION != ( REORDER & operations ) )
683 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
684 uint32_t endRemoveIndex = startRemoveIndex;
685 ClearCharacterRuns( startIndex,
687 mModel->mLogicalModel->mBidirectionalLineInfo,
691 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
693 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
694 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
695 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
699 BidirectionalLineInfoRun& bidiLineInfo = *it;
701 free( bidiLineInfo.visualToLogicalMap );
702 bidiLineInfo.visualToLogicalMap = NULL;
705 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
706 bidirectionalLineInfoBuffer + endRemoveIndex );
711 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
713 const CharacterIndex endIndexPlusOne = endIndex + 1u;
714 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
716 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
717 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
718 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
720 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
721 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
723 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
725 // Update the character to glyph indices.
726 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
727 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
731 CharacterIndex& index = *it;
732 index -= numberOfGlyphsRemoved;
735 // Clear the character to glyph conversion table.
736 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
737 charactersToGlyphBuffer + endIndexPlusOne );
739 // Clear the glyphs per character table.
740 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
741 glyphsPerCharacterBuffer + endIndexPlusOne );
743 // Clear the glyphs buffer.
744 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
745 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
746 glyphsBuffer + endGlyphIndexPlusOne );
748 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
750 // Update the glyph to character indices.
751 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
752 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
756 CharacterIndex& index = *it;
757 index -= numberOfCharactersRemoved;
760 // Clear the glyphs to characters buffer.
761 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
762 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
764 // Clear the characters per glyph buffer.
765 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
766 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
767 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
769 // Clear the positions buffer.
770 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
771 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
772 positionsBuffer + endGlyphIndexPlusOne );
775 if( NO_OPERATION != ( LAYOUT & operations ) )
778 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
779 uint32_t endRemoveIndex = startRemoveIndex;
780 ClearCharacterRuns( startIndex,
782 mModel->mVisualModel->mLines,
786 // Will update the glyph runs.
787 startRemoveIndex = mModel->mVisualModel->mLines.Count();
788 endRemoveIndex = startRemoveIndex;
789 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
790 endGlyphIndexPlusOne - 1u,
791 mModel->mVisualModel->mLines,
795 // Set the line index from where to insert the new laid-out lines.
796 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
798 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
799 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
800 linesBuffer + endRemoveIndex );
803 if( NO_OPERATION != ( COLOR & operations ) )
805 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
807 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
808 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
809 colorIndexBuffer + endGlyphIndexPlusOne );
812 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
814 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
815 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
816 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
821 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
823 if( mTextUpdateInfo.mClearAll ||
824 ( ( 0u == startIndex ) &&
825 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
827 ClearFullModelData( operations );
831 // Clear the model data related with characters.
832 ClearCharacterModelData( startIndex, endIndex, operations );
834 // Clear the model data related with glyphs.
835 ClearGlyphModelData( startIndex, endIndex, operations );
838 // The estimated number of lines. Used to avoid reallocations when layouting.
839 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
841 mModel->mVisualModel->ClearCaches();
844 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
846 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
848 // Calculate the operations to be done.
849 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
851 if( NO_OPERATION == operations )
853 // Nothing to do if no operations are pending and required.
857 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
858 Vector<Character> displayCharacters;
859 bool useHiddenText = false;
860 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
862 mHiddenInput->Substitute( srcCharacters,displayCharacters );
863 useHiddenText = true;
866 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
867 const Length numberOfCharacters = utf32Characters.Count();
869 // Index to the first character of the first paragraph to be updated.
870 CharacterIndex startIndex = 0u;
871 // Number of characters of the paragraphs to be removed.
872 Length paragraphCharacters = 0u;
874 CalculateTextUpdateIndices( paragraphCharacters );
876 // Check whether the indices for updating the text is valid
877 if ( numberOfCharacters > 0u &&
878 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
879 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
881 std::string currentText;
882 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
884 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
885 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
887 // Dump mTextUpdateInfo
888 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
889 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
890 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
891 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
892 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
893 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
894 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
895 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
896 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
897 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
898 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
899 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
900 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
905 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
907 if( mTextUpdateInfo.mClearAll ||
908 ( 0u != paragraphCharacters ) )
910 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
913 mTextUpdateInfo.mClearAll = false;
915 // Whether the model is updated.
916 bool updated = false;
918 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
919 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
921 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
923 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
924 // calculate the bidirectional info for each 'paragraph'.
925 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
926 // is not shaped together).
927 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
929 SetLineBreakInfo( utf32Characters,
931 requestedNumberOfCharacters,
934 // Create the paragraph info.
935 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
936 requestedNumberOfCharacters );
940 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
941 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
943 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
944 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
946 if( getScripts || validateFonts )
948 // Validates the fonts assigned by the application or assigns default ones.
949 // It makes sure all the characters are going to be rendered by the correct font.
950 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
954 // Retrieves the scripts used in the text.
955 multilanguageSupport.SetScripts( utf32Characters,
957 requestedNumberOfCharacters,
963 // Validate the fonts set through the mark-up string.
964 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
966 // Get the default font's description.
967 TextAbstraction::FontDescription defaultFontDescription;
968 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
970 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
972 // If the placeholder font is set specifically, only placeholder font is changed.
973 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
974 if( mEventData->mPlaceholderFont->sizeDefined )
976 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
979 else if( NULL != mFontDefaults )
981 // Set the normal font and the placeholder font.
982 defaultFontDescription = mFontDefaults->mFontDescription;
984 if( mTextFitEnabled )
986 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
990 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
994 // Validates the fonts. If there is a character with no assigned font it sets a default one.
995 // After this call, fonts are validated.
996 multilanguageSupport.ValidateFonts( utf32Characters,
999 defaultFontDescription,
1002 requestedNumberOfCharacters,
1008 Vector<Character> mirroredUtf32Characters;
1009 bool textMirrored = false;
1010 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
1011 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1013 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1014 bidirectionalInfo.Reserve( numberOfParagraphs );
1016 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1017 SetBidirectionalInfo( utf32Characters,
1021 requestedNumberOfCharacters,
1023 mModel->mMatchSystemLanguageDirection,
1026 if( 0u != bidirectionalInfo.Count() )
1028 // Only set the character directions if there is right to left characters.
1029 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1030 GetCharactersDirection( bidirectionalInfo,
1033 requestedNumberOfCharacters,
1036 // This paragraph has right to left text. Some characters may need to be mirrored.
1037 // TODO: consider if the mirrored string can be stored as well.
1039 textMirrored = GetMirroredText( utf32Characters,
1043 requestedNumberOfCharacters,
1044 mirroredUtf32Characters );
1048 // There is no right to left characters. Clear the directions vector.
1049 mModel->mLogicalModel->mCharacterDirections.Clear();
1054 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1055 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1056 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1057 Vector<GlyphIndex> newParagraphGlyphs;
1058 newParagraphGlyphs.Reserve( numberOfParagraphs );
1060 const Length currentNumberOfGlyphs = glyphs.Count();
1061 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1063 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1065 ShapeText( textToShape,
1070 mTextUpdateInfo.mStartGlyphIndex,
1071 requestedNumberOfCharacters,
1073 glyphsToCharactersMap,
1075 newParagraphGlyphs );
1077 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1078 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1079 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1083 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1085 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1087 GlyphInfo* glyphsBuffer = glyphs.Begin();
1088 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1090 // Update the width and advance of all new paragraph characters.
1091 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1093 const GlyphIndex index = *it;
1094 GlyphInfo& glyph = *( glyphsBuffer + index );
1096 glyph.xBearing = 0.f;
1098 glyph.advance = 0.f;
1103 if( ( NULL != mEventData ) &&
1104 mEventData->mPreEditFlag &&
1105 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1107 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1108 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1109 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1111 // Check the type of preedit and run it.
1112 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1114 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1115 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1116 type = attrData.preeditType;
1118 // Check the number of commit characters for the start position.
1119 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1120 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1124 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1126 // Add the underline for the pre-edit text.
1127 GlyphRun underlineRun;
1128 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1129 underlineRun.numberOfGlyphs = numberOfIndices;
1130 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1133 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1135 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1136 ColorRun backgroundColorRun;
1137 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1138 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1139 backgroundColorRun.color = textColor;
1140 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1142 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1143 if(backgroundColor.a == 0) // There is no text background color.
1145 // Try use the control's background color.
1146 if(nullptr != mEditableControlInterface)
1148 mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
1149 if(backgroundColor.a == 0) // There is no control background color.
1151 // Determines black or white color according to text color.
1152 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
1153 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
1154 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
1159 Vector<ColorRun> colorRuns;
1160 colorRuns.Resize(1u);
1161 ColorRun& colorRun = *(colorRuns.Begin());
1162 colorRun.color = backgroundColor;
1163 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1164 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1166 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1169 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1171 ColorRun backgroundColorRun;
1172 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1173 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1174 backgroundColorRun.color = LIGHT_BLUE;
1175 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1178 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1180 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1181 ColorRun backgroundColorRun;
1182 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1183 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1184 backgroundColorRun.color = BACKGROUND_SUB4;
1185 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1187 GlyphRun underlineRun;
1188 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1189 underlineRun.numberOfGlyphs = numberOfIndices;
1190 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1193 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1195 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1196 ColorRun backgroundColorRun;
1197 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1198 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1199 backgroundColorRun.color = BACKGROUND_SUB5;
1200 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1202 GlyphRun underlineRun;
1203 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1204 underlineRun.numberOfGlyphs = numberOfIndices;
1205 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1208 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1210 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1211 ColorRun backgroundColorRun;
1212 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1213 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1214 backgroundColorRun.color = BACKGROUND_SUB6;
1215 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1217 GlyphRun underlineRun;
1218 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1219 underlineRun.numberOfGlyphs = numberOfIndices;
1220 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1223 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1225 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1226 ColorRun backgroundColorRun;
1227 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1228 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1229 backgroundColorRun.color = BACKGROUND_SUB7;
1230 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1232 GlyphRun underlineRun;
1233 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1234 underlineRun.numberOfGlyphs = numberOfIndices;
1235 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1238 case Dali::InputMethodContext::PreeditStyle::NONE:
1249 if( NO_OPERATION != ( COLOR & operations ) )
1251 // Set the color runs in glyphs.
1252 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1253 mModel->mVisualModel->mCharactersToGlyph,
1254 mModel->mVisualModel->mGlyphsPerCharacter,
1256 mTextUpdateInfo.mStartGlyphIndex,
1257 requestedNumberOfCharacters,
1258 mModel->mVisualModel->mColors,
1259 mModel->mVisualModel->mColorIndices );
1261 // Set the background color runs in glyphs.
1262 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1263 mModel->mVisualModel->mCharactersToGlyph,
1264 mModel->mVisualModel->mGlyphsPerCharacter,
1266 mTextUpdateInfo.mStartGlyphIndex,
1267 requestedNumberOfCharacters,
1268 mModel->mVisualModel->mBackgroundColors,
1269 mModel->mVisualModel->mBackgroundColorIndices );
1275 // The estimated number of lines. Used to avoid reallocations when layouting.
1276 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1278 // Set the previous number of characters for the next time the text is updated.
1279 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1284 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1286 // Sets the default text's color.
1287 inputStyle.textColor = mTextColor;
1288 inputStyle.isDefaultColor = true;
1290 inputStyle.familyName.clear();
1291 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1292 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1293 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1294 inputStyle.size = 0.f;
1296 inputStyle.lineSpacing = 0.f;
1298 inputStyle.underlineProperties.clear();
1299 inputStyle.shadowProperties.clear();
1300 inputStyle.embossProperties.clear();
1301 inputStyle.outlineProperties.clear();
1303 inputStyle.isFamilyDefined = false;
1304 inputStyle.isWeightDefined = false;
1305 inputStyle.isWidthDefined = false;
1306 inputStyle.isSlantDefined = false;
1307 inputStyle.isSizeDefined = false;
1309 inputStyle.isLineSpacingDefined = false;
1311 inputStyle.isUnderlineDefined = false;
1312 inputStyle.isShadowDefined = false;
1313 inputStyle.isEmbossDefined = false;
1314 inputStyle.isOutlineDefined = false;
1316 // Sets the default font's family name, weight, width, slant and size.
1319 if( mFontDefaults->familyDefined )
1321 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1322 inputStyle.isFamilyDefined = true;
1325 if( mFontDefaults->weightDefined )
1327 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1328 inputStyle.isWeightDefined = true;
1331 if( mFontDefaults->widthDefined )
1333 inputStyle.width = mFontDefaults->mFontDescription.width;
1334 inputStyle.isWidthDefined = true;
1337 if( mFontDefaults->slantDefined )
1339 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1340 inputStyle.isSlantDefined = true;
1343 if( mFontDefaults->sizeDefined )
1345 inputStyle.size = mFontDefaults->mDefaultPointSize;
1346 inputStyle.isSizeDefined = true;
1351 float Controller::Impl::GetDefaultFontLineHeight()
1353 FontId defaultFontId = 0u;
1354 if( NULL == mFontDefaults )
1356 TextAbstraction::FontDescription fontDescription;
1357 defaultFontId = mFontClient.GetFontId( fontDescription );
1361 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1364 Text::FontMetrics fontMetrics;
1365 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1367 return( fontMetrics.ascender - fontMetrics.descender );
1370 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1372 ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
1375 void Controller::Impl::OnTapEvent( const Event& event )
1377 ControllerImplEventHandler::OnTapEvent(*this, event);
1380 void Controller::Impl::OnPanEvent( const Event& event )
1382 ControllerImplEventHandler::OnPanEvent(*this, event);
1385 void Controller::Impl::OnLongPressEvent( const Event& event )
1387 ControllerImplEventHandler::OnLongPressEvent(*this, event);
1390 void Controller::Impl::OnHandleEvent( const Event& event )
1392 ControllerImplEventHandler::OnHandleEvent(*this, event);
1395 void Controller::Impl::OnSelectEvent( const Event& event )
1397 ControllerImplEventHandler::OnSelectEvent(*this, event);
1400 void Controller::Impl::OnSelectAllEvent()
1402 ControllerImplEventHandler::OnSelectAllEvent(*this);
1405 void Controller::Impl::OnSelectNoneEvent()
1407 ControllerImplEventHandler::OnSelectNoneEvent(*this);
1410 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
1412 if( nullptr == mEventData )
1414 // Nothing to do if there is no text.
1418 if( mEventData->mSelectionEnabled && (pStart || pEnd))
1420 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1424 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1428 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1431 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1433 ChangeState( EventData::EDITING );
1434 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1435 mEventData->mUpdateCursorPosition = true;
1439 ChangeState( EventData::SELECTING );
1440 mEventData->mUpdateHighlightBox = true;
1441 mEventData->mUpdateLeftSelectionPosition = true;
1442 mEventData->mUpdateRightSelectionPosition = true;
1447 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1453 range.first = mEventData->mLeftSelectionPosition;
1454 range.second = mEventData->mRightSelectionPosition;
1460 bool Controller::Impl::IsEditable() const
1462 return mEventData && mEventData->mEditingEnabled;
1465 void Controller::Impl::SetEditable( bool editable )
1469 mEventData->mEditingEnabled = editable;
1473 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1475 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1477 // Nothing to select if handles are in the same place.
1478 selectedText.clear();
1482 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1484 //Get start and end position of selection
1485 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1486 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1488 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1489 const Length numberOfCharacters = utf32Characters.Count();
1491 // Validate the start and end selection points
1492 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1494 //Get text as a UTF8 string
1495 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1497 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1499 // Keep a copy of the current input style.
1500 InputStyle currentInputStyle;
1501 currentInputStyle.Copy( mEventData->mInputStyle );
1503 // Set as input style the style of the first deleted character.
1504 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1506 // Compare if the input style has changed.
1507 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1509 if( hasInputStyleChanged )
1511 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1512 // Queue the input style changed signal.
1513 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1516 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1518 // Mark the paragraphs to be updated.
1519 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1521 mTextUpdateInfo.mCharacterIndex = 0;
1522 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1523 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1524 mTextUpdateInfo.mClearAll = true;
1528 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1529 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1532 // Delete text between handles
1533 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1534 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1535 utf32Characters.Erase( first, last );
1537 // Will show the cursor at the first character of the selection.
1538 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1542 // Will show the cursor at the last character of the selection.
1543 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1546 mEventData->mDecoratorUpdated = true;
1550 void Controller::Impl::ShowClipboard()
1554 mClipboard.ShowClipboard();
1558 void Controller::Impl::HideClipboard()
1560 if( mClipboard && mClipboardHideEnabled )
1562 mClipboard.HideClipboard();
1566 void Controller::Impl::SetClipboardHideEnable(bool enable)
1568 mClipboardHideEnabled = enable;
1571 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1573 //Send string to clipboard
1574 return ( mClipboard && mClipboard.SetItem( source ) );
1577 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1579 std::string selectedText;
1580 RetrieveSelection( selectedText, deleteAfterSending );
1581 CopyStringToClipboard( selectedText );
1582 ChangeState( EventData::EDITING );
1585 void Controller::Impl::RequestGetTextFromClipboard()
1589 mClipboard.RequestItem();
1593 void Controller::Impl::RepositionSelectionHandles()
1595 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1596 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1598 if( selectionStart == selectionEnd )
1600 // Nothing to select if handles are in the same place.
1601 // So, deactive Highlight box.
1602 mEventData->mDecorator->SetHighlightActive( false );
1606 mEventData->mDecorator->ClearHighlights();
1608 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1609 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1610 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1611 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1612 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1613 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1614 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1616 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1617 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1618 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1620 // Swap the indices if the start is greater than the end.
1621 const bool indicesSwapped = selectionStart > selectionEnd;
1623 // Tell the decorator to flip the selection handles if needed.
1624 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1626 if( indicesSwapped )
1628 std::swap( selectionStart, selectionEnd );
1631 // Get the indices to the first and last selected glyphs.
1632 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1633 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1634 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1635 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1637 // Get the lines where the glyphs are laid-out.
1638 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1640 LineIndex lineIndex = 0u;
1641 Length numberOfLines = 0u;
1642 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1643 1u + glyphEnd - glyphStart,
1646 const LineIndex firstLineIndex = lineIndex;
1648 // Create the structure to store some selection box info.
1649 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1650 selectionBoxLinesInfo.Resize( numberOfLines );
1652 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1653 selectionBoxInfo->minX = MAX_FLOAT;
1654 selectionBoxInfo->maxX = MIN_FLOAT;
1656 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1657 float minHighlightX = std::numeric_limits<float>::max();
1658 float maxHighlightX = std::numeric_limits<float>::min();
1660 Vector2 highLightPosition; // The highlight position in decorator's coords.
1662 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1664 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1665 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1668 // Transform to decorator's (control) coords.
1669 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1671 lineRun += firstLineIndex;
1673 // The line height is the addition of the line ascender and the line descender.
1674 // However, the line descender has a negative value, hence the subtraction.
1675 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1677 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1679 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1680 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1681 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1683 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1684 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1685 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1687 // The number of quads of the selection box.
1688 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1689 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1691 // Count the actual number of quads.
1692 unsigned int actualNumberOfQuads = 0u;
1695 // Traverse the glyphs.
1696 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1698 const GlyphInfo& glyph = *( glyphsBuffer + index );
1699 const Vector2& position = *( positionsBuffer + index );
1701 if( splitStartGlyph )
1703 // 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.
1705 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1706 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1707 // Get the direction of the character.
1708 CharacterDirection isCurrentRightToLeft = false;
1709 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1711 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1714 // The end point could be in the middle of the ligature.
1715 // Calculate the number of characters selected.
1716 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1718 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1719 quad.y = selectionBoxInfo->lineOffset;
1720 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1721 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1723 // Store the min and max 'x' for each line.
1724 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1725 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1727 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1728 ++actualNumberOfQuads;
1730 splitStartGlyph = false;
1734 if( splitEndGlyph && ( index == glyphEnd ) )
1736 // 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.
1738 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1739 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1740 // Get the direction of the character.
1741 CharacterDirection isCurrentRightToLeft = false;
1742 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1744 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1747 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1749 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1750 quad.y = selectionBoxInfo->lineOffset;
1751 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1752 quad.w = quad.y + selectionBoxInfo->lineHeight;
1754 // Store the min and max 'x' for each line.
1755 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1756 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1758 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1760 ++actualNumberOfQuads;
1762 splitEndGlyph = false;
1766 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1767 quad.y = selectionBoxInfo->lineOffset;
1768 quad.z = quad.x + glyph.advance;
1769 quad.w = quad.y + selectionBoxInfo->lineHeight;
1771 // Store the min and max 'x' for each line.
1772 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1773 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1775 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1777 ++actualNumberOfQuads;
1779 // Whether to retrieve the next line.
1780 if( index == lastGlyphOfLine )
1783 if( lineIndex < firstLineIndex + numberOfLines )
1785 // Retrieve the next line.
1788 // Get the last glyph of the new line.
1789 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1791 // Keep the offset and height of the current selection box.
1792 const float currentLineOffset = selectionBoxInfo->lineOffset;
1793 const float currentLineHeight = selectionBoxInfo->lineHeight;
1795 // Get the selection box info for the next line.
1798 selectionBoxInfo->minX = MAX_FLOAT;
1799 selectionBoxInfo->maxX = MIN_FLOAT;
1801 // Update the line's vertical offset.
1802 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1804 // The line height is the addition of the line ascender and the line descender.
1805 // However, the line descender has a negative value, hence the subtraction.
1806 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1811 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1812 // The final width is calculated after 'boxifying' the selection.
1813 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1814 endIt = selectionBoxLinesInfo.End();
1818 const SelectionBoxInfo& info = *it;
1820 // Update the size of the highlighted text.
1821 highLightSize.height += info.lineHeight;
1822 minHighlightX = std::min( minHighlightX, info.minX );
1823 maxHighlightX = std::max( maxHighlightX, info.maxX );
1826 // Add extra geometry to 'boxify' the selection.
1828 if( 1u < numberOfLines )
1830 // Boxify the first line.
1831 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
1832 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1834 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1835 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1840 quad.y = firstSelectionBoxLineInfo.lineOffset;
1841 quad.z = firstSelectionBoxLineInfo.minX;
1842 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1844 // Boxify at the beginning of the line.
1845 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1847 ++actualNumberOfQuads;
1849 // Update the size of the highlighted text.
1850 minHighlightX = 0.f;
1855 quad.x = firstSelectionBoxLineInfo.maxX;
1856 quad.y = firstSelectionBoxLineInfo.lineOffset;
1857 quad.z = mModel->mVisualModel->mControlSize.width;
1858 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1860 // Boxify at the end of the line.
1861 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1863 ++actualNumberOfQuads;
1865 // Update the size of the highlighted text.
1866 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1869 // Boxify the central lines.
1870 if( 2u < numberOfLines )
1872 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1873 endIt = selectionBoxLinesInfo.End() - 1u;
1877 const SelectionBoxInfo& info = *it;
1880 quad.y = info.lineOffset;
1882 quad.w = info.lineOffset + info.lineHeight;
1884 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1886 ++actualNumberOfQuads;
1889 quad.y = info.lineOffset;
1890 quad.z = mModel->mVisualModel->mControlSize.width;
1891 quad.w = info.lineOffset + info.lineHeight;
1893 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1895 ++actualNumberOfQuads;
1898 // Update the size of the highlighted text.
1899 minHighlightX = 0.f;
1900 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1903 // Boxify the last line.
1904 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1905 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1907 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1908 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1913 quad.y = lastSelectionBoxLineInfo.lineOffset;
1914 quad.z = lastSelectionBoxLineInfo.minX;
1915 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1917 // Boxify at the beginning of the line.
1918 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1920 ++actualNumberOfQuads;
1922 // Update the size of the highlighted text.
1923 minHighlightX = 0.f;
1928 quad.x = lastSelectionBoxLineInfo.maxX;
1929 quad.y = lastSelectionBoxLineInfo.lineOffset;
1930 quad.z = mModel->mVisualModel->mControlSize.width;
1931 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1933 // Boxify at the end of the line.
1934 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1936 ++actualNumberOfQuads;
1938 // Update the size of the highlighted text.
1939 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1943 // Set the actual number of quads.
1944 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
1946 // Sets the highlight's size and position. In decorator's coords.
1947 // The highlight's height has been calculated above (before 'boxifying' the highlight).
1948 highLightSize.width = maxHighlightX - minHighlightX;
1950 highLightPosition.x = minHighlightX;
1951 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1952 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
1954 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
1956 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1958 CursorInfo primaryCursorInfo;
1959 GetCursorPosition( mEventData->mLeftSelectionPosition,
1960 primaryCursorInfo );
1962 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1964 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1966 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1967 primaryCursorInfo.lineHeight );
1969 CursorInfo secondaryCursorInfo;
1970 GetCursorPosition( mEventData->mRightSelectionPosition,
1971 secondaryCursorInfo );
1973 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1975 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1976 secondaryPosition.x,
1977 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1978 secondaryCursorInfo.lineHeight );
1981 // Set the flag to update the decorator.
1982 mEventData->mDecoratorUpdated = true;
1985 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
1987 if( NULL == mEventData )
1989 // Nothing to do if there is no text input.
1993 if( IsShowingPlaceholderText() )
1995 // Nothing to do if there is the place-holder text.
1999 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2000 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2001 if( ( 0 == numberOfGlyphs ) ||
2002 ( 0 == numberOfLines ) )
2004 // Nothing to do if there is no text.
2008 // Find which word was selected
2009 CharacterIndex selectionStart( 0 );
2010 CharacterIndex selectionEnd( 0 );
2011 CharacterIndex noTextHitIndex( 0 );
2012 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2013 mModel->mLogicalModel,
2020 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2022 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2024 ChangeState( EventData::SELECTING );
2026 mEventData->mLeftSelectionPosition = selectionStart;
2027 mEventData->mRightSelectionPosition = selectionEnd;
2029 mEventData->mUpdateLeftSelectionPosition = true;
2030 mEventData->mUpdateRightSelectionPosition = true;
2031 mEventData->mUpdateHighlightBox = true;
2033 // It may happen an InputMethodContext commit event arrives before the selection event
2034 // if the InputMethodContext is in pre-edit state. The commit event will set the
2035 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2036 // to false, the highlight box won't be updated.
2037 mEventData->mUpdateCursorPosition = false;
2039 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2041 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2042 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2044 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2046 // Nothing to select. i.e. a white space, out of bounds
2047 ChangeState( EventData::EDITING_WITH_POPUP );
2049 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2051 mEventData->mUpdateCursorPosition = true;
2052 mEventData->mUpdateGrabHandlePosition = true;
2053 mEventData->mScrollAfterUpdatePosition = true;
2054 mEventData->mUpdateInputStyle = true;
2056 else if( Controller::NoTextTap::NO_ACTION == action )
2058 // Nothing to select. i.e. a white space, out of bounds
2059 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2061 mEventData->mUpdateCursorPosition = true;
2062 mEventData->mUpdateGrabHandlePosition = true;
2063 mEventData->mScrollAfterUpdatePosition = true;
2064 mEventData->mUpdateInputStyle = true;
2068 void Controller::Impl::SetPopupButtons()
2071 * Sets the Popup buttons to be shown depending on State.
2073 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2075 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2078 bool isEditable = IsEditable();
2079 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2081 if( EventData::SELECTING == mEventData->mState )
2083 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2086 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2089 if( !IsClipboardEmpty() )
2093 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2095 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2098 if( !mEventData->mAllTextSelected )
2100 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2103 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2105 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2107 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2110 if( !IsClipboardEmpty() )
2114 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2116 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2119 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2121 if ( !IsClipboardEmpty() )
2125 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2127 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2131 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2134 void Controller::Impl::ChangeState( EventData::State newState )
2136 if( NULL == mEventData )
2138 // Nothing to do if there is no text input.
2142 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2144 if( mEventData->mState != newState )
2146 mEventData->mPreviousState = mEventData->mState;
2147 mEventData->mState = newState;
2149 switch( mEventData->mState )
2151 case EventData::INACTIVE:
2153 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2154 mEventData->mDecorator->StopCursorBlink();
2155 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2156 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2157 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2158 mEventData->mDecorator->SetHighlightActive( false );
2159 mEventData->mDecorator->SetPopupActive( false );
2160 mEventData->mDecoratorUpdated = true;
2163 case EventData::INTERRUPTED:
2165 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2166 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2167 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2168 mEventData->mDecorator->SetHighlightActive( false );
2169 mEventData->mDecorator->SetPopupActive( false );
2170 mEventData->mDecoratorUpdated = true;
2173 case EventData::SELECTING:
2175 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2176 mEventData->mDecorator->StopCursorBlink();
2177 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2178 if ( mEventData->mGrabHandleEnabled )
2180 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2181 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2183 mEventData->mDecorator->SetHighlightActive( true );
2184 if( mEventData->mGrabHandlePopupEnabled )
2187 mEventData->mDecorator->SetPopupActive( true );
2189 mEventData->mDecoratorUpdated = true;
2192 case EventData::EDITING:
2194 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2195 if( mEventData->mCursorBlinkEnabled )
2197 mEventData->mDecorator->StartCursorBlink();
2199 // Grab handle is not shown until a tap is received whilst EDITING
2200 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2201 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2202 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2203 mEventData->mDecorator->SetHighlightActive( false );
2204 if( mEventData->mGrabHandlePopupEnabled )
2206 mEventData->mDecorator->SetPopupActive( false );
2208 mEventData->mDecoratorUpdated = true;
2211 case EventData::EDITING_WITH_POPUP:
2213 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2215 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2216 if( mEventData->mCursorBlinkEnabled )
2218 mEventData->mDecorator->StartCursorBlink();
2220 if( mEventData->mSelectionEnabled )
2222 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2223 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2224 mEventData->mDecorator->SetHighlightActive( false );
2226 else if ( mEventData->mGrabHandleEnabled )
2228 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2230 if( mEventData->mGrabHandlePopupEnabled )
2233 mEventData->mDecorator->SetPopupActive( true );
2235 mEventData->mDecoratorUpdated = true;
2238 case EventData::EDITING_WITH_GRAB_HANDLE:
2240 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2242 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2243 if( mEventData->mCursorBlinkEnabled )
2245 mEventData->mDecorator->StartCursorBlink();
2247 // Grab handle is not shown until a tap is received whilst EDITING
2248 if ( mEventData->mGrabHandleEnabled )
2250 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2252 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2253 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2254 mEventData->mDecorator->SetHighlightActive( false );
2255 if( mEventData->mGrabHandlePopupEnabled )
2257 mEventData->mDecorator->SetPopupActive( false );
2259 mEventData->mDecoratorUpdated = true;
2262 case EventData::SELECTION_HANDLE_PANNING:
2264 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2265 mEventData->mDecorator->StopCursorBlink();
2266 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2267 if ( mEventData->mGrabHandleEnabled )
2269 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2270 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2272 mEventData->mDecorator->SetHighlightActive( true );
2273 if( mEventData->mGrabHandlePopupEnabled )
2275 mEventData->mDecorator->SetPopupActive( false );
2277 mEventData->mDecoratorUpdated = true;
2280 case EventData::GRAB_HANDLE_PANNING:
2282 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2284 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2285 if( mEventData->mCursorBlinkEnabled )
2287 mEventData->mDecorator->StartCursorBlink();
2289 if ( mEventData->mGrabHandleEnabled )
2291 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2293 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2294 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2295 mEventData->mDecorator->SetHighlightActive( false );
2296 if( mEventData->mGrabHandlePopupEnabled )
2298 mEventData->mDecorator->SetPopupActive( false );
2300 mEventData->mDecoratorUpdated = true;
2303 case EventData::EDITING_WITH_PASTE_POPUP:
2305 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2307 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2308 if( mEventData->mCursorBlinkEnabled )
2310 mEventData->mDecorator->StartCursorBlink();
2313 if ( mEventData->mGrabHandleEnabled )
2315 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2317 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2318 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2319 mEventData->mDecorator->SetHighlightActive( false );
2321 if( mEventData->mGrabHandlePopupEnabled )
2324 mEventData->mDecorator->SetPopupActive( true );
2326 mEventData->mDecoratorUpdated = true;
2329 case EventData::TEXT_PANNING:
2331 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2332 mEventData->mDecorator->StopCursorBlink();
2333 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2334 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2335 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2337 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2338 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2339 mEventData->mDecorator->SetHighlightActive( true );
2342 if( mEventData->mGrabHandlePopupEnabled )
2344 mEventData->mDecorator->SetPopupActive( false );
2347 mEventData->mDecoratorUpdated = true;
2354 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2355 CursorInfo& cursorInfo )
2357 if( !IsShowingRealText() )
2359 // Do not want to use the place-holder text to set the cursor position.
2361 // Use the line's height of the font's family set to set the cursor's size.
2362 // If there is no font's family set, use the default font.
2363 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2365 cursorInfo.lineOffset = 0.f;
2366 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2367 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2370 if( mModel->mMatchSystemLanguageDirection )
2372 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2375 switch( mModel->mHorizontalAlignment )
2377 case Text::HorizontalAlignment::BEGIN :
2381 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2385 cursorInfo.primaryPosition.x = 0.f;
2389 case Text::HorizontalAlignment::CENTER:
2391 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2394 case Text::HorizontalAlignment::END:
2398 cursorInfo.primaryPosition.x = 0.f;
2402 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2408 // Nothing else to do.
2412 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2413 GetCursorPositionParameters parameters;
2414 parameters.visualModel = mModel->mVisualModel;
2415 parameters.logicalModel = mModel->mLogicalModel;
2416 parameters.metrics = mMetrics;
2417 parameters.logical = logical;
2418 parameters.isMultiline = isMultiLine;
2420 Text::GetCursorPosition( parameters,
2423 // Adds Outline offset.
2424 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2425 cursorInfo.primaryPosition.x += outlineWidth;
2426 cursorInfo.primaryPosition.y += outlineWidth;
2427 cursorInfo.secondaryPosition.x += outlineWidth;
2428 cursorInfo.secondaryPosition.y += outlineWidth;
2432 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2434 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2435 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2437 if( 0.f > cursorInfo.primaryPosition.x )
2439 cursorInfo.primaryPosition.x = 0.f;
2442 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2443 if( cursorInfo.primaryPosition.x > edgeWidth )
2445 cursorInfo.primaryPosition.x = edgeWidth;
2450 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2452 if( NULL == mEventData )
2454 // Nothing to do if there is no text input.
2458 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2460 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2461 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2463 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2464 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2466 if( numberOfCharacters > 1u )
2468 const Script script = mModel->mLogicalModel->GetScript( index );
2469 if( HasLigatureMustBreak( script ) )
2471 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2472 numberOfCharacters = 1u;
2477 while( 0u == numberOfCharacters )
2480 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2484 if( index < mEventData->mPrimaryCursorPosition )
2486 cursorIndex -= numberOfCharacters;
2490 cursorIndex += numberOfCharacters;
2493 // Will update the cursor hook position.
2494 mEventData->mUpdateCursorHookPosition = true;
2499 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2501 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2502 if( NULL == mEventData )
2504 // Nothing to do if there is no text input.
2505 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2509 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2511 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2513 // Sets the cursor position.
2514 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2517 cursorInfo.primaryCursorHeight,
2518 cursorInfo.lineHeight );
2519 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2521 if( mEventData->mUpdateGrabHandlePosition )
2523 // Sets the grab handle position.
2524 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2526 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2527 cursorInfo.lineHeight );
2530 if( cursorInfo.isSecondaryCursor )
2532 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2533 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2534 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2535 cursorInfo.secondaryCursorHeight,
2536 cursorInfo.lineHeight );
2537 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2540 // Set which cursors are active according the state.
2541 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2543 if( cursorInfo.isSecondaryCursor )
2545 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2549 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2554 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2557 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2560 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2561 const CursorInfo& cursorInfo )
2563 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2564 ( RIGHT_SELECTION_HANDLE != handleType ) )
2569 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2571 // Sets the handle's position.
2572 mEventData->mDecorator->SetPosition( handleType,
2574 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2575 cursorInfo.lineHeight );
2577 // If selection handle at start of the text and other at end of the text then all text is selected.
2578 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2579 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2580 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2583 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2585 // Clamp between -space & -alignment offset.
2587 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2589 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2590 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2591 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2593 mEventData->mDecoratorUpdated = true;
2597 mModel->mScrollPosition.x = 0.f;
2601 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2603 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2605 // Nothing to do if the text is single line.
2609 // Clamp between -space & 0.
2610 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2612 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2613 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2614 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2616 mEventData->mDecoratorUpdated = true;
2620 mModel->mScrollPosition.y = 0.f;
2624 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2626 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2628 // position is in actor's coords.
2629 const float positionEndX = position.x + cursorWidth;
2630 const float positionEndY = position.y + lineHeight;
2632 // Transform the position to decorator coords.
2633 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2634 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2636 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2637 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2639 if( decoratorPositionBeginX < 0.f )
2641 mModel->mScrollPosition.x = -position.x;
2643 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2645 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2648 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2650 if( decoratorPositionBeginY < 0.f )
2652 mModel->mScrollPosition.y = -position.y;
2654 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2656 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2661 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2663 // Get the current cursor position in decorator coords.
2664 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2666 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2670 // Calculate the offset to match the cursor position before the character was deleted.
2671 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2673 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2674 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2676 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2677 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2681 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2682 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2684 // Makes the new cursor position visible if needed.
2685 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2688 void Controller::Impl::RequestRelayout()
2690 if( NULL != mControlInterface )
2692 mControlInterface->RequestTextRelayout();
2696 Actor Controller::Impl::CreateBackgroundActor()
2698 // NOTE: Currently we only support background color for one line left-to-right text,
2699 // so the following calculation is based on one line left-to-right text only!
2703 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2704 if( numberOfGlyphs > 0u )
2706 Vector<GlyphInfo> glyphs;
2707 glyphs.Resize( numberOfGlyphs );
2709 Vector<Vector2> positions;
2710 positions.Resize( numberOfGlyphs );
2712 // Get the line where the glyphs are laid-out.
2713 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2714 float alignmentOffset = lineRun->alignmentOffset;
2715 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
2721 glyphs.Resize( numberOfGlyphs );
2722 positions.Resize( numberOfGlyphs );
2724 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2725 const Vector2* const positionsBuffer = positions.Begin();
2727 BackgroundMesh mesh;
2728 mesh.mVertices.Reserve( 4u * glyphs.Size() );
2729 mesh.mIndices.Reserve( 6u * glyphs.Size() );
2731 const Vector2 textSize = mView.GetLayoutSize();
2733 const float offsetX = alignmentOffset + textSize.width * 0.5f;
2734 const float offsetY = textSize.height * 0.5f;
2736 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2737 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2738 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2741 uint32_t numberOfQuads = 0u;
2742 Length yLineOffset = 0;
2743 Length prevLineIndex = 0;
2744 LineIndex lineIndex;
2745 Length numberOfLines;
2747 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
2749 const GlyphInfo& glyph = *( glyphsBuffer + i );
2751 // Get the background color of the character.
2752 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2753 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
2754 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
2756 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2757 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2759 if(lineIndex != prevLineIndex)
2761 yLineOffset += lineHeight;
2764 // Only create quads for glyphs with a background color
2765 if(backgroundColor != Color::TRANSPARENT)
2767 const Vector2 position = *(positionsBuffer + i);
2769 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2771 quad.x = position.x;
2772 quad.y = yLineOffset;
2773 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2774 quad.w = lineHeight;
2776 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2778 quad.x = position.x;
2779 quad.y = yLineOffset;
2780 quad.z = quad.x - glyph.xBearing + glyph.advance;
2781 quad.w = quad.y + lineHeight;
2783 else if(i == glyphSize - 1u) // The last glyph in the whole text
2785 quad.x = position.x - glyph.xBearing;
2786 quad.y = yLineOffset;
2787 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2788 quad.w = quad.y + lineHeight;
2790 else // The glyph in the middle of the text
2792 quad.x = position.x - glyph.xBearing;
2793 quad.y = yLineOffset;
2794 quad.z = quad.x + glyph.advance;
2795 quad.w = quad.y + lineHeight;
2798 BackgroundVertex vertex;
2801 vertex.mPosition.x = quad.x - offsetX;
2802 vertex.mPosition.y = quad.y - offsetY;
2803 vertex.mColor = backgroundColor;
2804 mesh.mVertices.PushBack(vertex);
2807 vertex.mPosition.x = quad.z - offsetX;
2808 vertex.mPosition.y = quad.y - offsetY;
2809 vertex.mColor = backgroundColor;
2810 mesh.mVertices.PushBack(vertex);
2813 vertex.mPosition.x = quad.x - offsetX;
2814 vertex.mPosition.y = quad.w - offsetY;
2815 vertex.mColor = backgroundColor;
2816 mesh.mVertices.PushBack(vertex);
2819 vertex.mPosition.x = quad.z - offsetX;
2820 vertex.mPosition.y = quad.w - offsetY;
2821 vertex.mColor = backgroundColor;
2822 mesh.mVertices.PushBack(vertex);
2824 // Six indices in counter clockwise winding
2825 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2826 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2827 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2828 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2829 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2830 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2835 if(lineIndex != prevLineIndex)
2837 prevLineIndex = lineIndex;
2841 // Only create the background actor if there are glyphs with background color
2842 if ( mesh.mVertices.Count() > 0u )
2844 Property::Map quadVertexFormat;
2845 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2846 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2848 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2849 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2851 Geometry quadGeometry = Geometry::New();
2852 quadGeometry.AddVertexBuffer( quadVertices );
2853 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2855 if( !mShaderBackground )
2857 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
2860 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2861 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2862 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2864 actor = Actor::New();
2865 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2866 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2867 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2868 actor.SetProperty( Actor::Property::SIZE, textSize );
2869 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2870 actor.AddRenderer( renderer );
2879 } // namespace Toolkit