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 mCursorHookPositionX( 0.f ),
145 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
146 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
147 mIsShowingPlaceholderText( false ),
148 mPreEditFlag( false ),
149 mDecoratorUpdated( false ),
150 mCursorBlinkEnabled( true ),
151 mGrabHandleEnabled( true ),
152 mGrabHandlePopupEnabled( true ),
153 mSelectionEnabled( true ),
154 mUpdateCursorHookPosition( false ),
155 mUpdateCursorPosition( false ),
156 mUpdateGrabHandlePosition( false ),
157 mUpdateLeftSelectionPosition( false ),
158 mUpdateRightSelectionPosition( false ),
159 mIsLeftHandleSelected( false ),
160 mIsRightHandleSelected( false ),
161 mUpdateHighlightBox( false ),
162 mScrollAfterUpdatePosition( false ),
163 mScrollAfterDelete( false ),
164 mAllTextSelected( false ),
165 mUpdateInputStyle( false ),
166 mPasswordInput( false ),
167 mCheckScrollAmount( false ),
168 mIsPlaceholderPixelSize( false ),
169 mIsPlaceholderElideEnabled( false ),
170 mPlaceholderEllipsisFlag( false ),
171 mShiftSelectionFlag( true ),
172 mUpdateAlignment( false ),
173 mEditingEnabled( true )
177 EventData::~EventData()
180 bool Controller::Impl::ProcessInputEvents()
182 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
183 if( NULL == mEventData )
185 // Nothing to do if there is no text input.
186 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
190 if( mEventData->mDecorator )
192 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
193 iter != mEventData->mEventQueue.end();
198 case Event::CURSOR_KEY_EVENT:
200 OnCursorKeyEvent( *iter );
203 case Event::TAP_EVENT:
208 case Event::LONG_PRESS_EVENT:
210 OnLongPressEvent( *iter );
213 case Event::PAN_EVENT:
218 case Event::GRAB_HANDLE_EVENT:
219 case Event::LEFT_SELECTION_HANDLE_EVENT:
220 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
222 OnHandleEvent( *iter );
227 OnSelectEvent( *iter );
230 case Event::SELECT_ALL:
235 case Event::SELECT_NONE:
244 if( mEventData->mUpdateCursorPosition ||
245 mEventData->mUpdateHighlightBox )
247 NotifyInputMethodContext();
250 // The cursor must also be repositioned after inserts into the model
251 if( mEventData->mUpdateCursorPosition )
253 // Updates the cursor position and scrolls the text to make it visible.
254 CursorInfo cursorInfo;
255 // Calculate the cursor position from the new cursor index.
256 GetCursorPosition( mEventData->mPrimaryCursorPosition,
259 if( mEventData->mUpdateCursorHookPosition )
261 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
262 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
263 mEventData->mUpdateCursorHookPosition = false;
266 // Scroll first the text after delete ...
267 if( mEventData->mScrollAfterDelete )
269 ScrollTextToMatchCursor( cursorInfo );
272 // ... then, text can be scrolled to make the cursor visible.
273 if( mEventData->mScrollAfterUpdatePosition )
275 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
276 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
278 mEventData->mScrollAfterUpdatePosition = false;
279 mEventData->mScrollAfterDelete = false;
281 UpdateCursorPosition( cursorInfo );
283 mEventData->mDecoratorUpdated = true;
284 mEventData->mUpdateCursorPosition = false;
285 mEventData->mUpdateGrabHandlePosition = false;
289 CursorInfo leftHandleInfo;
290 CursorInfo rightHandleInfo;
292 if( mEventData->mUpdateHighlightBox )
294 GetCursorPosition( mEventData->mLeftSelectionPosition,
297 GetCursorPosition( mEventData->mRightSelectionPosition,
300 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
302 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
304 CursorInfo& infoLeft = leftHandleInfo;
306 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
307 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
309 CursorInfo& infoRight = rightHandleInfo;
311 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
312 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
316 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
318 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
319 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
324 if( mEventData->mUpdateLeftSelectionPosition )
326 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
330 mEventData->mDecoratorUpdated = true;
331 mEventData->mUpdateLeftSelectionPosition = false;
334 if( mEventData->mUpdateRightSelectionPosition )
336 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
340 mEventData->mDecoratorUpdated = true;
341 mEventData->mUpdateRightSelectionPosition = false;
344 if( mEventData->mUpdateHighlightBox )
346 RepositionSelectionHandles();
348 mEventData->mUpdateLeftSelectionPosition = false;
349 mEventData->mUpdateRightSelectionPosition = false;
350 mEventData->mUpdateHighlightBox = false;
351 mEventData->mIsLeftHandleSelected = false;
352 mEventData->mIsRightHandleSelected = false;
355 mEventData->mScrollAfterUpdatePosition = false;
358 if( mEventData->mUpdateInputStyle )
360 // Keep a copy of the current input style.
361 InputStyle currentInputStyle;
362 currentInputStyle.Copy( mEventData->mInputStyle );
364 // Set the default style first.
365 RetrieveDefaultInputStyle( mEventData->mInputStyle );
367 // Get the character index from the cursor index.
368 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
370 // Retrieve the style from the style runs stored in the logical model.
371 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
373 // Compare if the input style has changed.
374 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
376 if( hasInputStyleChanged )
378 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
379 // Queue the input style changed signal.
380 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
383 mEventData->mUpdateInputStyle = false;
386 mEventData->mEventQueue.clear();
388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
390 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
391 mEventData->mDecoratorUpdated = false;
393 return decoratorUpdated;
396 void Controller::Impl::NotifyInputMethodContext()
398 if( mEventData && mEventData->mInputMethodContext )
400 CharacterIndex cursorPosition = GetLogicalCursorPosition();
402 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
404 // Update the cursor position by removing the initial white spaces.
405 if( cursorPosition < numberOfWhiteSpaces )
411 cursorPosition -= numberOfWhiteSpaces;
414 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
415 mEventData->mInputMethodContext.NotifyCursorPosition();
419 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
421 if ( mEventData && mEventData->mInputMethodContext )
423 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
424 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
428 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
430 CharacterIndex cursorPosition = 0u;
434 if( ( EventData::SELECTING == mEventData->mState ) ||
435 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
437 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
441 cursorPosition = mEventData->mPrimaryCursorPosition;
445 return cursorPosition;
448 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
450 Length numberOfWhiteSpaces = 0u;
452 // Get the buffer to the text.
453 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
455 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
456 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
458 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
464 return numberOfWhiteSpaces;
467 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
469 // Get the total number of characters.
470 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
472 // Retrieve the text.
473 if( 0u != numberOfCharacters )
475 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
479 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
481 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
482 mTextUpdateInfo.mStartGlyphIndex = 0u;
483 mTextUpdateInfo.mStartLineIndex = 0u;
484 numberOfCharacters = 0u;
486 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
487 if( 0u == numberOfParagraphs )
489 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
490 numberOfCharacters = 0u;
492 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
494 // Nothing else to do if there are no paragraphs.
498 // Find the paragraphs to be updated.
499 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
500 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
502 // Text is being added at the end of the current text.
503 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
505 // Text is being added in a new paragraph after the last character of the text.
506 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
507 numberOfCharacters = 0u;
508 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
510 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
511 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
513 // Nothing else to do;
517 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
521 Length numberOfCharactersToUpdate = 0u;
522 if( mTextUpdateInfo.mFullRelayoutNeeded )
524 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
528 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
530 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
531 numberOfCharactersToUpdate,
532 paragraphsToBeUpdated );
535 if( 0u != paragraphsToBeUpdated.Count() )
537 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
538 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
539 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
541 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
542 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
544 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
545 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
546 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
547 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
549 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
550 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
552 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
556 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
560 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
561 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
564 void Controller::Impl::ClearFullModelData( OperationsMask operations )
566 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
568 mModel->mLogicalModel->mLineBreakInfo.Clear();
569 mModel->mLogicalModel->mParagraphInfo.Clear();
572 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
574 mModel->mLogicalModel->mScriptRuns.Clear();
577 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
579 mModel->mLogicalModel->mFontRuns.Clear();
582 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
584 if( NO_OPERATION != ( BIDI_INFO & operations ) )
586 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
587 mModel->mLogicalModel->mCharacterDirections.Clear();
590 if( NO_OPERATION != ( REORDER & operations ) )
592 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
593 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
594 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
598 BidirectionalLineInfoRun& bidiLineInfo = *it;
600 free( bidiLineInfo.visualToLogicalMap );
601 bidiLineInfo.visualToLogicalMap = NULL;
603 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
607 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
609 mModel->mVisualModel->mGlyphs.Clear();
610 mModel->mVisualModel->mGlyphsToCharacters.Clear();
611 mModel->mVisualModel->mCharactersToGlyph.Clear();
612 mModel->mVisualModel->mCharactersPerGlyph.Clear();
613 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
614 mModel->mVisualModel->mGlyphPositions.Clear();
617 if( NO_OPERATION != ( LAYOUT & operations ) )
619 mModel->mVisualModel->mLines.Clear();
622 if( NO_OPERATION != ( COLOR & operations ) )
624 mModel->mVisualModel->mColorIndices.Clear();
625 mModel->mVisualModel->mBackgroundColorIndices.Clear();
629 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
631 const CharacterIndex endIndexPlusOne = endIndex + 1u;
633 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
635 // Clear the line break info.
636 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
638 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
639 lineBreakInfoBuffer + endIndexPlusOne );
641 // Clear the paragraphs.
642 ClearCharacterRuns( startIndex,
644 mModel->mLogicalModel->mParagraphInfo );
647 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
649 // Clear the scripts.
650 ClearCharacterRuns( startIndex,
652 mModel->mLogicalModel->mScriptRuns );
655 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
658 ClearCharacterRuns( startIndex,
660 mModel->mLogicalModel->mFontRuns );
663 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
665 if( NO_OPERATION != ( BIDI_INFO & operations ) )
667 // Clear the bidirectional paragraph info.
668 ClearCharacterRuns( startIndex,
670 mModel->mLogicalModel->mBidirectionalParagraphInfo );
672 // Clear the character's directions.
673 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
675 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
676 characterDirectionsBuffer + endIndexPlusOne );
679 if( NO_OPERATION != ( REORDER & operations ) )
681 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
682 uint32_t endRemoveIndex = startRemoveIndex;
683 ClearCharacterRuns( startIndex,
685 mModel->mLogicalModel->mBidirectionalLineInfo,
689 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
691 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
692 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
693 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
697 BidirectionalLineInfoRun& bidiLineInfo = *it;
699 free( bidiLineInfo.visualToLogicalMap );
700 bidiLineInfo.visualToLogicalMap = NULL;
703 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
704 bidirectionalLineInfoBuffer + endRemoveIndex );
709 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
711 const CharacterIndex endIndexPlusOne = endIndex + 1u;
712 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
714 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
715 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
716 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
718 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
719 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
721 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
723 // Update the character to glyph indices.
724 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
725 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
729 CharacterIndex& index = *it;
730 index -= numberOfGlyphsRemoved;
733 // Clear the character to glyph conversion table.
734 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
735 charactersToGlyphBuffer + endIndexPlusOne );
737 // Clear the glyphs per character table.
738 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
739 glyphsPerCharacterBuffer + endIndexPlusOne );
741 // Clear the glyphs buffer.
742 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
743 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
744 glyphsBuffer + endGlyphIndexPlusOne );
746 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
748 // Update the glyph to character indices.
749 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
750 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
754 CharacterIndex& index = *it;
755 index -= numberOfCharactersRemoved;
758 // Clear the glyphs to characters buffer.
759 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
760 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
762 // Clear the characters per glyph buffer.
763 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
764 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
765 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
767 // Clear the positions buffer.
768 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
769 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
770 positionsBuffer + endGlyphIndexPlusOne );
773 if( NO_OPERATION != ( LAYOUT & operations ) )
776 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
777 uint32_t endRemoveIndex = startRemoveIndex;
778 ClearCharacterRuns( startIndex,
780 mModel->mVisualModel->mLines,
784 // Will update the glyph runs.
785 startRemoveIndex = mModel->mVisualModel->mLines.Count();
786 endRemoveIndex = startRemoveIndex;
787 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
788 endGlyphIndexPlusOne - 1u,
789 mModel->mVisualModel->mLines,
793 // Set the line index from where to insert the new laid-out lines.
794 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
796 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
797 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
798 linesBuffer + endRemoveIndex );
801 if( NO_OPERATION != ( COLOR & operations ) )
803 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
805 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
806 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
807 colorIndexBuffer + endGlyphIndexPlusOne );
810 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
812 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
813 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
814 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
819 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
821 if( mTextUpdateInfo.mClearAll ||
822 ( ( 0u == startIndex ) &&
823 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
825 ClearFullModelData( operations );
829 // Clear the model data related with characters.
830 ClearCharacterModelData( startIndex, endIndex, operations );
832 // Clear the model data related with glyphs.
833 ClearGlyphModelData( startIndex, endIndex, operations );
836 // The estimated number of lines. Used to avoid reallocations when layouting.
837 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
839 mModel->mVisualModel->ClearCaches();
842 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
844 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
846 // Calculate the operations to be done.
847 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
849 if( NO_OPERATION == operations )
851 // Nothing to do if no operations are pending and required.
855 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
856 Vector<Character> displayCharacters;
857 bool useHiddenText = false;
858 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
860 mHiddenInput->Substitute( srcCharacters,displayCharacters );
861 useHiddenText = true;
864 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
865 const Length numberOfCharacters = utf32Characters.Count();
867 // Index to the first character of the first paragraph to be updated.
868 CharacterIndex startIndex = 0u;
869 // Number of characters of the paragraphs to be removed.
870 Length paragraphCharacters = 0u;
872 CalculateTextUpdateIndices( paragraphCharacters );
874 // Check whether the indices for updating the text is valid
875 if ( numberOfCharacters > 0u &&
876 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
877 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
879 std::string currentText;
880 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
882 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
883 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
885 // Dump mTextUpdateInfo
886 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
887 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
888 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
889 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
890 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
891 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
892 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
893 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
894 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
895 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
896 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
897 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
898 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
903 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
905 if( mTextUpdateInfo.mClearAll ||
906 ( 0u != paragraphCharacters ) )
908 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
911 mTextUpdateInfo.mClearAll = false;
913 // Whether the model is updated.
914 bool updated = false;
916 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
917 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
919 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
921 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
922 // calculate the bidirectional info for each 'paragraph'.
923 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
924 // is not shaped together).
925 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
927 SetLineBreakInfo( utf32Characters,
929 requestedNumberOfCharacters,
932 // Create the paragraph info.
933 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
934 requestedNumberOfCharacters );
938 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
939 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
941 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
942 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
944 if( getScripts || validateFonts )
946 // Validates the fonts assigned by the application or assigns default ones.
947 // It makes sure all the characters are going to be rendered by the correct font.
948 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
952 // Retrieves the scripts used in the text.
953 multilanguageSupport.SetScripts( utf32Characters,
955 requestedNumberOfCharacters,
961 // Validate the fonts set through the mark-up string.
962 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
964 // Get the default font's description.
965 TextAbstraction::FontDescription defaultFontDescription;
966 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
968 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
970 // If the placeholder font is set specifically, only placeholder font is changed.
971 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
972 if( mEventData->mPlaceholderFont->sizeDefined )
974 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
977 else if( NULL != mFontDefaults )
979 // Set the normal font and the placeholder font.
980 defaultFontDescription = mFontDefaults->mFontDescription;
982 if( mTextFitEnabled )
984 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
988 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
992 // Validates the fonts. If there is a character with no assigned font it sets a default one.
993 // After this call, fonts are validated.
994 multilanguageSupport.ValidateFonts( utf32Characters,
997 defaultFontDescription,
1000 requestedNumberOfCharacters,
1006 Vector<Character> mirroredUtf32Characters;
1007 bool textMirrored = false;
1008 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
1009 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1011 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1012 bidirectionalInfo.Reserve( numberOfParagraphs );
1014 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1015 SetBidirectionalInfo( utf32Characters,
1019 requestedNumberOfCharacters,
1021 mModel->mMatchSystemLanguageDirection,
1024 if( 0u != bidirectionalInfo.Count() )
1026 // Only set the character directions if there is right to left characters.
1027 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1028 GetCharactersDirection( bidirectionalInfo,
1031 requestedNumberOfCharacters,
1034 // This paragraph has right to left text. Some characters may need to be mirrored.
1035 // TODO: consider if the mirrored string can be stored as well.
1037 textMirrored = GetMirroredText( utf32Characters,
1041 requestedNumberOfCharacters,
1042 mirroredUtf32Characters );
1046 // There is no right to left characters. Clear the directions vector.
1047 mModel->mLogicalModel->mCharacterDirections.Clear();
1052 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1053 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1054 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1055 Vector<GlyphIndex> newParagraphGlyphs;
1056 newParagraphGlyphs.Reserve( numberOfParagraphs );
1058 const Length currentNumberOfGlyphs = glyphs.Count();
1059 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1061 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1063 ShapeText( textToShape,
1068 mTextUpdateInfo.mStartGlyphIndex,
1069 requestedNumberOfCharacters,
1071 glyphsToCharactersMap,
1073 newParagraphGlyphs );
1075 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1076 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1077 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1081 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1083 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1085 GlyphInfo* glyphsBuffer = glyphs.Begin();
1086 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1088 // Update the width and advance of all new paragraph characters.
1089 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1091 const GlyphIndex index = *it;
1092 GlyphInfo& glyph = *( glyphsBuffer + index );
1094 glyph.xBearing = 0.f;
1096 glyph.advance = 0.f;
1101 if( ( NULL != mEventData ) &&
1102 mEventData->mPreEditFlag &&
1103 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1105 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1106 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1107 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1109 // Check the type of preedit and run it.
1110 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1112 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1113 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1114 type = attrData.preeditType;
1116 // Check the number of commit characters for the start position.
1117 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1118 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1122 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1124 // Add the underline for the pre-edit text.
1125 GlyphRun underlineRun;
1126 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1127 underlineRun.numberOfGlyphs = numberOfIndices;
1128 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1131 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1133 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1134 ColorRun backgroundColorRun;
1135 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1136 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1137 backgroundColorRun.color = textColor;
1138 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1140 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1141 if(backgroundColor.a == 0) // There is no text background color.
1143 // Try use the control's background color.
1144 if(nullptr != mEditableControlInterface)
1146 mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
1147 if(backgroundColor.a == 0) // There is no control background color.
1149 // Determines black or white color according to text color.
1150 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
1151 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
1152 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
1157 Vector<ColorRun> colorRuns;
1158 colorRuns.Resize(1u);
1159 ColorRun& colorRun = *(colorRuns.Begin());
1160 colorRun.color = backgroundColor;
1161 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1162 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1164 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1167 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1169 ColorRun backgroundColorRun;
1170 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1171 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1172 backgroundColorRun.color = LIGHT_BLUE;
1173 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1176 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1178 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1179 ColorRun backgroundColorRun;
1180 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1181 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1182 backgroundColorRun.color = BACKGROUND_SUB4;
1183 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1185 GlyphRun underlineRun;
1186 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1187 underlineRun.numberOfGlyphs = numberOfIndices;
1188 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1191 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1193 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1194 ColorRun backgroundColorRun;
1195 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1196 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1197 backgroundColorRun.color = BACKGROUND_SUB5;
1198 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1200 GlyphRun underlineRun;
1201 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1202 underlineRun.numberOfGlyphs = numberOfIndices;
1203 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1206 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1208 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1209 ColorRun backgroundColorRun;
1210 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1211 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1212 backgroundColorRun.color = BACKGROUND_SUB6;
1213 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1215 GlyphRun underlineRun;
1216 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1217 underlineRun.numberOfGlyphs = numberOfIndices;
1218 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1221 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1223 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1224 ColorRun backgroundColorRun;
1225 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1226 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1227 backgroundColorRun.color = BACKGROUND_SUB7;
1228 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1230 GlyphRun underlineRun;
1231 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1232 underlineRun.numberOfGlyphs = numberOfIndices;
1233 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1236 case Dali::InputMethodContext::PreeditStyle::NONE:
1247 if( NO_OPERATION != ( COLOR & operations ) )
1249 // Set the color runs in glyphs.
1250 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1251 mModel->mVisualModel->mCharactersToGlyph,
1252 mModel->mVisualModel->mGlyphsPerCharacter,
1254 mTextUpdateInfo.mStartGlyphIndex,
1255 requestedNumberOfCharacters,
1256 mModel->mVisualModel->mColors,
1257 mModel->mVisualModel->mColorIndices );
1259 // Set the background color runs in glyphs.
1260 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1261 mModel->mVisualModel->mCharactersToGlyph,
1262 mModel->mVisualModel->mGlyphsPerCharacter,
1264 mTextUpdateInfo.mStartGlyphIndex,
1265 requestedNumberOfCharacters,
1266 mModel->mVisualModel->mBackgroundColors,
1267 mModel->mVisualModel->mBackgroundColorIndices );
1273 // The estimated number of lines. Used to avoid reallocations when layouting.
1274 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1276 // Set the previous number of characters for the next time the text is updated.
1277 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1282 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1284 // Sets the default text's color.
1285 inputStyle.textColor = mTextColor;
1286 inputStyle.isDefaultColor = true;
1288 inputStyle.familyName.clear();
1289 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1290 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1291 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1292 inputStyle.size = 0.f;
1294 inputStyle.lineSpacing = 0.f;
1296 inputStyle.underlineProperties.clear();
1297 inputStyle.shadowProperties.clear();
1298 inputStyle.embossProperties.clear();
1299 inputStyle.outlineProperties.clear();
1301 inputStyle.isFamilyDefined = false;
1302 inputStyle.isWeightDefined = false;
1303 inputStyle.isWidthDefined = false;
1304 inputStyle.isSlantDefined = false;
1305 inputStyle.isSizeDefined = false;
1307 inputStyle.isLineSpacingDefined = false;
1309 inputStyle.isUnderlineDefined = false;
1310 inputStyle.isShadowDefined = false;
1311 inputStyle.isEmbossDefined = false;
1312 inputStyle.isOutlineDefined = false;
1314 // Sets the default font's family name, weight, width, slant and size.
1317 if( mFontDefaults->familyDefined )
1319 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1320 inputStyle.isFamilyDefined = true;
1323 if( mFontDefaults->weightDefined )
1325 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1326 inputStyle.isWeightDefined = true;
1329 if( mFontDefaults->widthDefined )
1331 inputStyle.width = mFontDefaults->mFontDescription.width;
1332 inputStyle.isWidthDefined = true;
1335 if( mFontDefaults->slantDefined )
1337 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1338 inputStyle.isSlantDefined = true;
1341 if( mFontDefaults->sizeDefined )
1343 inputStyle.size = mFontDefaults->mDefaultPointSize;
1344 inputStyle.isSizeDefined = true;
1349 float Controller::Impl::GetDefaultFontLineHeight()
1351 FontId defaultFontId = 0u;
1352 if( NULL == mFontDefaults )
1354 TextAbstraction::FontDescription fontDescription;
1355 defaultFontId = mFontClient.GetFontId( fontDescription );
1359 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1362 Text::FontMetrics fontMetrics;
1363 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1365 return( fontMetrics.ascender - fontMetrics.descender );
1368 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1370 ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
1373 void Controller::Impl::OnTapEvent( const Event& event )
1375 ControllerImplEventHandler::OnTapEvent(*this, event);
1378 void Controller::Impl::OnPanEvent( const Event& event )
1380 ControllerImplEventHandler::OnPanEvent(*this, event);
1383 void Controller::Impl::OnLongPressEvent( const Event& event )
1385 ControllerImplEventHandler::OnLongPressEvent(*this, event);
1388 void Controller::Impl::OnHandleEvent( const Event& event )
1390 ControllerImplEventHandler::OnHandleEvent(*this, event);
1393 void Controller::Impl::OnSelectEvent( const Event& event )
1395 ControllerImplEventHandler::OnSelectEvent(*this, event);
1398 void Controller::Impl::OnSelectAllEvent()
1400 ControllerImplEventHandler::OnSelectAllEvent(*this);
1403 void Controller::Impl::OnSelectNoneEvent()
1405 ControllerImplEventHandler::OnSelectNoneEvent(*this);
1408 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
1410 if( nullptr == mEventData )
1412 // Nothing to do if there is no text.
1416 if( mEventData->mSelectionEnabled && (pStart || pEnd))
1418 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1422 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1426 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1429 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1431 ChangeState( EventData::EDITING );
1432 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1433 mEventData->mUpdateCursorPosition = true;
1437 ChangeState( EventData::SELECTING );
1438 mEventData->mUpdateHighlightBox = true;
1439 mEventData->mUpdateLeftSelectionPosition = true;
1440 mEventData->mUpdateRightSelectionPosition = true;
1445 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1451 range.first = mEventData->mLeftSelectionPosition;
1452 range.second = mEventData->mRightSelectionPosition;
1458 bool Controller::Impl::IsEditable() const
1460 return mEventData && mEventData->mEditingEnabled;
1463 void Controller::Impl::SetEditable( bool editable )
1467 mEventData->mEditingEnabled = editable;
1471 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1473 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1475 // Nothing to select if handles are in the same place.
1476 selectedText.clear();
1480 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1482 //Get start and end position of selection
1483 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1484 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1486 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1487 const Length numberOfCharacters = utf32Characters.Count();
1489 // Validate the start and end selection points
1490 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1492 //Get text as a UTF8 string
1493 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1495 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1497 // Keep a copy of the current input style.
1498 InputStyle currentInputStyle;
1499 currentInputStyle.Copy( mEventData->mInputStyle );
1501 // Set as input style the style of the first deleted character.
1502 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1504 // Compare if the input style has changed.
1505 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1507 if( hasInputStyleChanged )
1509 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1510 // Queue the input style changed signal.
1511 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1514 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1516 // Mark the paragraphs to be updated.
1517 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1519 mTextUpdateInfo.mCharacterIndex = 0;
1520 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1521 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1522 mTextUpdateInfo.mClearAll = true;
1526 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1527 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1530 // Delete text between handles
1531 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1532 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1533 utf32Characters.Erase( first, last );
1535 // Will show the cursor at the first character of the selection.
1536 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1540 // Will show the cursor at the last character of the selection.
1541 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1544 mEventData->mDecoratorUpdated = true;
1548 void Controller::Impl::ShowClipboard()
1552 mClipboard.ShowClipboard();
1556 void Controller::Impl::HideClipboard()
1558 if( mClipboard && mClipboardHideEnabled )
1560 mClipboard.HideClipboard();
1564 void Controller::Impl::SetClipboardHideEnable(bool enable)
1566 mClipboardHideEnabled = enable;
1569 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1571 //Send string to clipboard
1572 return ( mClipboard && mClipboard.SetItem( source ) );
1575 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1577 std::string selectedText;
1578 RetrieveSelection( selectedText, deleteAfterSending );
1579 CopyStringToClipboard( selectedText );
1580 ChangeState( EventData::EDITING );
1583 void Controller::Impl::RequestGetTextFromClipboard()
1587 mClipboard.RequestItem();
1591 void Controller::Impl::RepositionSelectionHandles()
1593 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1594 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1596 if( selectionStart == selectionEnd )
1598 // Nothing to select if handles are in the same place.
1599 // So, deactive Highlight box.
1600 mEventData->mDecorator->SetHighlightActive( false );
1604 mEventData->mDecorator->ClearHighlights();
1606 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1607 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1608 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1609 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1610 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1611 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1612 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1614 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1615 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1616 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1618 // Swap the indices if the start is greater than the end.
1619 const bool indicesSwapped = selectionStart > selectionEnd;
1621 // Tell the decorator to flip the selection handles if needed.
1622 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1624 if( indicesSwapped )
1626 std::swap( selectionStart, selectionEnd );
1629 // Get the indices to the first and last selected glyphs.
1630 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1631 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1632 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1633 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1635 // Get the lines where the glyphs are laid-out.
1636 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1638 LineIndex lineIndex = 0u;
1639 Length numberOfLines = 0u;
1640 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1641 1u + glyphEnd - glyphStart,
1644 const LineIndex firstLineIndex = lineIndex;
1646 // Create the structure to store some selection box info.
1647 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1648 selectionBoxLinesInfo.Resize( numberOfLines );
1650 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1651 selectionBoxInfo->minX = MAX_FLOAT;
1652 selectionBoxInfo->maxX = MIN_FLOAT;
1654 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1655 float minHighlightX = std::numeric_limits<float>::max();
1656 float maxHighlightX = std::numeric_limits<float>::min();
1658 Vector2 highLightPosition; // The highlight position in decorator's coords.
1660 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1662 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1663 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1666 // Transform to decorator's (control) coords.
1667 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1669 lineRun += firstLineIndex;
1671 // The line height is the addition of the line ascender and the line descender.
1672 // However, the line descender has a negative value, hence the subtraction.
1673 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1675 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1677 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1678 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1679 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1681 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1682 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1683 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1685 // The number of quads of the selection box.
1686 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1687 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1689 // Count the actual number of quads.
1690 unsigned int actualNumberOfQuads = 0u;
1693 // Traverse the glyphs.
1694 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1696 const GlyphInfo& glyph = *( glyphsBuffer + index );
1697 const Vector2& position = *( positionsBuffer + index );
1699 if( splitStartGlyph )
1701 // 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.
1703 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1704 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1705 // Get the direction of the character.
1706 CharacterDirection isCurrentRightToLeft = false;
1707 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1709 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1712 // The end point could be in the middle of the ligature.
1713 // Calculate the number of characters selected.
1714 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1716 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1717 quad.y = selectionBoxInfo->lineOffset;
1718 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1719 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1721 // Store the min and max 'x' for each line.
1722 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1723 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1725 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1726 ++actualNumberOfQuads;
1728 splitStartGlyph = false;
1732 if( splitEndGlyph && ( index == glyphEnd ) )
1734 // 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.
1736 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1737 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1738 // Get the direction of the character.
1739 CharacterDirection isCurrentRightToLeft = false;
1740 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1742 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1745 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1747 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1748 quad.y = selectionBoxInfo->lineOffset;
1749 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1750 quad.w = quad.y + selectionBoxInfo->lineHeight;
1752 // Store the min and max 'x' for each line.
1753 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1754 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1756 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1758 ++actualNumberOfQuads;
1760 splitEndGlyph = false;
1764 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1765 quad.y = selectionBoxInfo->lineOffset;
1766 quad.z = quad.x + glyph.advance;
1767 quad.w = quad.y + selectionBoxInfo->lineHeight;
1769 // Store the min and max 'x' for each line.
1770 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1771 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1773 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1775 ++actualNumberOfQuads;
1777 // Whether to retrieve the next line.
1778 if( index == lastGlyphOfLine )
1781 if( lineIndex < firstLineIndex + numberOfLines )
1783 // Retrieve the next line.
1786 // Get the last glyph of the new line.
1787 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1789 // Keep the offset and height of the current selection box.
1790 const float currentLineOffset = selectionBoxInfo->lineOffset;
1791 const float currentLineHeight = selectionBoxInfo->lineHeight;
1793 // Get the selection box info for the next line.
1796 selectionBoxInfo->minX = MAX_FLOAT;
1797 selectionBoxInfo->maxX = MIN_FLOAT;
1799 // Update the line's vertical offset.
1800 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1802 // The line height is the addition of the line ascender and the line descender.
1803 // However, the line descender has a negative value, hence the subtraction.
1804 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1809 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1810 // The final width is calculated after 'boxifying' the selection.
1811 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1812 endIt = selectionBoxLinesInfo.End();
1816 const SelectionBoxInfo& info = *it;
1818 // Update the size of the highlighted text.
1819 highLightSize.height += info.lineHeight;
1820 minHighlightX = std::min( minHighlightX, info.minX );
1821 maxHighlightX = std::max( maxHighlightX, info.maxX );
1824 // Add extra geometry to 'boxify' the selection.
1826 if( 1u < numberOfLines )
1828 // Boxify the first line.
1829 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
1830 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1832 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1833 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1838 quad.y = firstSelectionBoxLineInfo.lineOffset;
1839 quad.z = firstSelectionBoxLineInfo.minX;
1840 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1842 // Boxify at the beginning of the line.
1843 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1845 ++actualNumberOfQuads;
1847 // Update the size of the highlighted text.
1848 minHighlightX = 0.f;
1853 quad.x = firstSelectionBoxLineInfo.maxX;
1854 quad.y = firstSelectionBoxLineInfo.lineOffset;
1855 quad.z = mModel->mVisualModel->mControlSize.width;
1856 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1858 // Boxify at the end of the line.
1859 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1861 ++actualNumberOfQuads;
1863 // Update the size of the highlighted text.
1864 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1867 // Boxify the central lines.
1868 if( 2u < numberOfLines )
1870 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1871 endIt = selectionBoxLinesInfo.End() - 1u;
1875 const SelectionBoxInfo& info = *it;
1878 quad.y = info.lineOffset;
1880 quad.w = info.lineOffset + info.lineHeight;
1882 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1884 ++actualNumberOfQuads;
1887 quad.y = info.lineOffset;
1888 quad.z = mModel->mVisualModel->mControlSize.width;
1889 quad.w = info.lineOffset + info.lineHeight;
1891 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1893 ++actualNumberOfQuads;
1896 // Update the size of the highlighted text.
1897 minHighlightX = 0.f;
1898 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1901 // Boxify the last line.
1902 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1903 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1905 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1906 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1911 quad.y = lastSelectionBoxLineInfo.lineOffset;
1912 quad.z = lastSelectionBoxLineInfo.minX;
1913 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1915 // Boxify at the beginning of the line.
1916 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1918 ++actualNumberOfQuads;
1920 // Update the size of the highlighted text.
1921 minHighlightX = 0.f;
1926 quad.x = lastSelectionBoxLineInfo.maxX;
1927 quad.y = lastSelectionBoxLineInfo.lineOffset;
1928 quad.z = mModel->mVisualModel->mControlSize.width;
1929 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
1931 // Boxify at the end of the line.
1932 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1934 ++actualNumberOfQuads;
1936 // Update the size of the highlighted text.
1937 maxHighlightX = mModel->mVisualModel->mControlSize.width;
1941 // Set the actual number of quads.
1942 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
1944 // Sets the highlight's size and position. In decorator's coords.
1945 // The highlight's height has been calculated above (before 'boxifying' the highlight).
1946 highLightSize.width = maxHighlightX - minHighlightX;
1948 highLightPosition.x = minHighlightX;
1949 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1950 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
1952 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
1954 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1956 CursorInfo primaryCursorInfo;
1957 GetCursorPosition( mEventData->mLeftSelectionPosition,
1958 primaryCursorInfo );
1960 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1962 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1964 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1965 primaryCursorInfo.lineHeight );
1967 CursorInfo secondaryCursorInfo;
1968 GetCursorPosition( mEventData->mRightSelectionPosition,
1969 secondaryCursorInfo );
1971 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
1973 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1974 secondaryPosition.x,
1975 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
1976 secondaryCursorInfo.lineHeight );
1979 // Set the flag to update the decorator.
1980 mEventData->mDecoratorUpdated = true;
1983 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
1985 if( NULL == mEventData )
1987 // Nothing to do if there is no text input.
1991 if( IsShowingPlaceholderText() )
1993 // Nothing to do if there is the place-holder text.
1997 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
1998 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
1999 if( ( 0 == numberOfGlyphs ) ||
2000 ( 0 == numberOfLines ) )
2002 // Nothing to do if there is no text.
2006 // Find which word was selected
2007 CharacterIndex selectionStart( 0 );
2008 CharacterIndex selectionEnd( 0 );
2009 CharacterIndex noTextHitIndex( 0 );
2010 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2011 mModel->mLogicalModel,
2018 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2020 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2022 ChangeState( EventData::SELECTING );
2024 mEventData->mLeftSelectionPosition = selectionStart;
2025 mEventData->mRightSelectionPosition = selectionEnd;
2027 mEventData->mUpdateLeftSelectionPosition = true;
2028 mEventData->mUpdateRightSelectionPosition = true;
2029 mEventData->mUpdateHighlightBox = true;
2031 // It may happen an InputMethodContext commit event arrives before the selection event
2032 // if the InputMethodContext is in pre-edit state. The commit event will set the
2033 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2034 // to false, the highlight box won't be updated.
2035 mEventData->mUpdateCursorPosition = false;
2037 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2039 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2040 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2042 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2044 // Nothing to select. i.e. a white space, out of bounds
2045 ChangeState( EventData::EDITING_WITH_POPUP );
2047 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2049 mEventData->mUpdateCursorPosition = true;
2050 mEventData->mUpdateGrabHandlePosition = true;
2051 mEventData->mScrollAfterUpdatePosition = true;
2052 mEventData->mUpdateInputStyle = true;
2054 else if( Controller::NoTextTap::NO_ACTION == action )
2056 // Nothing to select. i.e. a white space, out of bounds
2057 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2059 mEventData->mUpdateCursorPosition = true;
2060 mEventData->mUpdateGrabHandlePosition = true;
2061 mEventData->mScrollAfterUpdatePosition = true;
2062 mEventData->mUpdateInputStyle = true;
2066 void Controller::Impl::SetPopupButtons()
2069 * Sets the Popup buttons to be shown depending on State.
2071 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2073 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2076 bool isEditable = IsEditable();
2077 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2079 if( EventData::SELECTING == mEventData->mState )
2081 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2084 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2087 if( !IsClipboardEmpty() )
2091 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2093 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2096 if( !mEventData->mAllTextSelected )
2098 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2101 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2103 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2105 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2108 if( !IsClipboardEmpty() )
2112 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2114 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2117 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2119 if ( !IsClipboardEmpty() )
2123 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2125 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2129 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2132 void Controller::Impl::ChangeState( EventData::State newState )
2134 if( NULL == mEventData )
2136 // Nothing to do if there is no text input.
2140 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2142 if( mEventData->mState != newState )
2144 mEventData->mPreviousState = mEventData->mState;
2145 mEventData->mState = newState;
2147 switch( mEventData->mState )
2149 case EventData::INACTIVE:
2151 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2152 mEventData->mDecorator->StopCursorBlink();
2153 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2154 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2155 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2156 mEventData->mDecorator->SetHighlightActive( false );
2157 mEventData->mDecorator->SetPopupActive( false );
2158 mEventData->mDecoratorUpdated = true;
2161 case EventData::INTERRUPTED:
2163 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2164 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2165 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2166 mEventData->mDecorator->SetHighlightActive( false );
2167 mEventData->mDecorator->SetPopupActive( false );
2168 mEventData->mDecoratorUpdated = true;
2171 case EventData::SELECTING:
2173 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2174 mEventData->mDecorator->StopCursorBlink();
2175 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2176 if ( mEventData->mGrabHandleEnabled )
2178 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2179 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2181 mEventData->mDecorator->SetHighlightActive( true );
2182 if( mEventData->mGrabHandlePopupEnabled )
2185 mEventData->mDecorator->SetPopupActive( true );
2187 mEventData->mDecoratorUpdated = true;
2190 case EventData::EDITING:
2192 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2193 if( mEventData->mCursorBlinkEnabled )
2195 mEventData->mDecorator->StartCursorBlink();
2197 // Grab handle is not shown until a tap is received whilst EDITING
2198 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2199 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2200 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2201 mEventData->mDecorator->SetHighlightActive( false );
2202 if( mEventData->mGrabHandlePopupEnabled )
2204 mEventData->mDecorator->SetPopupActive( false );
2206 mEventData->mDecoratorUpdated = true;
2209 case EventData::EDITING_WITH_POPUP:
2211 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2213 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2214 if( mEventData->mCursorBlinkEnabled )
2216 mEventData->mDecorator->StartCursorBlink();
2218 if( mEventData->mSelectionEnabled )
2220 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2221 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2222 mEventData->mDecorator->SetHighlightActive( false );
2224 else if ( mEventData->mGrabHandleEnabled )
2226 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2228 if( mEventData->mGrabHandlePopupEnabled )
2231 mEventData->mDecorator->SetPopupActive( true );
2233 mEventData->mDecoratorUpdated = true;
2236 case EventData::EDITING_WITH_GRAB_HANDLE:
2238 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2240 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2241 if( mEventData->mCursorBlinkEnabled )
2243 mEventData->mDecorator->StartCursorBlink();
2245 // Grab handle is not shown until a tap is received whilst EDITING
2246 if ( mEventData->mGrabHandleEnabled )
2248 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2250 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2251 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2252 mEventData->mDecorator->SetHighlightActive( false );
2253 if( mEventData->mGrabHandlePopupEnabled )
2255 mEventData->mDecorator->SetPopupActive( false );
2257 mEventData->mDecoratorUpdated = true;
2260 case EventData::SELECTION_HANDLE_PANNING:
2262 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2263 mEventData->mDecorator->StopCursorBlink();
2264 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2265 if ( mEventData->mGrabHandleEnabled )
2267 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2268 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2270 mEventData->mDecorator->SetHighlightActive( true );
2271 if( mEventData->mGrabHandlePopupEnabled )
2273 mEventData->mDecorator->SetPopupActive( false );
2275 mEventData->mDecoratorUpdated = true;
2278 case EventData::GRAB_HANDLE_PANNING:
2280 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2282 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2283 if( mEventData->mCursorBlinkEnabled )
2285 mEventData->mDecorator->StartCursorBlink();
2287 if ( mEventData->mGrabHandleEnabled )
2289 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2291 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2292 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2293 mEventData->mDecorator->SetHighlightActive( false );
2294 if( mEventData->mGrabHandlePopupEnabled )
2296 mEventData->mDecorator->SetPopupActive( false );
2298 mEventData->mDecoratorUpdated = true;
2301 case EventData::EDITING_WITH_PASTE_POPUP:
2303 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2305 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2306 if( mEventData->mCursorBlinkEnabled )
2308 mEventData->mDecorator->StartCursorBlink();
2311 if ( mEventData->mGrabHandleEnabled )
2313 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2315 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2316 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2317 mEventData->mDecorator->SetHighlightActive( false );
2319 if( mEventData->mGrabHandlePopupEnabled )
2322 mEventData->mDecorator->SetPopupActive( true );
2324 mEventData->mDecoratorUpdated = true;
2327 case EventData::TEXT_PANNING:
2329 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2330 mEventData->mDecorator->StopCursorBlink();
2331 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2332 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2333 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2335 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2336 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2337 mEventData->mDecorator->SetHighlightActive( true );
2340 if( mEventData->mGrabHandlePopupEnabled )
2342 mEventData->mDecorator->SetPopupActive( false );
2345 mEventData->mDecoratorUpdated = true;
2352 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2353 CursorInfo& cursorInfo )
2355 if( !IsShowingRealText() )
2357 // Do not want to use the place-holder text to set the cursor position.
2359 // Use the line's height of the font's family set to set the cursor's size.
2360 // If there is no font's family set, use the default font.
2361 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2363 cursorInfo.lineOffset = 0.f;
2364 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2365 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2368 if( mModel->mMatchSystemLanguageDirection )
2370 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2373 switch( mModel->mHorizontalAlignment )
2375 case Text::HorizontalAlignment::BEGIN :
2379 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2383 cursorInfo.primaryPosition.x = 0.f;
2387 case Text::HorizontalAlignment::CENTER:
2389 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2392 case Text::HorizontalAlignment::END:
2396 cursorInfo.primaryPosition.x = 0.f;
2400 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2406 // Nothing else to do.
2410 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2411 GetCursorPositionParameters parameters;
2412 parameters.visualModel = mModel->mVisualModel;
2413 parameters.logicalModel = mModel->mLogicalModel;
2414 parameters.metrics = mMetrics;
2415 parameters.logical = logical;
2416 parameters.isMultiline = isMultiLine;
2418 Text::GetCursorPosition( parameters,
2421 // Adds Outline offset.
2422 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2423 cursorInfo.primaryPosition.x += outlineWidth;
2424 cursorInfo.primaryPosition.y += outlineWidth;
2425 cursorInfo.secondaryPosition.x += outlineWidth;
2426 cursorInfo.secondaryPosition.y += outlineWidth;
2430 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2432 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2433 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2435 if( 0.f > cursorInfo.primaryPosition.x )
2437 cursorInfo.primaryPosition.x = 0.f;
2440 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2441 if( cursorInfo.primaryPosition.x > edgeWidth )
2443 cursorInfo.primaryPosition.x = edgeWidth;
2448 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2450 if( NULL == mEventData )
2452 // Nothing to do if there is no text input.
2456 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2458 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2459 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2461 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2462 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2464 if( numberOfCharacters > 1u )
2466 const Script script = mModel->mLogicalModel->GetScript( index );
2467 if( HasLigatureMustBreak( script ) )
2469 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2470 numberOfCharacters = 1u;
2475 while( 0u == numberOfCharacters )
2478 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2482 if( index < mEventData->mPrimaryCursorPosition )
2484 cursorIndex -= numberOfCharacters;
2488 cursorIndex += numberOfCharacters;
2491 // Will update the cursor hook position.
2492 mEventData->mUpdateCursorHookPosition = true;
2497 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2499 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2500 if( NULL == mEventData )
2502 // Nothing to do if there is no text input.
2503 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2507 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2509 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2511 // Sets the cursor position.
2512 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2515 cursorInfo.primaryCursorHeight,
2516 cursorInfo.lineHeight );
2517 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2519 if( mEventData->mUpdateGrabHandlePosition )
2521 // Sets the grab handle position.
2522 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2524 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2525 cursorInfo.lineHeight );
2528 if( cursorInfo.isSecondaryCursor )
2530 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2531 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2532 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2533 cursorInfo.secondaryCursorHeight,
2534 cursorInfo.lineHeight );
2535 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2538 // Set which cursors are active according the state.
2539 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2541 if( cursorInfo.isSecondaryCursor )
2543 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2547 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2552 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2555 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2558 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2559 const CursorInfo& cursorInfo )
2561 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2562 ( RIGHT_SELECTION_HANDLE != handleType ) )
2567 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2569 // Sets the handle's position.
2570 mEventData->mDecorator->SetPosition( handleType,
2572 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2573 cursorInfo.lineHeight );
2575 // If selection handle at start of the text and other at end of the text then all text is selected.
2576 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2577 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2578 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2581 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2583 // Clamp between -space & -alignment offset.
2585 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2587 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2588 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2589 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2591 mEventData->mDecoratorUpdated = true;
2595 mModel->mScrollPosition.x = 0.f;
2599 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2601 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2603 // Nothing to do if the text is single line.
2607 // Clamp between -space & 0.
2608 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2610 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2611 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2612 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2614 mEventData->mDecoratorUpdated = true;
2618 mModel->mScrollPosition.y = 0.f;
2622 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2624 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2626 // position is in actor's coords.
2627 const float positionEndX = position.x + cursorWidth;
2628 const float positionEndY = position.y + lineHeight;
2630 // Transform the position to decorator coords.
2631 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2632 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2634 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2635 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2637 if( decoratorPositionBeginX < 0.f )
2639 mModel->mScrollPosition.x = -position.x;
2641 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2643 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2646 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2648 if( decoratorPositionBeginY < 0.f )
2650 mModel->mScrollPosition.y = -position.y;
2652 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2654 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2659 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2661 // Get the current cursor position in decorator coords.
2662 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2664 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2668 // Calculate the offset to match the cursor position before the character was deleted.
2669 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2671 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2672 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2674 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2675 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2679 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2680 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2682 // Makes the new cursor position visible if needed.
2683 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2686 void Controller::Impl::RequestRelayout()
2688 if( NULL != mControlInterface )
2690 mControlInterface->RequestTextRelayout();
2694 Actor Controller::Impl::CreateBackgroundActor()
2696 // NOTE: Currently we only support background color for one line left-to-right text,
2697 // so the following calculation is based on one line left-to-right text only!
2701 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2702 if( numberOfGlyphs > 0u )
2704 Vector<GlyphInfo> glyphs;
2705 glyphs.Resize( numberOfGlyphs );
2707 Vector<Vector2> positions;
2708 positions.Resize( numberOfGlyphs );
2710 // Get the line where the glyphs are laid-out.
2711 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2712 float alignmentOffset = lineRun->alignmentOffset;
2713 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
2719 glyphs.Resize( numberOfGlyphs );
2720 positions.Resize( numberOfGlyphs );
2722 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2723 const Vector2* const positionsBuffer = positions.Begin();
2725 BackgroundMesh mesh;
2726 mesh.mVertices.Reserve( 4u * glyphs.Size() );
2727 mesh.mIndices.Reserve( 6u * glyphs.Size() );
2729 const Vector2 textSize = mView.GetLayoutSize();
2731 const float offsetX = textSize.width * 0.5f;
2732 const float offsetY = textSize.height * 0.5f;
2734 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2735 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2736 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2739 uint32_t numberOfQuads = 0u;
2741 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
2743 const GlyphInfo& glyph = *( glyphsBuffer + i );
2745 // Get the background color of the character.
2746 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2747 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
2748 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
2750 // Only create quads for glyphs with a background color
2751 if ( backgroundColor != Color::TRANSPARENT )
2753 const Vector2 position = *( positionsBuffer + i );
2755 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
2757 quad.x = position.x;
2759 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2760 quad.w = textSize.height;
2762 else if ( i == 0u ) // The first glyph in the whole text
2764 quad.x = position.x;
2766 quad.z = quad.x - glyph.xBearing + glyph.advance;
2767 quad.w = textSize.height;
2769 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
2771 quad.x = position.x - glyph.xBearing;
2773 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
2774 quad.w = textSize.height;
2776 else // The glyph in the middle of the text
2778 quad.x = position.x - glyph.xBearing;
2780 quad.z = quad.x + glyph.advance;
2781 quad.w = textSize.height;
2784 BackgroundVertex vertex;
2787 vertex.mPosition.x = quad.x - offsetX;
2788 vertex.mPosition.y = quad.y - offsetY;
2789 vertex.mColor = backgroundColor;
2790 mesh.mVertices.PushBack( vertex );
2793 vertex.mPosition.x = quad.z - offsetX;
2794 vertex.mPosition.y = quad.y - offsetY;
2795 vertex.mColor = backgroundColor;
2796 mesh.mVertices.PushBack( vertex );
2799 vertex.mPosition.x = quad.x - offsetX;
2800 vertex.mPosition.y = quad.w - offsetY;
2801 vertex.mColor = backgroundColor;
2802 mesh.mVertices.PushBack( vertex );
2805 vertex.mPosition.x = quad.z - offsetX;
2806 vertex.mPosition.y = quad.w - offsetY;
2807 vertex.mColor = backgroundColor;
2808 mesh.mVertices.PushBack( vertex );
2810 // Six indices in counter clockwise winding
2811 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2812 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
2813 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2814 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
2815 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
2816 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
2822 // Only create the background actor if there are glyphs with background color
2823 if ( mesh.mVertices.Count() > 0u )
2825 Property::Map quadVertexFormat;
2826 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
2827 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
2829 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
2830 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
2832 Geometry quadGeometry = Geometry::New();
2833 quadGeometry.AddVertexBuffer( quadVertices );
2834 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
2836 if( !mShaderBackground )
2838 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
2841 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
2842 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
2843 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
2845 actor = Actor::New();
2846 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
2847 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
2848 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
2849 actor.SetProperty( Actor::Property::SIZE, textSize );
2850 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
2851 actor.AddRenderer( renderer );
2860 } // namespace Toolkit