2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/color-segmentation.h>
32 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
45 * @brief Struct used to calculate the selection box.
47 struct SelectionBoxInfo
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
59 const float MAX_FLOAT = std::numeric_limits<float>::max();
60 const float MIN_FLOAT = std::numeric_limits<float>::min();
61 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
63 #define MAKE_SHADER(A)#A
65 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
66 attribute mediump vec2 aPosition;
67 attribute mediump vec4 aColor;
68 varying mediump vec4 vColor;
69 uniform highp mat4 uMvpMatrix;
73 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
74 gl_Position = uMvpMatrix * position;
79 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
80 varying mediump vec4 vColor;
81 uniform lowp vec4 uColor;
85 gl_FragColor = vColor * uColor;
89 struct BackgroundVertex
91 Vector2 mPosition; ///< Vertex posiiton
92 Vector4 mColor; ///< Vertex color
97 Vector< BackgroundVertex > mVertices; ///< container of vertices
98 Vector< unsigned short > mIndices; ///< container of indices
101 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
102 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
103 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
104 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
105 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
118 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
119 : mDecorator( decorator ),
120 mInputMethodContext( inputMethodContext ),
121 mPlaceholderFont( NULL ),
122 mPlaceholderTextActive(),
123 mPlaceholderTextInactive(),
124 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
126 mInputStyleChangedQueue(),
127 mPreviousState( INACTIVE ),
129 mPrimaryCursorPosition( 0u ),
130 mLeftSelectionPosition( 0u ),
131 mRightSelectionPosition( 0u ),
132 mPreEditStartPosition( 0u ),
133 mPreEditLength( 0u ),
134 mCursorHookPositionX( 0.f ),
135 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
136 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
137 mIsShowingPlaceholderText( false ),
138 mPreEditFlag( false ),
139 mDecoratorUpdated( false ),
140 mCursorBlinkEnabled( true ),
141 mGrabHandleEnabled( true ),
142 mGrabHandlePopupEnabled( true ),
143 mSelectionEnabled( true ),
144 mUpdateCursorHookPosition( false ),
145 mUpdateCursorPosition( false ),
146 mUpdateGrabHandlePosition( false ),
147 mUpdateLeftSelectionPosition( false ),
148 mUpdateRightSelectionPosition( false ),
149 mIsLeftHandleSelected( false ),
150 mIsRightHandleSelected( false ),
151 mUpdateHighlightBox( false ),
152 mScrollAfterUpdatePosition( false ),
153 mScrollAfterDelete( false ),
154 mAllTextSelected( false ),
155 mUpdateInputStyle( false ),
156 mPasswordInput( false ),
157 mCheckScrollAmount( false ),
158 mIsPlaceholderPixelSize( false ),
159 mIsPlaceholderElideEnabled( false ),
160 mPlaceholderEllipsisFlag( false ),
161 mShiftSelectionFlag( true ),
162 mUpdateAlignment( false ),
163 mEditingEnabled( true )
167 EventData::~EventData()
170 bool Controller::Impl::ProcessInputEvents()
172 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
173 if( NULL == mEventData )
175 // Nothing to do if there is no text input.
176 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
180 if( mEventData->mDecorator )
182 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
183 iter != mEventData->mEventQueue.end();
188 case Event::CURSOR_KEY_EVENT:
190 OnCursorKeyEvent( *iter );
193 case Event::TAP_EVENT:
198 case Event::LONG_PRESS_EVENT:
200 OnLongPressEvent( *iter );
203 case Event::PAN_EVENT:
208 case Event::GRAB_HANDLE_EVENT:
209 case Event::LEFT_SELECTION_HANDLE_EVENT:
210 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
212 OnHandleEvent( *iter );
217 OnSelectEvent( *iter );
220 case Event::SELECT_ALL:
225 case Event::SELECT_NONE:
234 if( mEventData->mUpdateCursorPosition ||
235 mEventData->mUpdateHighlightBox )
237 NotifyInputMethodContext();
240 // The cursor must also be repositioned after inserts into the model
241 if( mEventData->mUpdateCursorPosition )
243 // Updates the cursor position and scrolls the text to make it visible.
244 CursorInfo cursorInfo;
245 // Calculate the cursor position from the new cursor index.
246 GetCursorPosition( mEventData->mPrimaryCursorPosition,
249 if( mEventData->mUpdateCursorHookPosition )
251 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
252 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
253 mEventData->mUpdateCursorHookPosition = false;
256 // Scroll first the text after delete ...
257 if( mEventData->mScrollAfterDelete )
259 ScrollTextToMatchCursor( cursorInfo );
262 // ... then, text can be scrolled to make the cursor visible.
263 if( mEventData->mScrollAfterUpdatePosition )
265 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
266 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
268 mEventData->mScrollAfterUpdatePosition = false;
269 mEventData->mScrollAfterDelete = false;
271 UpdateCursorPosition( cursorInfo );
273 mEventData->mDecoratorUpdated = true;
274 mEventData->mUpdateCursorPosition = false;
275 mEventData->mUpdateGrabHandlePosition = false;
279 CursorInfo leftHandleInfo;
280 CursorInfo rightHandleInfo;
282 if( mEventData->mUpdateHighlightBox )
284 GetCursorPosition( mEventData->mLeftSelectionPosition,
287 GetCursorPosition( mEventData->mRightSelectionPosition,
290 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
292 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
294 CursorInfo& infoLeft = leftHandleInfo;
296 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
297 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
299 CursorInfo& infoRight = rightHandleInfo;
301 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
302 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
306 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
308 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
309 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
314 if( mEventData->mUpdateLeftSelectionPosition )
316 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
320 mEventData->mDecoratorUpdated = true;
321 mEventData->mUpdateLeftSelectionPosition = false;
324 if( mEventData->mUpdateRightSelectionPosition )
326 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
330 mEventData->mDecoratorUpdated = true;
331 mEventData->mUpdateRightSelectionPosition = false;
334 if( mEventData->mUpdateHighlightBox )
336 RepositionSelectionHandles();
338 mEventData->mUpdateLeftSelectionPosition = false;
339 mEventData->mUpdateRightSelectionPosition = false;
340 mEventData->mUpdateHighlightBox = false;
341 mEventData->mIsLeftHandleSelected = false;
342 mEventData->mIsRightHandleSelected = false;
345 mEventData->mScrollAfterUpdatePosition = false;
348 if( mEventData->mUpdateInputStyle )
350 // Keep a copy of the current input style.
351 InputStyle currentInputStyle;
352 currentInputStyle.Copy( mEventData->mInputStyle );
354 // Set the default style first.
355 RetrieveDefaultInputStyle( mEventData->mInputStyle );
357 // Get the character index from the cursor index.
358 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
360 // Retrieve the style from the style runs stored in the logical model.
361 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
363 // Compare if the input style has changed.
364 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
366 if( hasInputStyleChanged )
368 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
369 // Queue the input style changed signal.
370 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
373 mEventData->mUpdateInputStyle = false;
376 mEventData->mEventQueue.clear();
378 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
380 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
381 mEventData->mDecoratorUpdated = false;
383 return decoratorUpdated;
386 void Controller::Impl::NotifyInputMethodContext()
388 if( mEventData && mEventData->mInputMethodContext )
390 CharacterIndex cursorPosition = GetLogicalCursorPosition();
392 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
394 // Update the cursor position by removing the initial white spaces.
395 if( cursorPosition < numberOfWhiteSpaces )
401 cursorPosition -= numberOfWhiteSpaces;
404 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
405 mEventData->mInputMethodContext.NotifyCursorPosition();
409 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
411 if ( mEventData && mEventData->mInputMethodContext )
413 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
414 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
418 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
420 CharacterIndex cursorPosition = 0u;
424 if( ( EventData::SELECTING == mEventData->mState ) ||
425 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
427 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
431 cursorPosition = mEventData->mPrimaryCursorPosition;
435 return cursorPosition;
438 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
440 Length numberOfWhiteSpaces = 0u;
442 // Get the buffer to the text.
443 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
445 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
446 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
448 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
454 return numberOfWhiteSpaces;
457 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
459 // Get the total number of characters.
460 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
462 // Retrieve the text.
463 if( 0u != numberOfCharacters )
465 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
469 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
471 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
472 mTextUpdateInfo.mStartGlyphIndex = 0u;
473 mTextUpdateInfo.mStartLineIndex = 0u;
474 numberOfCharacters = 0u;
476 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
477 if( 0u == numberOfParagraphs )
479 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
480 numberOfCharacters = 0u;
482 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
484 // Nothing else to do if there are no paragraphs.
488 // Find the paragraphs to be updated.
489 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
490 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
492 // Text is being added at the end of the current text.
493 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
495 // Text is being added in a new paragraph after the last character of the text.
496 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
497 numberOfCharacters = 0u;
498 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
500 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
501 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
503 // Nothing else to do;
507 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
511 Length numberOfCharactersToUpdate = 0u;
512 if( mTextUpdateInfo.mFullRelayoutNeeded )
514 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
518 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
520 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
521 numberOfCharactersToUpdate,
522 paragraphsToBeUpdated );
525 if( 0u != paragraphsToBeUpdated.Count() )
527 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
528 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
529 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
531 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
532 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
534 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
535 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
536 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
537 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
539 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
540 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
542 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
546 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
550 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
551 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
554 void Controller::Impl::ClearFullModelData( OperationsMask operations )
556 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
558 mModel->mLogicalModel->mLineBreakInfo.Clear();
559 mModel->mLogicalModel->mParagraphInfo.Clear();
562 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
564 mModel->mLogicalModel->mScriptRuns.Clear();
567 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
569 mModel->mLogicalModel->mFontRuns.Clear();
572 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
574 if( NO_OPERATION != ( BIDI_INFO & operations ) )
576 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
577 mModel->mLogicalModel->mCharacterDirections.Clear();
580 if( NO_OPERATION != ( REORDER & operations ) )
582 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
583 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
584 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
588 BidirectionalLineInfoRun& bidiLineInfo = *it;
590 free( bidiLineInfo.visualToLogicalMap );
591 bidiLineInfo.visualToLogicalMap = NULL;
593 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
597 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
599 mModel->mVisualModel->mGlyphs.Clear();
600 mModel->mVisualModel->mGlyphsToCharacters.Clear();
601 mModel->mVisualModel->mCharactersToGlyph.Clear();
602 mModel->mVisualModel->mCharactersPerGlyph.Clear();
603 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
604 mModel->mVisualModel->mGlyphPositions.Clear();
607 if( NO_OPERATION != ( LAYOUT & operations ) )
609 mModel->mVisualModel->mLines.Clear();
612 if( NO_OPERATION != ( COLOR & operations ) )
614 mModel->mVisualModel->mColorIndices.Clear();
615 mModel->mVisualModel->mBackgroundColorIndices.Clear();
619 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
621 const CharacterIndex endIndexPlusOne = endIndex + 1u;
623 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
625 // Clear the line break info.
626 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
628 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
629 lineBreakInfoBuffer + endIndexPlusOne );
631 // Clear the paragraphs.
632 ClearCharacterRuns( startIndex,
634 mModel->mLogicalModel->mParagraphInfo );
637 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
639 // Clear the scripts.
640 ClearCharacterRuns( startIndex,
642 mModel->mLogicalModel->mScriptRuns );
645 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
648 ClearCharacterRuns( startIndex,
650 mModel->mLogicalModel->mFontRuns );
653 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
655 if( NO_OPERATION != ( BIDI_INFO & operations ) )
657 // Clear the bidirectional paragraph info.
658 ClearCharacterRuns( startIndex,
660 mModel->mLogicalModel->mBidirectionalParagraphInfo );
662 // Clear the character's directions.
663 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
665 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
666 characterDirectionsBuffer + endIndexPlusOne );
669 if( NO_OPERATION != ( REORDER & operations ) )
671 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
672 uint32_t endRemoveIndex = startRemoveIndex;
673 ClearCharacterRuns( startIndex,
675 mModel->mLogicalModel->mBidirectionalLineInfo,
679 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
681 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
682 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
683 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
687 BidirectionalLineInfoRun& bidiLineInfo = *it;
689 free( bidiLineInfo.visualToLogicalMap );
690 bidiLineInfo.visualToLogicalMap = NULL;
693 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
694 bidirectionalLineInfoBuffer + endRemoveIndex );
699 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
701 const CharacterIndex endIndexPlusOne = endIndex + 1u;
702 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
704 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
705 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
706 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
708 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
709 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
711 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
713 // Update the character to glyph indices.
714 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
715 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
719 CharacterIndex& index = *it;
720 index -= numberOfGlyphsRemoved;
723 // Clear the character to glyph conversion table.
724 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
725 charactersToGlyphBuffer + endIndexPlusOne );
727 // Clear the glyphs per character table.
728 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
729 glyphsPerCharacterBuffer + endIndexPlusOne );
731 // Clear the glyphs buffer.
732 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
733 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
734 glyphsBuffer + endGlyphIndexPlusOne );
736 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
738 // Update the glyph to character indices.
739 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
740 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
744 CharacterIndex& index = *it;
745 index -= numberOfCharactersRemoved;
748 // Clear the glyphs to characters buffer.
749 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
750 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
752 // Clear the characters per glyph buffer.
753 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
754 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
755 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
757 // Clear the positions buffer.
758 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
759 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
760 positionsBuffer + endGlyphIndexPlusOne );
763 if( NO_OPERATION != ( LAYOUT & operations ) )
766 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
767 uint32_t endRemoveIndex = startRemoveIndex;
768 ClearCharacterRuns( startIndex,
770 mModel->mVisualModel->mLines,
774 // Will update the glyph runs.
775 startRemoveIndex = mModel->mVisualModel->mLines.Count();
776 endRemoveIndex = startRemoveIndex;
777 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
778 endGlyphIndexPlusOne - 1u,
779 mModel->mVisualModel->mLines,
783 // Set the line index from where to insert the new laid-out lines.
784 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
786 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
787 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
788 linesBuffer + endRemoveIndex );
791 if( NO_OPERATION != ( COLOR & operations ) )
793 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
795 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
796 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
797 colorIndexBuffer + endGlyphIndexPlusOne );
800 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
802 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
803 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
804 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
809 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
811 if( mTextUpdateInfo.mClearAll ||
812 ( ( 0u == startIndex ) &&
813 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
815 ClearFullModelData( operations );
819 // Clear the model data related with characters.
820 ClearCharacterModelData( startIndex, endIndex, operations );
822 // Clear the model data related with glyphs.
823 ClearGlyphModelData( startIndex, endIndex, operations );
826 // The estimated number of lines. Used to avoid reallocations when layouting.
827 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
829 mModel->mVisualModel->ClearCaches();
832 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
834 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
836 // Calculate the operations to be done.
837 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
839 if( NO_OPERATION == operations )
841 // Nothing to do if no operations are pending and required.
845 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
846 Vector<Character> displayCharacters;
847 bool useHiddenText = false;
848 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
850 mHiddenInput->Substitute( srcCharacters,displayCharacters );
851 useHiddenText = true;
854 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
855 const Length numberOfCharacters = utf32Characters.Count();
857 // Index to the first character of the first paragraph to be updated.
858 CharacterIndex startIndex = 0u;
859 // Number of characters of the paragraphs to be removed.
860 Length paragraphCharacters = 0u;
862 CalculateTextUpdateIndices( paragraphCharacters );
864 // Check whether the indices for updating the text is valid
865 if ( numberOfCharacters > 0u &&
866 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
867 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
869 std::string currentText;
870 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
872 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
873 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
875 // Dump mTextUpdateInfo
876 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
877 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
878 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
879 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
880 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
881 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
882 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
883 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
884 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
885 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
886 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
887 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
888 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
893 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
895 if( mTextUpdateInfo.mClearAll ||
896 ( 0u != paragraphCharacters ) )
898 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
901 mTextUpdateInfo.mClearAll = false;
903 // Whether the model is updated.
904 bool updated = false;
906 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
907 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
909 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
911 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
912 // calculate the bidirectional info for each 'paragraph'.
913 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
914 // is not shaped together).
915 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
917 SetLineBreakInfo( utf32Characters,
919 requestedNumberOfCharacters,
922 // Create the paragraph info.
923 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
924 requestedNumberOfCharacters );
928 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
929 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
931 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
932 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
934 if( getScripts || validateFonts )
936 // Validates the fonts assigned by the application or assigns default ones.
937 // It makes sure all the characters are going to be rendered by the correct font.
938 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
942 // Retrieves the scripts used in the text.
943 multilanguageSupport.SetScripts( utf32Characters,
945 requestedNumberOfCharacters,
951 // Validate the fonts set through the mark-up string.
952 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
954 // Get the default font's description.
955 TextAbstraction::FontDescription defaultFontDescription;
956 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
958 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
960 // If the placeholder font is set specifically, only placeholder font is changed.
961 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
962 if( mEventData->mPlaceholderFont->sizeDefined )
964 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
967 else if( NULL != mFontDefaults )
969 // Set the normal font and the placeholder font.
970 defaultFontDescription = mFontDefaults->mFontDescription;
972 if( mTextFitEnabled )
974 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
978 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
982 // Validates the fonts. If there is a character with no assigned font it sets a default one.
983 // After this call, fonts are validated.
984 multilanguageSupport.ValidateFonts( utf32Characters,
987 defaultFontDescription,
990 requestedNumberOfCharacters,
996 Vector<Character> mirroredUtf32Characters;
997 bool textMirrored = false;
998 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
999 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1001 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1002 bidirectionalInfo.Reserve( numberOfParagraphs );
1004 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1005 SetBidirectionalInfo( utf32Characters,
1009 requestedNumberOfCharacters,
1011 mModel->mMatchSystemLanguageDirection,
1014 if( 0u != bidirectionalInfo.Count() )
1016 // Only set the character directions if there is right to left characters.
1017 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1018 GetCharactersDirection( bidirectionalInfo,
1021 requestedNumberOfCharacters,
1024 // This paragraph has right to left text. Some characters may need to be mirrored.
1025 // TODO: consider if the mirrored string can be stored as well.
1027 textMirrored = GetMirroredText( utf32Characters,
1031 requestedNumberOfCharacters,
1032 mirroredUtf32Characters );
1036 // There is no right to left characters. Clear the directions vector.
1037 mModel->mLogicalModel->mCharacterDirections.Clear();
1042 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1043 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1044 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1045 Vector<GlyphIndex> newParagraphGlyphs;
1046 newParagraphGlyphs.Reserve( numberOfParagraphs );
1048 const Length currentNumberOfGlyphs = glyphs.Count();
1049 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1051 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1053 ShapeText( textToShape,
1058 mTextUpdateInfo.mStartGlyphIndex,
1059 requestedNumberOfCharacters,
1061 glyphsToCharactersMap,
1063 newParagraphGlyphs );
1065 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1066 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1067 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1071 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1073 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1075 GlyphInfo* glyphsBuffer = glyphs.Begin();
1076 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1078 // Update the width and advance of all new paragraph characters.
1079 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1081 const GlyphIndex index = *it;
1082 GlyphInfo& glyph = *( glyphsBuffer + index );
1084 glyph.xBearing = 0.f;
1086 glyph.advance = 0.f;
1091 if( ( NULL != mEventData ) &&
1092 mEventData->mPreEditFlag &&
1093 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1095 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1096 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1097 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1099 // Check the type of preedit and run it.
1100 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1102 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1103 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1104 type = attrData.preeditType;
1106 // Check the number of commit characters for the start position.
1107 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1108 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1112 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1114 // Add the underline for the pre-edit text.
1115 GlyphRun underlineRun;
1116 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1117 underlineRun.numberOfGlyphs = numberOfIndices;
1118 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1121 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1123 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1124 ColorRun backgroundColorRun;
1125 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1126 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1127 backgroundColorRun.color = textColor;
1128 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1130 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1131 Vector<ColorRun> colorRuns;
1132 colorRuns.Resize( 1u );
1133 ColorRun& colorRun = *( colorRuns.Begin() );
1134 colorRun.color = backgroundColor;
1135 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1136 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1138 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1141 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1143 ColorRun backgroundColorRun;
1144 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1145 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1146 backgroundColorRun.color = LIGHT_BLUE;
1147 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1150 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1152 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1153 ColorRun backgroundColorRun;
1154 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1155 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1156 backgroundColorRun.color = BACKGROUND_SUB4;
1157 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1159 GlyphRun underlineRun;
1160 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1161 underlineRun.numberOfGlyphs = numberOfIndices;
1162 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1165 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1167 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1168 ColorRun backgroundColorRun;
1169 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1170 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1171 backgroundColorRun.color = BACKGROUND_SUB5;
1172 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1174 GlyphRun underlineRun;
1175 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1176 underlineRun.numberOfGlyphs = numberOfIndices;
1177 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1180 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1182 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1183 ColorRun backgroundColorRun;
1184 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1185 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1186 backgroundColorRun.color = BACKGROUND_SUB6;
1187 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1189 GlyphRun underlineRun;
1190 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1191 underlineRun.numberOfGlyphs = numberOfIndices;
1192 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1195 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1197 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1198 ColorRun backgroundColorRun;
1199 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1200 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1201 backgroundColorRun.color = BACKGROUND_SUB7;
1202 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1204 GlyphRun underlineRun;
1205 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1206 underlineRun.numberOfGlyphs = numberOfIndices;
1207 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1210 case Dali::InputMethodContext::PreeditStyle::NONE:
1221 if( NO_OPERATION != ( COLOR & operations ) )
1223 // Set the color runs in glyphs.
1224 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1225 mModel->mVisualModel->mCharactersToGlyph,
1226 mModel->mVisualModel->mGlyphsPerCharacter,
1228 mTextUpdateInfo.mStartGlyphIndex,
1229 requestedNumberOfCharacters,
1230 mModel->mVisualModel->mColors,
1231 mModel->mVisualModel->mColorIndices );
1233 // Set the background color runs in glyphs.
1234 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1235 mModel->mVisualModel->mCharactersToGlyph,
1236 mModel->mVisualModel->mGlyphsPerCharacter,
1238 mTextUpdateInfo.mStartGlyphIndex,
1239 requestedNumberOfCharacters,
1240 mModel->mVisualModel->mBackgroundColors,
1241 mModel->mVisualModel->mBackgroundColorIndices );
1247 // The estimated number of lines. Used to avoid reallocations when layouting.
1248 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1250 // Set the previous number of characters for the next time the text is updated.
1251 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1256 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1258 // Sets the default text's color.
1259 inputStyle.textColor = mTextColor;
1260 inputStyle.isDefaultColor = true;
1262 inputStyle.familyName.clear();
1263 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1264 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1265 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1266 inputStyle.size = 0.f;
1268 inputStyle.lineSpacing = 0.f;
1270 inputStyle.underlineProperties.clear();
1271 inputStyle.shadowProperties.clear();
1272 inputStyle.embossProperties.clear();
1273 inputStyle.outlineProperties.clear();
1275 inputStyle.isFamilyDefined = false;
1276 inputStyle.isWeightDefined = false;
1277 inputStyle.isWidthDefined = false;
1278 inputStyle.isSlantDefined = false;
1279 inputStyle.isSizeDefined = false;
1281 inputStyle.isLineSpacingDefined = false;
1283 inputStyle.isUnderlineDefined = false;
1284 inputStyle.isShadowDefined = false;
1285 inputStyle.isEmbossDefined = false;
1286 inputStyle.isOutlineDefined = false;
1288 // Sets the default font's family name, weight, width, slant and size.
1291 if( mFontDefaults->familyDefined )
1293 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1294 inputStyle.isFamilyDefined = true;
1297 if( mFontDefaults->weightDefined )
1299 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1300 inputStyle.isWeightDefined = true;
1303 if( mFontDefaults->widthDefined )
1305 inputStyle.width = mFontDefaults->mFontDescription.width;
1306 inputStyle.isWidthDefined = true;
1309 if( mFontDefaults->slantDefined )
1311 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1312 inputStyle.isSlantDefined = true;
1315 if( mFontDefaults->sizeDefined )
1317 inputStyle.size = mFontDefaults->mDefaultPointSize;
1318 inputStyle.isSizeDefined = true;
1323 float Controller::Impl::GetDefaultFontLineHeight()
1325 FontId defaultFontId = 0u;
1326 if( NULL == mFontDefaults )
1328 TextAbstraction::FontDescription fontDescription;
1329 defaultFontId = mFontClient.GetFontId( fontDescription );
1333 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1336 Text::FontMetrics fontMetrics;
1337 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1339 return( fontMetrics.ascender - fontMetrics.descender );
1342 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1344 if( NULL == mEventData || !IsShowingRealText() )
1346 // Nothing to do if there is no text input.
1350 int keyCode = event.p1.mInt;
1351 bool isShiftModifier = event.p2.mBool;
1353 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1355 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1357 if( mEventData->mPrimaryCursorPosition > 0u )
1359 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1361 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1365 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1369 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1371 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1373 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1375 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1379 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1383 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1385 // Ignore Shift-Up for text selection for now.
1387 // Get first the line index of the current cursor position index.
1388 CharacterIndex characterIndex = 0u;
1390 if( mEventData->mPrimaryCursorPosition > 0u )
1392 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1395 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1396 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1398 // Retrieve the cursor position info.
1399 CursorInfo cursorInfo;
1400 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1403 // Get the line above.
1404 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1406 // Get the next hit 'y' point.
1407 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1409 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1410 bool matchedCharacter = false;
1411 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1412 mModel->mLogicalModel,
1414 mEventData->mCursorHookPositionX,
1416 CharacterHitTest::TAP,
1419 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1421 // Ignore Shift-Down for text selection for now.
1423 // Get first the line index of the current cursor position index.
1424 CharacterIndex characterIndex = 0u;
1426 if( mEventData->mPrimaryCursorPosition > 0u )
1428 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1431 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1433 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1435 // Retrieve the cursor position info.
1436 CursorInfo cursorInfo;
1437 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1440 // Get the line below.
1441 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1443 // Get the next hit 'y' point.
1444 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1446 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1447 bool matchedCharacter = false;
1448 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1449 mModel->mLogicalModel,
1451 mEventData->mCursorHookPositionX,
1453 CharacterHitTest::TAP,
1458 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1460 // Update selection position after moving the cursor
1461 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1462 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1465 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1467 // Handle text selection
1468 bool selecting = false;
1470 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1472 // Shift-Left/Right to select the text
1473 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1474 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1476 mEventData->mRightSelectionPosition += cursorPositionDelta;
1480 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1482 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1488 // Notify the cursor position to the InputMethodContext.
1489 if( mEventData->mInputMethodContext )
1491 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1492 mEventData->mInputMethodContext.NotifyCursorPosition();
1495 ChangeState( EventData::SELECTING );
1497 mEventData->mUpdateLeftSelectionPosition = true;
1498 mEventData->mUpdateRightSelectionPosition = true;
1499 mEventData->mUpdateGrabHandlePosition = true;
1500 mEventData->mUpdateHighlightBox = true;
1502 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1503 if( mEventData->mGrabHandlePopupEnabled )
1505 mEventData->mDecorator->SetPopupActive( false );
1511 // Handle normal cursor move
1512 ChangeState( EventData::EDITING );
1513 mEventData->mUpdateCursorPosition = true;
1516 mEventData->mUpdateInputStyle = true;
1517 mEventData->mScrollAfterUpdatePosition = true;
1520 void Controller::Impl::OnTapEvent( const Event& event )
1522 if( NULL != mEventData )
1524 const unsigned int tapCount = event.p1.mUint;
1526 if( 1u == tapCount )
1528 if( IsShowingRealText() )
1530 // Convert from control's coords to text's coords.
1531 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1532 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1534 // Keep the tap 'x' position. Used to move the cursor.
1535 mEventData->mCursorHookPositionX = xPosition;
1537 // Whether to touch point hits on a glyph.
1538 bool matchedCharacter = false;
1539 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1540 mModel->mLogicalModel,
1544 CharacterHitTest::TAP,
1547 // When the cursor position is changing, delay cursor blinking
1548 mEventData->mDecorator->DelayCursorBlink();
1552 mEventData->mPrimaryCursorPosition = 0u;
1555 // Update selection position after tapping
1556 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1557 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1559 mEventData->mUpdateCursorPosition = true;
1560 mEventData->mUpdateGrabHandlePosition = true;
1561 mEventData->mScrollAfterUpdatePosition = true;
1562 mEventData->mUpdateInputStyle = true;
1564 // Notify the cursor position to the InputMethodContext.
1565 if( mEventData->mInputMethodContext )
1567 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1568 mEventData->mInputMethodContext.NotifyCursorPosition();
1571 else if( 2u == tapCount )
1573 if( mEventData->mSelectionEnabled )
1575 // Convert from control's coords to text's coords.
1576 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1577 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1579 // Calculates the logical position from the x,y coords.
1580 RepositionSelectionHandles( xPosition,
1582 mEventData->mDoubleTapAction );
1588 void Controller::Impl::OnPanEvent( const Event& event )
1590 if( NULL == mEventData )
1592 // Nothing to do if there is no text input.
1596 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1597 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1599 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1601 // Nothing to do if scrolling is not enabled.
1605 const GestureState state = static_cast<GestureState>( event.p1.mInt );
1608 case GestureState::STARTED:
1610 // Will remove the cursor, handles or text's popup, ...
1611 ChangeState( EventData::TEXT_PANNING );
1614 case GestureState::CONTINUING:
1616 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1617 const Vector2 currentScroll = mModel->mScrollPosition;
1619 if( isHorizontalScrollEnabled )
1621 const float displacementX = event.p2.mFloat;
1622 mModel->mScrollPosition.x += displacementX;
1624 ClampHorizontalScroll( layoutSize );
1627 if( isVerticalScrollEnabled )
1629 const float displacementY = event.p3.mFloat;
1630 mModel->mScrollPosition.y += displacementY;
1632 ClampVerticalScroll( layoutSize );
1635 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1638 case GestureState::FINISHED:
1639 case GestureState::CANCELLED: // FALLTHROUGH
1641 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1642 ChangeState( mEventData->mPreviousState );
1650 void Controller::Impl::OnLongPressEvent( const Event& event )
1652 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1654 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1656 ChangeState( EventData::EDITING_WITH_POPUP );
1657 mEventData->mDecoratorUpdated = true;
1658 mEventData->mUpdateInputStyle = true;
1662 if( mEventData->mSelectionEnabled )
1664 // Convert from control's coords to text's coords.
1665 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1666 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1668 // Calculates the logical position from the x,y coords.
1669 RepositionSelectionHandles( xPosition,
1671 mEventData->mLongPressAction );
1676 void Controller::Impl::OnHandleEvent( const Event& event )
1678 if( NULL == mEventData )
1680 // Nothing to do if there is no text input.
1684 const unsigned int state = event.p1.mUint;
1685 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1686 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1688 if( HANDLE_PRESSED == state )
1690 // Convert from decorator's coords to text's coords.
1691 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1692 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1694 // Need to calculate the handle's new position.
1695 bool matchedCharacter = false;
1696 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1697 mModel->mLogicalModel,
1701 CharacterHitTest::SCROLL,
1704 if( Event::GRAB_HANDLE_EVENT == event.type )
1706 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1708 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1710 // Updates the cursor position if the handle's new position is different than the current one.
1711 mEventData->mUpdateCursorPosition = true;
1712 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1713 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1714 mEventData->mPrimaryCursorPosition = handleNewPosition;
1717 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1718 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1720 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1722 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1724 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1725 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1727 // Updates the highlight box if the handle's new position is different than the current one.
1728 mEventData->mUpdateHighlightBox = true;
1729 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1730 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1731 mEventData->mLeftSelectionPosition = handleNewPosition;
1734 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1735 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1737 // Will define the order to scroll the text to match the handle position.
1738 mEventData->mIsLeftHandleSelected = true;
1739 mEventData->mIsRightHandleSelected = false;
1741 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1743 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1745 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1746 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1748 // Updates the highlight box if the handle's new position is different than the current one.
1749 mEventData->mUpdateHighlightBox = true;
1750 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1751 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1752 mEventData->mRightSelectionPosition = handleNewPosition;
1755 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1756 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1758 // Will define the order to scroll the text to match the handle position.
1759 mEventData->mIsLeftHandleSelected = false;
1760 mEventData->mIsRightHandleSelected = true;
1762 } // end ( HANDLE_PRESSED == state )
1763 else if( ( HANDLE_RELEASED == state ) ||
1764 handleStopScrolling )
1766 CharacterIndex handlePosition = 0u;
1767 if( handleStopScrolling || isSmoothHandlePanEnabled )
1769 // Convert from decorator's coords to text's coords.
1770 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1771 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1773 bool matchedCharacter = false;
1774 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1775 mModel->mLogicalModel,
1779 CharacterHitTest::SCROLL,
1783 if( Event::GRAB_HANDLE_EVENT == event.type )
1785 mEventData->mUpdateCursorPosition = true;
1786 mEventData->mUpdateGrabHandlePosition = true;
1787 mEventData->mUpdateInputStyle = true;
1789 if( !IsClipboardEmpty() )
1791 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1794 if( handleStopScrolling || isSmoothHandlePanEnabled )
1796 mEventData->mScrollAfterUpdatePosition = true;
1797 mEventData->mPrimaryCursorPosition = handlePosition;
1800 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1802 ChangeState( EventData::SELECTING );
1804 mEventData->mUpdateHighlightBox = true;
1805 mEventData->mUpdateLeftSelectionPosition = true;
1806 mEventData->mUpdateRightSelectionPosition = true;
1808 if( handleStopScrolling || isSmoothHandlePanEnabled )
1810 mEventData->mScrollAfterUpdatePosition = true;
1812 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1813 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1815 mEventData->mLeftSelectionPosition = handlePosition;
1819 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1821 ChangeState( EventData::SELECTING );
1823 mEventData->mUpdateHighlightBox = true;
1824 mEventData->mUpdateRightSelectionPosition = true;
1825 mEventData->mUpdateLeftSelectionPosition = true;
1827 if( handleStopScrolling || isSmoothHandlePanEnabled )
1829 mEventData->mScrollAfterUpdatePosition = true;
1830 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1831 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1833 mEventData->mRightSelectionPosition = handlePosition;
1838 mEventData->mDecoratorUpdated = true;
1839 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1840 else if( HANDLE_SCROLLING == state )
1842 const float xSpeed = event.p2.mFloat;
1843 const float ySpeed = event.p3.mFloat;
1844 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1845 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1847 mModel->mScrollPosition.x += xSpeed;
1848 mModel->mScrollPosition.y += ySpeed;
1850 ClampHorizontalScroll( layoutSize );
1851 ClampVerticalScroll( layoutSize );
1853 bool endOfScroll = false;
1854 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1856 // Notify the decorator there is no more text to scroll.
1857 // The decorator won't send more scroll events.
1858 mEventData->mDecorator->NotifyEndOfScroll();
1859 // Still need to set the position of the handle.
1863 // Set the position of the handle.
1864 const bool scrollRightDirection = xSpeed > 0.f;
1865 const bool scrollBottomDirection = ySpeed > 0.f;
1866 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1867 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1869 if( Event::GRAB_HANDLE_EVENT == event.type )
1871 ChangeState( EventData::GRAB_HANDLE_PANNING );
1873 // Get the grab handle position in decorator coords.
1874 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1876 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1878 // Position the grag handle close to either the left or right edge.
1879 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1882 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1884 position.x = mEventData->mCursorHookPositionX;
1886 // Position the grag handle close to either the top or bottom edge.
1887 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1890 // Get the new handle position.
1891 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1892 bool matchedCharacter = false;
1893 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1894 mModel->mLogicalModel,
1896 position.x - mModel->mScrollPosition.x,
1897 position.y - mModel->mScrollPosition.y,
1898 CharacterHitTest::SCROLL,
1901 if( mEventData->mPrimaryCursorPosition != handlePosition )
1903 mEventData->mUpdateCursorPosition = true;
1904 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1905 mEventData->mScrollAfterUpdatePosition = true;
1906 mEventData->mPrimaryCursorPosition = handlePosition;
1908 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1910 // Updates the decorator if the soft handle panning is enabled.
1911 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1913 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1915 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1917 // Get the selection handle position in decorator coords.
1918 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1920 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1922 // Position the selection handle close to either the left or right edge.
1923 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1926 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1928 position.x = mEventData->mCursorHookPositionX;
1930 // Position the grag handle close to either the top or bottom edge.
1931 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1934 // Get the new handle position.
1935 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1936 bool matchedCharacter = false;
1937 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1938 mModel->mLogicalModel,
1940 position.x - mModel->mScrollPosition.x,
1941 position.y - mModel->mScrollPosition.y,
1942 CharacterHitTest::SCROLL,
1945 if( leftSelectionHandleEvent )
1947 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1949 if( differentHandles || endOfScroll )
1951 mEventData->mUpdateHighlightBox = true;
1952 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1953 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1954 mEventData->mLeftSelectionPosition = handlePosition;
1959 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1960 if( differentHandles || endOfScroll )
1962 mEventData->mUpdateHighlightBox = true;
1963 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1964 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1965 mEventData->mRightSelectionPosition = handlePosition;
1969 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1971 RepositionSelectionHandles();
1973 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1976 mEventData->mDecoratorUpdated = true;
1977 } // end ( HANDLE_SCROLLING == state )
1980 void Controller::Impl::OnSelectEvent( const Event& event )
1982 if( NULL == mEventData )
1984 // Nothing to do if there is no text.
1988 if( mEventData->mSelectionEnabled )
1990 // Convert from control's coords to text's coords.
1991 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1992 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1994 // Calculates the logical position from the x,y coords.
1995 RepositionSelectionHandles( xPosition,
1997 Controller::NoTextTap::HIGHLIGHT );
2001 void Controller::Impl::OnSelectAllEvent()
2003 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2005 if( NULL == mEventData )
2007 // Nothing to do if there is no text.
2011 if( mEventData->mSelectionEnabled )
2013 // Calculates the logical position from the start.
2014 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
2015 0.f - mModel->mScrollPosition.y,
2016 Controller::NoTextTap::HIGHLIGHT );
2018 mEventData->mLeftSelectionPosition = 0u;
2019 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
2023 void Controller::Impl::OnSelectNoneEvent()
2025 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2027 if( NULL == mEventData )
2029 // Nothing to do if there is no text.
2033 if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING)
2035 mEventData->mPrimaryCursorPosition = 0u;
2036 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
2037 ChangeState( EventData::INACTIVE );
2038 mEventData->mUpdateCursorPosition = true;
2039 mEventData->mUpdateInputStyle = true;
2040 mEventData->mScrollAfterUpdatePosition = true;
2044 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
2046 if( nullptr == mEventData )
2048 // Nothing to do if there is no text.
2052 if( mEventData->mSelectionEnabled && (pStart || pEnd))
2054 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
2058 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
2062 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
2065 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
2067 ChangeState( EventData::EDITING );
2068 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
2069 mEventData->mUpdateCursorPosition = true;
2073 ChangeState( EventData::SELECTING );
2074 mEventData->mUpdateHighlightBox = true;
2075 mEventData->mUpdateLeftSelectionPosition = true;
2076 mEventData->mUpdateRightSelectionPosition = true;
2081 Uint32Pair Controller::Impl::GetTextSelectionRange() const
2087 range.first = mEventData->mLeftSelectionPosition;
2088 range.second = mEventData->mRightSelectionPosition;
2094 bool Controller::Impl::IsEditable() const
2096 return mEventData && mEventData->mEditingEnabled;
2099 void Controller::Impl::SetEditable( bool editable )
2103 mEventData->mEditingEnabled = editable;
2107 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2109 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2111 // Nothing to select if handles are in the same place.
2112 selectedText.clear();
2116 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2118 //Get start and end position of selection
2119 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2120 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2122 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2123 const Length numberOfCharacters = utf32Characters.Count();
2125 // Validate the start and end selection points
2126 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2128 //Get text as a UTF8 string
2129 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2131 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2133 // Keep a copy of the current input style.
2134 InputStyle currentInputStyle;
2135 currentInputStyle.Copy( mEventData->mInputStyle );
2137 // Set as input style the style of the first deleted character.
2138 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2140 // Compare if the input style has changed.
2141 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2143 if( hasInputStyleChanged )
2145 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2146 // Queue the input style changed signal.
2147 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2150 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2152 // Mark the paragraphs to be updated.
2153 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2155 mTextUpdateInfo.mCharacterIndex = 0;
2156 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2157 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2158 mTextUpdateInfo.mClearAll = true;
2162 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2163 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2166 // Delete text between handles
2167 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2168 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2169 utf32Characters.Erase( first, last );
2171 // Will show the cursor at the first character of the selection.
2172 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2176 // Will show the cursor at the last character of the selection.
2177 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2180 mEventData->mDecoratorUpdated = true;
2184 void Controller::Impl::ShowClipboard()
2188 mClipboard.ShowClipboard();
2192 void Controller::Impl::HideClipboard()
2194 if( mClipboard && mClipboardHideEnabled )
2196 mClipboard.HideClipboard();
2200 void Controller::Impl::SetClipboardHideEnable(bool enable)
2202 mClipboardHideEnabled = enable;
2205 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2207 //Send string to clipboard
2208 return ( mClipboard && mClipboard.SetItem( source ) );
2211 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2213 std::string selectedText;
2214 RetrieveSelection( selectedText, deleteAfterSending );
2215 CopyStringToClipboard( selectedText );
2216 ChangeState( EventData::EDITING );
2219 void Controller::Impl::RequestGetTextFromClipboard()
2223 mClipboard.RequestItem();
2227 void Controller::Impl::RepositionSelectionHandles()
2229 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2230 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2232 if( selectionStart == selectionEnd )
2234 // Nothing to select if handles are in the same place.
2235 // So, deactive Highlight box.
2236 mEventData->mDecorator->SetHighlightActive( false );
2240 mEventData->mDecorator->ClearHighlights();
2242 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2243 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2244 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2245 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2246 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2247 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2248 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2250 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2251 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2252 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2254 // Swap the indices if the start is greater than the end.
2255 const bool indicesSwapped = selectionStart > selectionEnd;
2257 // Tell the decorator to flip the selection handles if needed.
2258 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2260 if( indicesSwapped )
2262 std::swap( selectionStart, selectionEnd );
2265 // Get the indices to the first and last selected glyphs.
2266 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2267 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2268 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2269 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2271 // Get the lines where the glyphs are laid-out.
2272 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2274 LineIndex lineIndex = 0u;
2275 Length numberOfLines = 0u;
2276 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2277 1u + glyphEnd - glyphStart,
2280 const LineIndex firstLineIndex = lineIndex;
2282 // Create the structure to store some selection box info.
2283 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2284 selectionBoxLinesInfo.Resize( numberOfLines );
2286 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2287 selectionBoxInfo->minX = MAX_FLOAT;
2288 selectionBoxInfo->maxX = MIN_FLOAT;
2290 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2291 float minHighlightX = std::numeric_limits<float>::max();
2292 float maxHighlightX = std::numeric_limits<float>::min();
2294 Vector2 highLightPosition; // The highlight position in decorator's coords.
2296 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2298 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2299 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2302 // Transform to decorator's (control) coords.
2303 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2305 lineRun += firstLineIndex;
2307 // The line height is the addition of the line ascender and the line descender.
2308 // However, the line descender has a negative value, hence the subtraction.
2309 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2311 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2313 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2314 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2315 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2317 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2318 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2319 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2321 // The number of quads of the selection box.
2322 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2323 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2325 // Count the actual number of quads.
2326 unsigned int actualNumberOfQuads = 0u;
2329 // Traverse the glyphs.
2330 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2332 const GlyphInfo& glyph = *( glyphsBuffer + index );
2333 const Vector2& position = *( positionsBuffer + index );
2335 if( splitStartGlyph )
2337 // 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.
2339 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2340 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2341 // Get the direction of the character.
2342 CharacterDirection isCurrentRightToLeft = false;
2343 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2345 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2348 // The end point could be in the middle of the ligature.
2349 // Calculate the number of characters selected.
2350 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2352 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2353 quad.y = selectionBoxInfo->lineOffset;
2354 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2355 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2357 // Store the min and max 'x' for each line.
2358 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2359 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2361 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2362 ++actualNumberOfQuads;
2364 splitStartGlyph = false;
2368 if( splitEndGlyph && ( index == glyphEnd ) )
2370 // 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.
2372 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2373 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2374 // Get the direction of the character.
2375 CharacterDirection isCurrentRightToLeft = false;
2376 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2378 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2381 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2383 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2384 quad.y = selectionBoxInfo->lineOffset;
2385 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2386 quad.w = quad.y + selectionBoxInfo->lineHeight;
2388 // Store the min and max 'x' for each line.
2389 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2390 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2392 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2394 ++actualNumberOfQuads;
2396 splitEndGlyph = false;
2400 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2401 quad.y = selectionBoxInfo->lineOffset;
2402 quad.z = quad.x + glyph.advance;
2403 quad.w = quad.y + selectionBoxInfo->lineHeight;
2405 // Store the min and max 'x' for each line.
2406 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2407 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2409 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2411 ++actualNumberOfQuads;
2413 // Whether to retrieve the next line.
2414 if( index == lastGlyphOfLine )
2417 if( lineIndex < firstLineIndex + numberOfLines )
2419 // Retrieve the next line.
2422 // Get the last glyph of the new line.
2423 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2425 // Keep the offset and height of the current selection box.
2426 const float currentLineOffset = selectionBoxInfo->lineOffset;
2427 const float currentLineHeight = selectionBoxInfo->lineHeight;
2429 // Get the selection box info for the next line.
2432 selectionBoxInfo->minX = MAX_FLOAT;
2433 selectionBoxInfo->maxX = MIN_FLOAT;
2435 // Update the line's vertical offset.
2436 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2438 // The line height is the addition of the line ascender and the line descender.
2439 // However, the line descender has a negative value, hence the subtraction.
2440 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2445 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2446 // The final width is calculated after 'boxifying' the selection.
2447 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2448 endIt = selectionBoxLinesInfo.End();
2452 const SelectionBoxInfo& info = *it;
2454 // Update the size of the highlighted text.
2455 highLightSize.height += info.lineHeight;
2456 minHighlightX = std::min( minHighlightX, info.minX );
2457 maxHighlightX = std::max( maxHighlightX, info.maxX );
2460 // Add extra geometry to 'boxify' the selection.
2462 if( 1u < numberOfLines )
2464 // Boxify the first line.
2465 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2466 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2468 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2469 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2474 quad.y = firstSelectionBoxLineInfo.lineOffset;
2475 quad.z = firstSelectionBoxLineInfo.minX;
2476 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2478 // Boxify at the beginning of the line.
2479 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2481 ++actualNumberOfQuads;
2483 // Update the size of the highlighted text.
2484 minHighlightX = 0.f;
2489 quad.x = firstSelectionBoxLineInfo.maxX;
2490 quad.y = firstSelectionBoxLineInfo.lineOffset;
2491 quad.z = mModel->mVisualModel->mControlSize.width;
2492 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2494 // Boxify at the end of the line.
2495 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2497 ++actualNumberOfQuads;
2499 // Update the size of the highlighted text.
2500 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2503 // Boxify the central lines.
2504 if( 2u < numberOfLines )
2506 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2507 endIt = selectionBoxLinesInfo.End() - 1u;
2511 const SelectionBoxInfo& info = *it;
2514 quad.y = info.lineOffset;
2516 quad.w = info.lineOffset + info.lineHeight;
2518 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2520 ++actualNumberOfQuads;
2523 quad.y = info.lineOffset;
2524 quad.z = mModel->mVisualModel->mControlSize.width;
2525 quad.w = info.lineOffset + info.lineHeight;
2527 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2529 ++actualNumberOfQuads;
2532 // Update the size of the highlighted text.
2533 minHighlightX = 0.f;
2534 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2537 // Boxify the last line.
2538 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2539 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2541 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2542 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2547 quad.y = lastSelectionBoxLineInfo.lineOffset;
2548 quad.z = lastSelectionBoxLineInfo.minX;
2549 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2551 // Boxify at the beginning of the line.
2552 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2554 ++actualNumberOfQuads;
2556 // Update the size of the highlighted text.
2557 minHighlightX = 0.f;
2562 quad.x = lastSelectionBoxLineInfo.maxX;
2563 quad.y = lastSelectionBoxLineInfo.lineOffset;
2564 quad.z = mModel->mVisualModel->mControlSize.width;
2565 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2567 // Boxify at the end of the line.
2568 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2570 ++actualNumberOfQuads;
2572 // Update the size of the highlighted text.
2573 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2577 // Set the actual number of quads.
2578 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2580 // Sets the highlight's size and position. In decorator's coords.
2581 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2582 highLightSize.width = maxHighlightX - minHighlightX;
2584 highLightPosition.x = minHighlightX;
2585 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2586 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2588 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2590 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2592 CursorInfo primaryCursorInfo;
2593 GetCursorPosition( mEventData->mLeftSelectionPosition,
2594 primaryCursorInfo );
2596 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2598 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2600 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2601 primaryCursorInfo.lineHeight );
2603 CursorInfo secondaryCursorInfo;
2604 GetCursorPosition( mEventData->mRightSelectionPosition,
2605 secondaryCursorInfo );
2607 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2609 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2610 secondaryPosition.x,
2611 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2612 secondaryCursorInfo.lineHeight );
2615 // Set the flag to update the decorator.
2616 mEventData->mDecoratorUpdated = true;
2619 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2621 if( NULL == mEventData )
2623 // Nothing to do if there is no text input.
2627 if( IsShowingPlaceholderText() )
2629 // Nothing to do if there is the place-holder text.
2633 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2634 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2635 if( ( 0 == numberOfGlyphs ) ||
2636 ( 0 == numberOfLines ) )
2638 // Nothing to do if there is no text.
2642 // Find which word was selected
2643 CharacterIndex selectionStart( 0 );
2644 CharacterIndex selectionEnd( 0 );
2645 CharacterIndex noTextHitIndex( 0 );
2646 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2647 mModel->mLogicalModel,
2654 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2656 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2658 ChangeState( EventData::SELECTING );
2660 mEventData->mLeftSelectionPosition = selectionStart;
2661 mEventData->mRightSelectionPosition = selectionEnd;
2663 mEventData->mUpdateLeftSelectionPosition = true;
2664 mEventData->mUpdateRightSelectionPosition = true;
2665 mEventData->mUpdateHighlightBox = true;
2667 // It may happen an InputMethodContext commit event arrives before the selection event
2668 // if the InputMethodContext is in pre-edit state. The commit event will set the
2669 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2670 // to false, the highlight box won't be updated.
2671 mEventData->mUpdateCursorPosition = false;
2673 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2675 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2676 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2678 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2680 // Nothing to select. i.e. a white space, out of bounds
2681 ChangeState( EventData::EDITING_WITH_POPUP );
2683 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2685 mEventData->mUpdateCursorPosition = true;
2686 mEventData->mUpdateGrabHandlePosition = true;
2687 mEventData->mScrollAfterUpdatePosition = true;
2688 mEventData->mUpdateInputStyle = true;
2690 else if( Controller::NoTextTap::NO_ACTION == action )
2692 // Nothing to select. i.e. a white space, out of bounds
2693 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2695 mEventData->mUpdateCursorPosition = true;
2696 mEventData->mUpdateGrabHandlePosition = true;
2697 mEventData->mScrollAfterUpdatePosition = true;
2698 mEventData->mUpdateInputStyle = true;
2702 void Controller::Impl::SetPopupButtons()
2705 * Sets the Popup buttons to be shown depending on State.
2707 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2709 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2712 bool isEditable = IsEditable();
2713 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2715 if( EventData::SELECTING == mEventData->mState )
2717 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2720 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2723 if( !IsClipboardEmpty() )
2727 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2729 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2732 if( !mEventData->mAllTextSelected )
2734 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2737 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2739 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2741 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2744 if( !IsClipboardEmpty() )
2748 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2750 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2753 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2755 if ( !IsClipboardEmpty() )
2759 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2761 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2765 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2768 void Controller::Impl::ChangeState( EventData::State newState )
2770 if( NULL == mEventData )
2772 // Nothing to do if there is no text input.
2776 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2778 if( mEventData->mState != newState )
2780 mEventData->mPreviousState = mEventData->mState;
2781 mEventData->mState = newState;
2783 switch( mEventData->mState )
2785 case EventData::INACTIVE:
2787 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2788 mEventData->mDecorator->StopCursorBlink();
2789 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2790 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2791 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2792 mEventData->mDecorator->SetHighlightActive( false );
2793 mEventData->mDecorator->SetPopupActive( false );
2794 mEventData->mDecoratorUpdated = true;
2797 case EventData::INTERRUPTED:
2799 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2800 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2801 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2802 mEventData->mDecorator->SetHighlightActive( false );
2803 mEventData->mDecorator->SetPopupActive( false );
2804 mEventData->mDecoratorUpdated = true;
2807 case EventData::SELECTING:
2809 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2810 mEventData->mDecorator->StopCursorBlink();
2811 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2812 if ( mEventData->mGrabHandleEnabled )
2814 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2815 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2817 mEventData->mDecorator->SetHighlightActive( true );
2818 if( mEventData->mGrabHandlePopupEnabled )
2821 mEventData->mDecorator->SetPopupActive( true );
2823 mEventData->mDecoratorUpdated = true;
2826 case EventData::EDITING:
2828 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2829 if( mEventData->mCursorBlinkEnabled )
2831 mEventData->mDecorator->StartCursorBlink();
2833 // Grab handle is not shown until a tap is received whilst EDITING
2834 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2835 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2836 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2837 mEventData->mDecorator->SetHighlightActive( false );
2838 if( mEventData->mGrabHandlePopupEnabled )
2840 mEventData->mDecorator->SetPopupActive( false );
2842 mEventData->mDecoratorUpdated = true;
2845 case EventData::EDITING_WITH_POPUP:
2847 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2849 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2850 if( mEventData->mCursorBlinkEnabled )
2852 mEventData->mDecorator->StartCursorBlink();
2854 if( mEventData->mSelectionEnabled )
2856 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2857 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2858 mEventData->mDecorator->SetHighlightActive( false );
2860 else if ( mEventData->mGrabHandleEnabled )
2862 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2864 if( mEventData->mGrabHandlePopupEnabled )
2867 mEventData->mDecorator->SetPopupActive( true );
2869 mEventData->mDecoratorUpdated = true;
2872 case EventData::EDITING_WITH_GRAB_HANDLE:
2874 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2876 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2877 if( mEventData->mCursorBlinkEnabled )
2879 mEventData->mDecorator->StartCursorBlink();
2881 // Grab handle is not shown until a tap is received whilst EDITING
2882 if ( mEventData->mGrabHandleEnabled )
2884 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2886 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2887 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2888 mEventData->mDecorator->SetHighlightActive( false );
2889 if( mEventData->mGrabHandlePopupEnabled )
2891 mEventData->mDecorator->SetPopupActive( false );
2893 mEventData->mDecoratorUpdated = true;
2896 case EventData::SELECTION_HANDLE_PANNING:
2898 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2899 mEventData->mDecorator->StopCursorBlink();
2900 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2901 if ( mEventData->mGrabHandleEnabled )
2903 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2904 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2906 mEventData->mDecorator->SetHighlightActive( true );
2907 if( mEventData->mGrabHandlePopupEnabled )
2909 mEventData->mDecorator->SetPopupActive( false );
2911 mEventData->mDecoratorUpdated = true;
2914 case EventData::GRAB_HANDLE_PANNING:
2916 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2918 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2919 if( mEventData->mCursorBlinkEnabled )
2921 mEventData->mDecorator->StartCursorBlink();
2923 if ( mEventData->mGrabHandleEnabled )
2925 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2927 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2928 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2929 mEventData->mDecorator->SetHighlightActive( false );
2930 if( mEventData->mGrabHandlePopupEnabled )
2932 mEventData->mDecorator->SetPopupActive( false );
2934 mEventData->mDecoratorUpdated = true;
2937 case EventData::EDITING_WITH_PASTE_POPUP:
2939 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2941 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2942 if( mEventData->mCursorBlinkEnabled )
2944 mEventData->mDecorator->StartCursorBlink();
2947 if ( mEventData->mGrabHandleEnabled )
2949 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2951 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2952 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2953 mEventData->mDecorator->SetHighlightActive( false );
2955 if( mEventData->mGrabHandlePopupEnabled )
2958 mEventData->mDecorator->SetPopupActive( true );
2960 mEventData->mDecoratorUpdated = true;
2963 case EventData::TEXT_PANNING:
2965 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2966 mEventData->mDecorator->StopCursorBlink();
2967 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2968 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2969 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2971 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2972 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2973 mEventData->mDecorator->SetHighlightActive( true );
2976 if( mEventData->mGrabHandlePopupEnabled )
2978 mEventData->mDecorator->SetPopupActive( false );
2981 mEventData->mDecoratorUpdated = true;
2988 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2989 CursorInfo& cursorInfo )
2991 if( !IsShowingRealText() )
2993 // Do not want to use the place-holder text to set the cursor position.
2995 // Use the line's height of the font's family set to set the cursor's size.
2996 // If there is no font's family set, use the default font.
2997 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2999 cursorInfo.lineOffset = 0.f;
3000 cursorInfo.lineHeight = GetDefaultFontLineHeight();
3001 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
3004 if( mModel->mMatchSystemLanguageDirection )
3006 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
3009 switch( mModel->mHorizontalAlignment )
3011 case Text::HorizontalAlignment::BEGIN :
3015 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
3019 cursorInfo.primaryPosition.x = 0.f;
3023 case Text::HorizontalAlignment::CENTER:
3025 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
3028 case Text::HorizontalAlignment::END:
3032 cursorInfo.primaryPosition.x = 0.f;
3036 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
3042 // Nothing else to do.
3046 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
3047 GetCursorPositionParameters parameters;
3048 parameters.visualModel = mModel->mVisualModel;
3049 parameters.logicalModel = mModel->mLogicalModel;
3050 parameters.metrics = mMetrics;
3051 parameters.logical = logical;
3052 parameters.isMultiline = isMultiLine;
3054 Text::GetCursorPosition( parameters,
3057 // Adds Outline offset.
3058 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
3059 cursorInfo.primaryPosition.x += outlineWidth;
3060 cursorInfo.primaryPosition.y += outlineWidth;
3061 cursorInfo.secondaryPosition.x += outlineWidth;
3062 cursorInfo.secondaryPosition.y += outlineWidth;
3066 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
3068 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
3069 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
3071 if( 0.f > cursorInfo.primaryPosition.x )
3073 cursorInfo.primaryPosition.x = 0.f;
3076 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
3077 if( cursorInfo.primaryPosition.x > edgeWidth )
3079 cursorInfo.primaryPosition.x = edgeWidth;
3084 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
3086 if( NULL == mEventData )
3088 // Nothing to do if there is no text input.
3092 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
3094 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
3095 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
3097 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
3098 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3100 if( numberOfCharacters > 1u )
3102 const Script script = mModel->mLogicalModel->GetScript( index );
3103 if( HasLigatureMustBreak( script ) )
3105 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3106 numberOfCharacters = 1u;
3111 while( 0u == numberOfCharacters )
3114 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3118 if( index < mEventData->mPrimaryCursorPosition )
3120 cursorIndex -= numberOfCharacters;
3124 cursorIndex += numberOfCharacters;
3127 // Will update the cursor hook position.
3128 mEventData->mUpdateCursorHookPosition = true;
3133 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3135 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3136 if( NULL == mEventData )
3138 // Nothing to do if there is no text input.
3139 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3143 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3145 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3147 // Sets the cursor position.
3148 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3151 cursorInfo.primaryCursorHeight,
3152 cursorInfo.lineHeight );
3153 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3155 if( mEventData->mUpdateGrabHandlePosition )
3157 // Sets the grab handle position.
3158 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3160 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3161 cursorInfo.lineHeight );
3164 if( cursorInfo.isSecondaryCursor )
3166 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3167 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3168 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3169 cursorInfo.secondaryCursorHeight,
3170 cursorInfo.lineHeight );
3171 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3174 // Set which cursors are active according the state.
3175 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3177 if( cursorInfo.isSecondaryCursor )
3179 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3183 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3188 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3191 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3194 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3195 const CursorInfo& cursorInfo )
3197 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3198 ( RIGHT_SELECTION_HANDLE != handleType ) )
3203 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3205 // Sets the handle's position.
3206 mEventData->mDecorator->SetPosition( handleType,
3208 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3209 cursorInfo.lineHeight );
3211 // If selection handle at start of the text and other at end of the text then all text is selected.
3212 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3213 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3214 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3217 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3219 // Clamp between -space & -alignment offset.
3221 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3223 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3224 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3225 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3227 mEventData->mDecoratorUpdated = true;
3231 mModel->mScrollPosition.x = 0.f;
3235 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3237 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3239 // Nothing to do if the text is single line.
3243 // Clamp between -space & 0.
3244 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3246 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3247 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3248 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3250 mEventData->mDecoratorUpdated = true;
3254 mModel->mScrollPosition.y = 0.f;
3258 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3260 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3262 // position is in actor's coords.
3263 const float positionEndX = position.x + cursorWidth;
3264 const float positionEndY = position.y + lineHeight;
3266 // Transform the position to decorator coords.
3267 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3268 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3270 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3271 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3273 if( decoratorPositionBeginX < 0.f )
3275 mModel->mScrollPosition.x = -position.x;
3277 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3279 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3282 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3284 if( decoratorPositionBeginY < 0.f )
3286 mModel->mScrollPosition.y = -position.y;
3288 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3290 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3295 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3297 // Get the current cursor position in decorator coords.
3298 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3300 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3304 // Calculate the offset to match the cursor position before the character was deleted.
3305 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3307 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3308 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3310 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3311 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3315 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3316 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3318 // Makes the new cursor position visible if needed.
3319 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3322 void Controller::Impl::RequestRelayout()
3324 if( NULL != mControlInterface )
3326 mControlInterface->RequestTextRelayout();
3330 Actor Controller::Impl::CreateBackgroundActor()
3332 // NOTE: Currently we only support background color for one line left-to-right text,
3333 // so the following calculation is based on one line left-to-right text only!
3337 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3338 if( numberOfGlyphs > 0u )
3340 Vector<GlyphInfo> glyphs;
3341 glyphs.Resize( numberOfGlyphs );
3343 Vector<Vector2> positions;
3344 positions.Resize( numberOfGlyphs );
3346 // Get the line where the glyphs are laid-out.
3347 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3348 float alignmentOffset = lineRun->alignmentOffset;
3349 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3355 glyphs.Resize( numberOfGlyphs );
3356 positions.Resize( numberOfGlyphs );
3358 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3359 const Vector2* const positionsBuffer = positions.Begin();
3361 BackgroundMesh mesh;
3362 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3363 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3365 const Vector2 textSize = mView.GetLayoutSize();
3367 const float offsetX = textSize.width * 0.5f;
3368 const float offsetY = textSize.height * 0.5f;
3370 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3371 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3372 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3375 uint32_t numberOfQuads = 0u;
3377 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3379 const GlyphInfo& glyph = *( glyphsBuffer + i );
3381 // Get the background color of the character.
3382 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3383 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3384 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3386 // Only create quads for glyphs with a background color
3387 if ( backgroundColor != Color::TRANSPARENT )
3389 const Vector2 position = *( positionsBuffer + i );
3391 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3393 quad.x = position.x;
3395 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3396 quad.w = textSize.height;
3398 else if ( i == 0u ) // The first glyph in the whole text
3400 quad.x = position.x;
3402 quad.z = quad.x - glyph.xBearing + glyph.advance;
3403 quad.w = textSize.height;
3405 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3407 quad.x = position.x - glyph.xBearing;
3409 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3410 quad.w = textSize.height;
3412 else // The glyph in the middle of the text
3414 quad.x = position.x - glyph.xBearing;
3416 quad.z = quad.x + glyph.advance;
3417 quad.w = textSize.height;
3420 BackgroundVertex vertex;
3423 vertex.mPosition.x = quad.x - offsetX;
3424 vertex.mPosition.y = quad.y - offsetY;
3425 vertex.mColor = backgroundColor;
3426 mesh.mVertices.PushBack( vertex );
3429 vertex.mPosition.x = quad.z - offsetX;
3430 vertex.mPosition.y = quad.y - offsetY;
3431 vertex.mColor = backgroundColor;
3432 mesh.mVertices.PushBack( vertex );
3435 vertex.mPosition.x = quad.x - offsetX;
3436 vertex.mPosition.y = quad.w - offsetY;
3437 vertex.mColor = backgroundColor;
3438 mesh.mVertices.PushBack( vertex );
3441 vertex.mPosition.x = quad.z - offsetX;
3442 vertex.mPosition.y = quad.w - offsetY;
3443 vertex.mColor = backgroundColor;
3444 mesh.mVertices.PushBack( vertex );
3446 // Six indices in counter clockwise winding
3447 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3448 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3449 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3450 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3451 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3452 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3458 // Only create the background actor if there are glyphs with background color
3459 if ( mesh.mVertices.Count() > 0u )
3461 Property::Map quadVertexFormat;
3462 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3463 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3465 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
3466 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3468 Geometry quadGeometry = Geometry::New();
3469 quadGeometry.AddVertexBuffer( quadVertices );
3470 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3472 if( !mShaderBackground )
3474 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3477 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3478 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3479 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3481 actor = Actor::New();
3482 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
3483 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
3484 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
3485 actor.SetProperty( Actor::Property::SIZE, textSize );
3486 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
3487 actor.AddRenderer( renderer );
3496 } // namespace Toolkit