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/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderFont( NULL ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false ),
109 mCheckScrollAmount( false ),
110 mIsPlaceholderPixelSize( false ),
111 mIsPlaceholderElideEnabled( false ),
112 mPlaceholderEllipsisFlag( false ),
113 mShiftSelectionFlag( true )
115 mImfManager = ImfManager::Get();
118 EventData::~EventData()
121 bool Controller::Impl::ProcessInputEvents()
123 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
124 if( NULL == mEventData )
126 // Nothing to do if there is no text input.
127 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
131 if( mEventData->mDecorator )
133 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
134 iter != mEventData->mEventQueue.end();
139 case Event::CURSOR_KEY_EVENT:
141 OnCursorKeyEvent( *iter );
144 case Event::TAP_EVENT:
149 case Event::LONG_PRESS_EVENT:
151 OnLongPressEvent( *iter );
154 case Event::PAN_EVENT:
159 case Event::GRAB_HANDLE_EVENT:
160 case Event::LEFT_SELECTION_HANDLE_EVENT:
161 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
163 OnHandleEvent( *iter );
168 OnSelectEvent( *iter );
171 case Event::SELECT_ALL:
180 if( mEventData->mUpdateCursorPosition ||
181 mEventData->mUpdateHighlightBox )
186 // The cursor must also be repositioned after inserts into the model
187 if( mEventData->mUpdateCursorPosition )
189 // Updates the cursor position and scrolls the text to make it visible.
190 CursorInfo cursorInfo;
191 // Calculate the cursor position from the new cursor index.
192 GetCursorPosition( mEventData->mPrimaryCursorPosition,
195 if( mEventData->mUpdateCursorHookPosition )
197 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
198 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
199 mEventData->mUpdateCursorHookPosition = false;
202 // Scroll first the text after delete ...
203 if( mEventData->mScrollAfterDelete )
205 ScrollTextToMatchCursor( cursorInfo );
208 // ... then, text can be scrolled to make the cursor visible.
209 if( mEventData->mScrollAfterUpdatePosition )
211 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
212 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
214 mEventData->mScrollAfterUpdatePosition = false;
215 mEventData->mScrollAfterDelete = false;
217 UpdateCursorPosition( cursorInfo );
219 mEventData->mDecoratorUpdated = true;
220 mEventData->mUpdateCursorPosition = false;
221 mEventData->mUpdateGrabHandlePosition = false;
225 CursorInfo leftHandleInfo;
226 CursorInfo rightHandleInfo;
228 if( mEventData->mUpdateHighlightBox )
230 GetCursorPosition( mEventData->mLeftSelectionPosition,
233 GetCursorPosition( mEventData->mRightSelectionPosition,
236 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
238 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
240 CursorInfo& infoLeft = leftHandleInfo;
242 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
243 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
245 CursorInfo& infoRight = rightHandleInfo;
247 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
248 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
252 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
254 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
255 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
260 if( mEventData->mUpdateLeftSelectionPosition )
262 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
266 mEventData->mDecoratorUpdated = true;
267 mEventData->mUpdateLeftSelectionPosition = false;
270 if( mEventData->mUpdateRightSelectionPosition )
272 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
276 mEventData->mDecoratorUpdated = true;
277 mEventData->mUpdateRightSelectionPosition = false;
280 if( mEventData->mUpdateHighlightBox )
282 RepositionSelectionHandles();
284 mEventData->mUpdateLeftSelectionPosition = false;
285 mEventData->mUpdateRightSelectionPosition = false;
286 mEventData->mUpdateHighlightBox = false;
287 mEventData->mIsLeftHandleSelected = false;
288 mEventData->mIsRightHandleSelected = false;
291 mEventData->mScrollAfterUpdatePosition = false;
294 if( mEventData->mUpdateInputStyle )
296 // Keep a copy of the current input style.
297 InputStyle currentInputStyle;
298 currentInputStyle.Copy( mEventData->mInputStyle );
300 // Set the default style first.
301 RetrieveDefaultInputStyle( mEventData->mInputStyle );
303 // Get the character index from the cursor index.
304 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
306 // Retrieve the style from the style runs stored in the logical model.
307 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
309 // Compare if the input style has changed.
310 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
312 if( hasInputStyleChanged )
314 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
315 // Queue the input style changed signal.
316 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
319 mEventData->mUpdateInputStyle = false;
322 mEventData->mEventQueue.clear();
324 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
326 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
327 mEventData->mDecoratorUpdated = false;
329 return decoratorUpdated;
332 void Controller::Impl::NotifyImfManager()
334 if( mEventData && mEventData->mImfManager )
336 CharacterIndex cursorPosition = GetLogicalCursorPosition();
338 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
340 // Update the cursor position by removing the initial white spaces.
341 if( cursorPosition < numberOfWhiteSpaces )
347 cursorPosition -= numberOfWhiteSpaces;
350 mEventData->mImfManager.SetCursorPosition( cursorPosition );
351 mEventData->mImfManager.NotifyCursorPosition();
355 void Controller::Impl::NotifyImfMultiLineStatus()
359 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
360 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
364 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
366 CharacterIndex cursorPosition = 0u;
370 if( ( EventData::SELECTING == mEventData->mState ) ||
371 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
373 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
377 cursorPosition = mEventData->mPrimaryCursorPosition;
381 return cursorPosition;
384 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
386 Length numberOfWhiteSpaces = 0u;
388 // Get the buffer to the text.
389 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
391 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
392 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
394 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
400 return numberOfWhiteSpaces;
403 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
405 // Get the total number of characters.
406 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
408 // Retrieve the text.
409 if( 0u != numberOfCharacters )
411 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
415 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
417 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
418 mTextUpdateInfo.mStartGlyphIndex = 0u;
419 mTextUpdateInfo.mStartLineIndex = 0u;
420 numberOfCharacters = 0u;
422 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
423 if( 0u == numberOfParagraphs )
425 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
426 numberOfCharacters = 0u;
428 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
429 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
431 // Nothing else to do if there are no paragraphs.
435 // Find the paragraphs to be updated.
436 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
437 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
439 // Text is being added at the end of the current text.
440 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
442 // Text is being added in a new paragraph after the last character of the text.
443 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
444 numberOfCharacters = 0u;
446 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
447 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
449 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
450 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
452 // Nothing else to do;
456 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
460 Length numberOfCharactersToUpdate = 0u;
461 if( mTextUpdateInfo.mFullRelayoutNeeded )
463 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
467 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
469 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
470 numberOfCharactersToUpdate,
471 paragraphsToBeUpdated );
474 if( 0u != paragraphsToBeUpdated.Count() )
476 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
477 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
478 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
480 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
481 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
483 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
484 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
485 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
486 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
488 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
489 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
491 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
495 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
499 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
500 if( numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove )
502 mTextUpdateInfo.mRequestedNumberOfCharacters = 0u;
506 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
508 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
511 void Controller::Impl::ClearFullModelData( OperationsMask operations )
513 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
515 mModel->mLogicalModel->mLineBreakInfo.Clear();
516 mModel->mLogicalModel->mParagraphInfo.Clear();
519 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
521 mModel->mLogicalModel->mLineBreakInfo.Clear();
524 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
526 mModel->mLogicalModel->mScriptRuns.Clear();
529 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
531 mModel->mLogicalModel->mFontRuns.Clear();
534 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
536 if( NO_OPERATION != ( BIDI_INFO & operations ) )
538 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
539 mModel->mLogicalModel->mCharacterDirections.Clear();
542 if( NO_OPERATION != ( REORDER & operations ) )
544 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
545 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
546 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
550 BidirectionalLineInfoRun& bidiLineInfo = *it;
552 free( bidiLineInfo.visualToLogicalMap );
553 bidiLineInfo.visualToLogicalMap = NULL;
555 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
559 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
561 mModel->mVisualModel->mGlyphs.Clear();
562 mModel->mVisualModel->mGlyphsToCharacters.Clear();
563 mModel->mVisualModel->mCharactersToGlyph.Clear();
564 mModel->mVisualModel->mCharactersPerGlyph.Clear();
565 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
566 mModel->mVisualModel->mGlyphPositions.Clear();
569 if( NO_OPERATION != ( LAYOUT & operations ) )
571 mModel->mVisualModel->mLines.Clear();
574 if( NO_OPERATION != ( COLOR & operations ) )
576 mModel->mVisualModel->mColorIndices.Clear();
580 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
582 const CharacterIndex endIndexPlusOne = endIndex + 1u;
584 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
586 // Clear the line break info.
587 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
589 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
590 lineBreakInfoBuffer + endIndexPlusOne );
592 // Clear the paragraphs.
593 ClearCharacterRuns( startIndex,
595 mModel->mLogicalModel->mParagraphInfo );
598 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
600 // Clear the word break info.
601 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
603 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
604 wordBreakInfoBuffer + endIndexPlusOne );
607 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
609 // Clear the scripts.
610 ClearCharacterRuns( startIndex,
612 mModel->mLogicalModel->mScriptRuns );
615 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
618 ClearCharacterRuns( startIndex,
620 mModel->mLogicalModel->mFontRuns );
623 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
625 if( NO_OPERATION != ( BIDI_INFO & operations ) )
627 // Clear the bidirectional paragraph info.
628 ClearCharacterRuns( startIndex,
630 mModel->mLogicalModel->mBidirectionalParagraphInfo );
632 // Clear the character's directions.
633 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
635 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
636 characterDirectionsBuffer + endIndexPlusOne );
639 if( NO_OPERATION != ( REORDER & operations ) )
641 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
642 uint32_t endRemoveIndex = startRemoveIndex;
643 ClearCharacterRuns( startIndex,
645 mModel->mLogicalModel->mBidirectionalLineInfo,
649 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
651 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
652 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
653 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
657 BidirectionalLineInfoRun& bidiLineInfo = *it;
659 free( bidiLineInfo.visualToLogicalMap );
660 bidiLineInfo.visualToLogicalMap = NULL;
663 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
664 bidirectionalLineInfoBuffer + endRemoveIndex );
669 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
671 const CharacterIndex endIndexPlusOne = endIndex + 1u;
672 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
674 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
675 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
676 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
678 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
679 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
681 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
683 // Update the character to glyph indices.
684 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
685 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
689 CharacterIndex& index = *it;
690 index -= numberOfGlyphsRemoved;
693 // Clear the character to glyph conversion table.
694 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
695 charactersToGlyphBuffer + endIndexPlusOne );
697 // Clear the glyphs per character table.
698 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
699 glyphsPerCharacterBuffer + endIndexPlusOne );
701 // Clear the glyphs buffer.
702 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
703 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
704 glyphsBuffer + endGlyphIndexPlusOne );
706 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
708 // Update the glyph to character indices.
709 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
710 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
714 CharacterIndex& index = *it;
715 index -= numberOfCharactersRemoved;
718 // Clear the glyphs to characters buffer.
719 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
720 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
722 // Clear the characters per glyph buffer.
723 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
724 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
725 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
727 // Clear the positions buffer.
728 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
729 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
730 positionsBuffer + endGlyphIndexPlusOne );
733 if( NO_OPERATION != ( LAYOUT & operations ) )
736 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
737 uint32_t endRemoveIndex = startRemoveIndex;
738 ClearCharacterRuns( startIndex,
740 mModel->mVisualModel->mLines,
744 // Will update the glyph runs.
745 startRemoveIndex = mModel->mVisualModel->mLines.Count();
746 endRemoveIndex = startRemoveIndex;
747 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
748 endGlyphIndexPlusOne - 1u,
749 mModel->mVisualModel->mLines,
753 // Set the line index from where to insert the new laid-out lines.
754 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
756 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
757 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
758 linesBuffer + endRemoveIndex );
761 if( NO_OPERATION != ( COLOR & operations ) )
763 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
765 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
766 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
767 colorIndexBuffer + endGlyphIndexPlusOne );
772 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
774 if( mTextUpdateInfo.mClearAll ||
775 ( ( 0u == startIndex ) &&
776 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
778 ClearFullModelData( operations );
782 // Clear the model data related with characters.
783 ClearCharacterModelData( startIndex, endIndex, operations );
785 // Clear the model data related with glyphs.
786 ClearGlyphModelData( startIndex, endIndex, operations );
789 // The estimated number of lines. Used to avoid reallocations when layouting.
790 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
792 mModel->mVisualModel->ClearCaches();
795 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
797 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
799 // Calculate the operations to be done.
800 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
802 if( NO_OPERATION == operations )
804 // Nothing to do if no operations are pending and required.
808 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
809 Vector<Character> displayCharacters;
810 bool useHiddenText = false;
811 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
813 mHiddenInput->Substitute( srcCharacters,displayCharacters );
814 useHiddenText = true;
817 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
818 const Length numberOfCharacters = utf32Characters.Count();
820 // Index to the first character of the first paragraph to be updated.
821 CharacterIndex startIndex = 0u;
822 // Number of characters of the paragraphs to be removed.
823 Length paragraphCharacters = 0u;
825 CalculateTextUpdateIndices( paragraphCharacters );
827 // Check whether the indices for updating the text is valid
828 if ( numberOfCharacters > 0u &&
829 ( mTextUpdateInfo.mParagraphCharacterIndex >= numberOfCharacters ||
830 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
832 std::string currentText;
833 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
835 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
836 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
838 // Dump mTextUpdateInfo
839 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
840 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
841 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
842 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
843 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
844 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
845 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
846 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
847 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
848 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
849 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
850 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
851 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
856 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
858 if( mTextUpdateInfo.mClearAll ||
859 ( 0u != paragraphCharacters ) )
861 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
864 mTextUpdateInfo.mClearAll = false;
866 // Whether the model is updated.
867 bool updated = false;
869 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
870 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
872 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
874 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
875 // calculate the bidirectional info for each 'paragraph'.
876 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
877 // is not shaped together).
878 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
880 SetLineBreakInfo( utf32Characters,
882 requestedNumberOfCharacters,
885 // Create the paragraph info.
886 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
887 requestedNumberOfCharacters );
891 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
892 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
894 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
895 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
897 SetWordBreakInfo( utf32Characters,
899 requestedNumberOfCharacters,
904 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
905 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
907 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
908 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
910 if( getScripts || validateFonts )
912 // Validates the fonts assigned by the application or assigns default ones.
913 // It makes sure all the characters are going to be rendered by the correct font.
914 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
918 // Retrieves the scripts used in the text.
919 multilanguageSupport.SetScripts( utf32Characters,
921 requestedNumberOfCharacters,
927 // Validate the fonts set through the mark-up string.
928 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
930 // Get the default font's description.
931 TextAbstraction::FontDescription defaultFontDescription;
932 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
934 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
936 // If the placeholder font is set specifically, only placeholder font is changed.
937 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
938 if( mEventData->mPlaceholderFont->sizeDefined )
940 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
943 else if( NULL != mFontDefaults )
945 // Set the normal font and the placeholder font.
946 defaultFontDescription = mFontDefaults->mFontDescription;
947 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
950 // Validates the fonts. If there is a character with no assigned font it sets a default one.
951 // After this call, fonts are validated.
952 multilanguageSupport.ValidateFonts( utf32Characters,
955 defaultFontDescription,
958 requestedNumberOfCharacters,
964 Vector<Character> mirroredUtf32Characters;
965 bool textMirrored = false;
966 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
967 if( NO_OPERATION != ( BIDI_INFO & operations ) )
969 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
970 bidirectionalInfo.Reserve( numberOfParagraphs );
972 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
973 SetBidirectionalInfo( utf32Characters,
977 requestedNumberOfCharacters,
980 if( 0u != bidirectionalInfo.Count() )
982 // Only set the character directions if there is right to left characters.
983 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
984 GetCharactersDirection( bidirectionalInfo,
987 requestedNumberOfCharacters,
990 // This paragraph has right to left text. Some characters may need to be mirrored.
991 // TODO: consider if the mirrored string can be stored as well.
993 textMirrored = GetMirroredText( utf32Characters,
997 requestedNumberOfCharacters,
998 mirroredUtf32Characters );
1002 // There is no right to left characters. Clear the directions vector.
1003 mModel->mLogicalModel->mCharacterDirections.Clear();
1008 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1009 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1010 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1011 Vector<GlyphIndex> newParagraphGlyphs;
1012 newParagraphGlyphs.Reserve( numberOfParagraphs );
1014 const Length currentNumberOfGlyphs = glyphs.Count();
1015 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1017 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1019 ShapeText( textToShape,
1024 mTextUpdateInfo.mStartGlyphIndex,
1025 requestedNumberOfCharacters,
1027 glyphsToCharactersMap,
1029 newParagraphGlyphs );
1031 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1032 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1033 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1037 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1039 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1041 GlyphInfo* glyphsBuffer = glyphs.Begin();
1042 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1044 // Update the width and advance of all new paragraph characters.
1045 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1047 const GlyphIndex index = *it;
1048 GlyphInfo& glyph = *( glyphsBuffer + index );
1050 glyph.xBearing = 0.f;
1052 glyph.advance = 0.f;
1057 if( NO_OPERATION != ( COLOR & operations ) )
1059 // Set the color runs in glyphs.
1060 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1061 mModel->mVisualModel->mCharactersToGlyph,
1062 mModel->mVisualModel->mGlyphsPerCharacter,
1064 mTextUpdateInfo.mStartGlyphIndex,
1065 requestedNumberOfCharacters,
1066 mModel->mVisualModel->mColors,
1067 mModel->mVisualModel->mColorIndices );
1072 if( ( NULL != mEventData ) &&
1073 mEventData->mPreEditFlag &&
1074 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1076 // Add the underline for the pre-edit text.
1077 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1078 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1080 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1081 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1082 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1083 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1085 GlyphRun underlineRun;
1086 underlineRun.glyphIndex = glyphStart;
1087 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1089 // TODO: At the moment the underline runs are only for pre-edit.
1090 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1093 // The estimated number of lines. Used to avoid reallocations when layouting.
1094 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1096 // Set the previous number of characters for the next time the text is updated.
1097 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1102 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1104 // Sets the default text's color.
1105 inputStyle.textColor = mTextColor;
1106 inputStyle.isDefaultColor = true;
1108 inputStyle.familyName.clear();
1109 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1110 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1111 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1112 inputStyle.size = 0.f;
1114 inputStyle.lineSpacing = 0.f;
1116 inputStyle.underlineProperties.clear();
1117 inputStyle.shadowProperties.clear();
1118 inputStyle.embossProperties.clear();
1119 inputStyle.outlineProperties.clear();
1121 inputStyle.isFamilyDefined = false;
1122 inputStyle.isWeightDefined = false;
1123 inputStyle.isWidthDefined = false;
1124 inputStyle.isSlantDefined = false;
1125 inputStyle.isSizeDefined = false;
1127 inputStyle.isLineSpacingDefined = false;
1129 inputStyle.isUnderlineDefined = false;
1130 inputStyle.isShadowDefined = false;
1131 inputStyle.isEmbossDefined = false;
1132 inputStyle.isOutlineDefined = false;
1134 // Sets the default font's family name, weight, width, slant and size.
1137 if( mFontDefaults->familyDefined )
1139 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1140 inputStyle.isFamilyDefined = true;
1143 if( mFontDefaults->weightDefined )
1145 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1146 inputStyle.isWeightDefined = true;
1149 if( mFontDefaults->widthDefined )
1151 inputStyle.width = mFontDefaults->mFontDescription.width;
1152 inputStyle.isWidthDefined = true;
1155 if( mFontDefaults->slantDefined )
1157 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1158 inputStyle.isSlantDefined = true;
1161 if( mFontDefaults->sizeDefined )
1163 inputStyle.size = mFontDefaults->mDefaultPointSize;
1164 inputStyle.isSizeDefined = true;
1169 float Controller::Impl::GetDefaultFontLineHeight()
1171 FontId defaultFontId = 0u;
1172 if( NULL == mFontDefaults )
1174 TextAbstraction::FontDescription fontDescription;
1175 defaultFontId = mFontClient.GetFontId( fontDescription );
1179 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1182 Text::FontMetrics fontMetrics;
1183 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1185 return( fontMetrics.ascender - fontMetrics.descender );
1188 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1190 if( NULL == mEventData )
1192 // Nothing to do if there is no text input.
1196 int keyCode = event.p1.mInt;
1197 bool isShiftModifier = event.p2.mBool;
1199 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1201 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1203 if( mEventData->mPrimaryCursorPosition > 0u )
1205 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1207 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1211 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1215 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1217 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1219 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1221 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1225 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1229 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1231 // Ignore Shift-Up for text selection for now.
1233 // Get first the line index of the current cursor position index.
1234 CharacterIndex characterIndex = 0u;
1236 if( mEventData->mPrimaryCursorPosition > 0u )
1238 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1241 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1242 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1244 // Retrieve the cursor position info.
1245 CursorInfo cursorInfo;
1246 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1249 // Get the line above.
1250 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1252 // Get the next hit 'y' point.
1253 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1255 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1256 bool matchedCharacter = false;
1257 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1258 mModel->mLogicalModel,
1260 mEventData->mCursorHookPositionX,
1262 CharacterHitTest::TAP,
1265 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1267 // Ignore Shift-Down for text selection for now.
1269 // Get first the line index of the current cursor position index.
1270 CharacterIndex characterIndex = 0u;
1272 if( mEventData->mPrimaryCursorPosition > 0u )
1274 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1277 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1279 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1281 // Retrieve the cursor position info.
1282 CursorInfo cursorInfo;
1283 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1286 // Get the line below.
1287 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1289 // Get the next hit 'y' point.
1290 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1292 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1293 bool matchedCharacter = false;
1294 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1295 mModel->mLogicalModel,
1297 mEventData->mCursorHookPositionX,
1299 CharacterHitTest::TAP,
1304 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1306 // Update selection position after moving the cursor
1307 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1308 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1311 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1313 // Handle text selection
1314 bool selecting = false;
1316 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1318 // Shift-Left/Right to select the text
1319 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1320 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1322 mEventData->mRightSelectionPosition += cursorPositionDelta;
1326 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1328 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1334 // Notify the cursor position to the imf manager.
1335 if( mEventData->mImfManager )
1337 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1338 mEventData->mImfManager.NotifyCursorPosition();
1341 ChangeState( EventData::SELECTING );
1343 mEventData->mUpdateLeftSelectionPosition = true;
1344 mEventData->mUpdateRightSelectionPosition = true;
1345 mEventData->mUpdateGrabHandlePosition = true;
1346 mEventData->mUpdateHighlightBox = true;
1348 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1349 if( mEventData->mGrabHandlePopupEnabled )
1351 mEventData->mDecorator->SetPopupActive( false );
1357 // Handle normal cursor move
1358 ChangeState( EventData::EDITING );
1359 mEventData->mUpdateCursorPosition = true;
1362 mEventData->mUpdateInputStyle = true;
1363 mEventData->mScrollAfterUpdatePosition = true;
1366 void Controller::Impl::OnTapEvent( const Event& event )
1368 if( NULL != mEventData )
1370 const unsigned int tapCount = event.p1.mUint;
1372 if( 1u == tapCount )
1374 if( IsShowingRealText() )
1376 // Convert from control's coords to text's coords.
1377 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1378 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1380 // Keep the tap 'x' position. Used to move the cursor.
1381 mEventData->mCursorHookPositionX = xPosition;
1383 // Whether to touch point hits on a glyph.
1384 bool matchedCharacter = false;
1385 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1386 mModel->mLogicalModel,
1390 CharacterHitTest::TAP,
1393 // When the cursor position is changing, delay cursor blinking
1394 mEventData->mDecorator->DelayCursorBlink();
1398 mEventData->mPrimaryCursorPosition = 0u;
1401 // Update selection position after tapping
1402 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1403 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1405 mEventData->mUpdateCursorPosition = true;
1406 mEventData->mUpdateGrabHandlePosition = true;
1407 mEventData->mScrollAfterUpdatePosition = true;
1408 mEventData->mUpdateInputStyle = true;
1410 // Notify the cursor position to the imf manager.
1411 if( mEventData->mImfManager )
1413 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1414 mEventData->mImfManager.NotifyCursorPosition();
1417 else if( 2u == tapCount )
1419 if( mEventData->mSelectionEnabled )
1421 // Convert from control's coords to text's coords.
1422 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1423 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1425 // Calculates the logical position from the x,y coords.
1426 RepositionSelectionHandles( xPosition,
1428 mEventData->mDoubleTapAction );
1434 void Controller::Impl::OnPanEvent( const Event& event )
1436 if( NULL == mEventData )
1438 // Nothing to do if there is no text input.
1442 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1443 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1445 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1447 // Nothing to do if scrolling is not enabled.
1451 const int state = event.p1.mInt;
1455 case Gesture::Started:
1457 // Will remove the cursor, handles or text's popup, ...
1458 ChangeState( EventData::TEXT_PANNING );
1461 case Gesture::Continuing:
1463 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1464 const Vector2 currentScroll = mModel->mScrollPosition;
1466 if( isHorizontalScrollEnabled )
1468 const float displacementX = event.p2.mFloat;
1469 mModel->mScrollPosition.x += displacementX;
1471 ClampHorizontalScroll( layoutSize );
1474 if( isVerticalScrollEnabled )
1476 const float displacementY = event.p3.mFloat;
1477 mModel->mScrollPosition.y += displacementY;
1479 ClampVerticalScroll( layoutSize );
1482 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1485 case Gesture::Finished:
1486 case Gesture::Cancelled: // FALLTHROUGH
1488 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1489 ChangeState( mEventData->mPreviousState );
1497 void Controller::Impl::OnLongPressEvent( const Event& event )
1499 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1501 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1503 ChangeState( EventData::EDITING_WITH_POPUP );
1504 mEventData->mDecoratorUpdated = true;
1505 mEventData->mUpdateInputStyle = true;
1509 if( mEventData->mSelectionEnabled )
1511 // Convert from control's coords to text's coords.
1512 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1513 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1515 // Calculates the logical position from the x,y coords.
1516 RepositionSelectionHandles( xPosition,
1518 mEventData->mLongPressAction );
1523 void Controller::Impl::OnHandleEvent( const Event& event )
1525 if( NULL == mEventData )
1527 // Nothing to do if there is no text input.
1531 const unsigned int state = event.p1.mUint;
1532 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1533 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1535 if( HANDLE_PRESSED == state )
1537 // Convert from decorator's coords to text's coords.
1538 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1539 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1541 // Need to calculate the handle's new position.
1542 bool matchedCharacter = false;
1543 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1544 mModel->mLogicalModel,
1548 CharacterHitTest::SCROLL,
1551 if( Event::GRAB_HANDLE_EVENT == event.type )
1553 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1555 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1557 // Updates the cursor position if the handle's new position is different than the current one.
1558 mEventData->mUpdateCursorPosition = true;
1559 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1560 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1561 mEventData->mPrimaryCursorPosition = handleNewPosition;
1564 // 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.
1565 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1567 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1569 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1571 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1572 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1574 // Updates the highlight box if the handle's new position is different than the current one.
1575 mEventData->mUpdateHighlightBox = true;
1576 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1577 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1578 mEventData->mLeftSelectionPosition = handleNewPosition;
1581 // 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.
1582 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1584 // Will define the order to scroll the text to match the handle position.
1585 mEventData->mIsLeftHandleSelected = true;
1586 mEventData->mIsRightHandleSelected = false;
1588 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1590 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1592 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1593 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1595 // Updates the highlight box if the handle's new position is different than the current one.
1596 mEventData->mUpdateHighlightBox = true;
1597 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1598 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1599 mEventData->mRightSelectionPosition = handleNewPosition;
1602 // 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.
1603 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1605 // Will define the order to scroll the text to match the handle position.
1606 mEventData->mIsLeftHandleSelected = false;
1607 mEventData->mIsRightHandleSelected = true;
1609 } // end ( HANDLE_PRESSED == state )
1610 else if( ( HANDLE_RELEASED == state ) ||
1611 handleStopScrolling )
1613 CharacterIndex handlePosition = 0u;
1614 if( handleStopScrolling || isSmoothHandlePanEnabled )
1616 // Convert from decorator's coords to text's coords.
1617 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1618 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1620 bool matchedCharacter = false;
1621 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1622 mModel->mLogicalModel,
1626 CharacterHitTest::SCROLL,
1630 if( Event::GRAB_HANDLE_EVENT == event.type )
1632 mEventData->mUpdateCursorPosition = true;
1633 mEventData->mUpdateGrabHandlePosition = true;
1634 mEventData->mUpdateInputStyle = true;
1636 if( !IsClipboardEmpty() )
1638 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1641 if( handleStopScrolling || isSmoothHandlePanEnabled )
1643 mEventData->mScrollAfterUpdatePosition = true;
1644 mEventData->mPrimaryCursorPosition = handlePosition;
1647 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1649 ChangeState( EventData::SELECTING );
1651 mEventData->mUpdateHighlightBox = true;
1652 mEventData->mUpdateLeftSelectionPosition = true;
1653 mEventData->mUpdateRightSelectionPosition = true;
1655 if( handleStopScrolling || isSmoothHandlePanEnabled )
1657 mEventData->mScrollAfterUpdatePosition = true;
1659 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1660 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1662 mEventData->mLeftSelectionPosition = handlePosition;
1666 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1668 ChangeState( EventData::SELECTING );
1670 mEventData->mUpdateHighlightBox = true;
1671 mEventData->mUpdateRightSelectionPosition = true;
1672 mEventData->mUpdateLeftSelectionPosition = true;
1674 if( handleStopScrolling || isSmoothHandlePanEnabled )
1676 mEventData->mScrollAfterUpdatePosition = true;
1677 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1678 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1680 mEventData->mRightSelectionPosition = handlePosition;
1685 mEventData->mDecoratorUpdated = true;
1686 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1687 else if( HANDLE_SCROLLING == state )
1689 const float xSpeed = event.p2.mFloat;
1690 const float ySpeed = event.p3.mFloat;
1691 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1692 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1694 mModel->mScrollPosition.x += xSpeed;
1695 mModel->mScrollPosition.y += ySpeed;
1697 ClampHorizontalScroll( layoutSize );
1698 ClampVerticalScroll( layoutSize );
1700 bool endOfScroll = false;
1701 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1703 // Notify the decorator there is no more text to scroll.
1704 // The decorator won't send more scroll events.
1705 mEventData->mDecorator->NotifyEndOfScroll();
1706 // Still need to set the position of the handle.
1710 // Set the position of the handle.
1711 const bool scrollRightDirection = xSpeed > 0.f;
1712 const bool scrollBottomDirection = ySpeed > 0.f;
1713 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1714 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1716 if( Event::GRAB_HANDLE_EVENT == event.type )
1718 ChangeState( EventData::GRAB_HANDLE_PANNING );
1720 // Get the grab handle position in decorator coords.
1721 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1723 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1725 // Position the grag handle close to either the left or right edge.
1726 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1729 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1731 position.x = mEventData->mCursorHookPositionX;
1733 // Position the grag handle close to either the top or bottom edge.
1734 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1737 // Get the new handle position.
1738 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1739 bool matchedCharacter = false;
1740 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1741 mModel->mLogicalModel,
1743 position.x - mModel->mScrollPosition.x,
1744 position.y - mModel->mScrollPosition.y,
1745 CharacterHitTest::SCROLL,
1748 if( mEventData->mPrimaryCursorPosition != handlePosition )
1750 mEventData->mUpdateCursorPosition = true;
1751 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1752 mEventData->mScrollAfterUpdatePosition = true;
1753 mEventData->mPrimaryCursorPosition = handlePosition;
1755 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1757 // Updates the decorator if the soft handle panning is enabled.
1758 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1760 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1762 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1764 // Get the selection handle position in decorator coords.
1765 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1767 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1769 // Position the selection handle close to either the left or right edge.
1770 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1773 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1775 position.x = mEventData->mCursorHookPositionX;
1777 // Position the grag handle close to either the top or bottom edge.
1778 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1781 // Get the new handle position.
1782 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1783 bool matchedCharacter = false;
1784 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1785 mModel->mLogicalModel,
1787 position.x - mModel->mScrollPosition.x,
1788 position.y - mModel->mScrollPosition.y,
1789 CharacterHitTest::SCROLL,
1792 if( leftSelectionHandleEvent )
1794 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1796 if( differentHandles || endOfScroll )
1798 mEventData->mUpdateHighlightBox = true;
1799 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1800 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1801 mEventData->mLeftSelectionPosition = handlePosition;
1806 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1807 if( differentHandles || endOfScroll )
1809 mEventData->mUpdateHighlightBox = true;
1810 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1811 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1812 mEventData->mRightSelectionPosition = handlePosition;
1816 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1818 RepositionSelectionHandles();
1820 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1823 mEventData->mDecoratorUpdated = true;
1824 } // end ( HANDLE_SCROLLING == state )
1827 void Controller::Impl::OnSelectEvent( const Event& event )
1829 if( NULL == mEventData )
1831 // Nothing to do if there is no text.
1835 if( mEventData->mSelectionEnabled )
1837 // Convert from control's coords to text's coords.
1838 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1839 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1841 // Calculates the logical position from the x,y coords.
1842 RepositionSelectionHandles( xPosition,
1844 Controller::NoTextTap::HIGHLIGHT );
1848 void Controller::Impl::OnSelectAllEvent()
1850 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1852 if( NULL == mEventData )
1854 // Nothing to do if there is no text.
1858 if( mEventData->mSelectionEnabled )
1860 ChangeState( EventData::SELECTING );
1862 mEventData->mLeftSelectionPosition = 0u;
1863 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1865 mEventData->mScrollAfterUpdatePosition = true;
1866 mEventData->mUpdateLeftSelectionPosition = true;
1867 mEventData->mUpdateRightSelectionPosition = true;
1868 mEventData->mUpdateHighlightBox = true;
1872 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1874 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1876 // Nothing to select if handles are in the same place.
1877 selectedText.clear();
1881 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1883 //Get start and end position of selection
1884 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1885 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1887 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1888 const Length numberOfCharacters = utf32Characters.Count();
1890 // Validate the start and end selection points
1891 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1893 //Get text as a UTF8 string
1894 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1896 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1898 // Keep a copy of the current input style.
1899 InputStyle currentInputStyle;
1900 currentInputStyle.Copy( mEventData->mInputStyle );
1902 // Set as input style the style of the first deleted character.
1903 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1905 // Compare if the input style has changed.
1906 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1908 if( hasInputStyleChanged )
1910 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1911 // Queue the input style changed signal.
1912 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1915 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1917 // Mark the paragraphs to be updated.
1918 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1920 mTextUpdateInfo.mCharacterIndex = 0;
1921 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1922 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1923 mTextUpdateInfo.mClearAll = true;
1927 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1928 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1931 // Delete text between handles
1932 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1933 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1934 utf32Characters.Erase( first, last );
1936 // Will show the cursor at the first character of the selection.
1937 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1941 // Will show the cursor at the last character of the selection.
1942 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1945 mEventData->mDecoratorUpdated = true;
1949 void Controller::Impl::ShowClipboard()
1953 mClipboard.ShowClipboard();
1957 void Controller::Impl::HideClipboard()
1959 if( mClipboard && mClipboardHideEnabled )
1961 mClipboard.HideClipboard();
1965 void Controller::Impl::SetClipboardHideEnable(bool enable)
1967 mClipboardHideEnabled = enable;
1970 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1972 //Send string to clipboard
1973 return ( mClipboard && mClipboard.SetItem( source ) );
1976 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1978 std::string selectedText;
1979 RetrieveSelection( selectedText, deleteAfterSending );
1980 CopyStringToClipboard( selectedText );
1981 ChangeState( EventData::EDITING );
1984 void Controller::Impl::RequestGetTextFromClipboard()
1988 mClipboard.RequestItem();
1992 void Controller::Impl::RepositionSelectionHandles()
1994 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1995 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1997 if( selectionStart == selectionEnd )
1999 // Nothing to select if handles are in the same place.
2000 // So, deactive Highlight box.
2001 mEventData->mDecorator->SetHighlightActive( false );
2005 mEventData->mDecorator->ClearHighlights();
2007 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2008 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2009 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2010 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2011 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2012 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2013 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2015 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2016 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2017 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2019 // Swap the indices if the start is greater than the end.
2020 const bool indicesSwapped = selectionStart > selectionEnd;
2022 // Tell the decorator to flip the selection handles if needed.
2023 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2025 if( indicesSwapped )
2027 std::swap( selectionStart, selectionEnd );
2030 // Get the indices to the first and last selected glyphs.
2031 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2032 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2033 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2034 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2036 // Get the lines where the glyphs are laid-out.
2037 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2039 LineIndex lineIndex = 0u;
2040 Length numberOfLines = 0u;
2041 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2042 1u + glyphEnd - glyphStart,
2045 const LineIndex firstLineIndex = lineIndex;
2047 // Create the structure to store some selection box info.
2048 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2049 selectionBoxLinesInfo.Resize( numberOfLines );
2051 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2052 selectionBoxInfo->minX = MAX_FLOAT;
2053 selectionBoxInfo->maxX = MIN_FLOAT;
2055 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2056 float minHighlightX = std::numeric_limits<float>::max();
2057 float maxHighlightX = std::numeric_limits<float>::min();
2059 Vector2 highLightPosition; // The highlight position in decorator's coords.
2061 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2063 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2064 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2067 // Transform to decorator's (control) coords.
2068 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2070 lineRun += firstLineIndex;
2072 // The line height is the addition of the line ascender and the line descender.
2073 // However, the line descender has a negative value, hence the subtraction.
2074 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2076 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2078 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2079 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2080 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2082 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2083 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2084 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2086 // The number of quads of the selection box.
2087 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2088 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2090 // Count the actual number of quads.
2091 unsigned int actualNumberOfQuads = 0u;
2094 // Traverse the glyphs.
2095 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2097 const GlyphInfo& glyph = *( glyphsBuffer + index );
2098 const Vector2& position = *( positionsBuffer + index );
2100 if( splitStartGlyph )
2102 // 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.
2104 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2105 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2106 // Get the direction of the character.
2107 CharacterDirection isCurrentRightToLeft = false;
2108 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2110 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2113 // The end point could be in the middle of the ligature.
2114 // Calculate the number of characters selected.
2115 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2117 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2118 quad.y = selectionBoxInfo->lineOffset;
2119 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2120 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2122 // Store the min and max 'x' for each line.
2123 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2124 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2126 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2127 ++actualNumberOfQuads;
2129 splitStartGlyph = false;
2133 if( splitEndGlyph && ( index == glyphEnd ) )
2135 // 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.
2137 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2138 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2139 // Get the direction of the character.
2140 CharacterDirection isCurrentRightToLeft = false;
2141 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2143 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2146 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2148 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2149 quad.y = selectionBoxInfo->lineOffset;
2150 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2151 quad.w = quad.y + selectionBoxInfo->lineHeight;
2153 // Store the min and max 'x' for each line.
2154 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2155 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2157 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2159 ++actualNumberOfQuads;
2161 splitEndGlyph = false;
2165 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2166 quad.y = selectionBoxInfo->lineOffset;
2167 quad.z = quad.x + glyph.advance;
2168 quad.w = quad.y + selectionBoxInfo->lineHeight;
2170 // Store the min and max 'x' for each line.
2171 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2172 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2174 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2176 ++actualNumberOfQuads;
2178 // Whether to retrieve the next line.
2179 if( index == lastGlyphOfLine )
2182 if( lineIndex < firstLineIndex + numberOfLines )
2184 // Retrieve the next line.
2187 // Get the last glyph of the new line.
2188 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2190 // Keep the offset and height of the current selection box.
2191 const float currentLineOffset = selectionBoxInfo->lineOffset;
2192 const float currentLineHeight = selectionBoxInfo->lineHeight;
2194 // Get the selection box info for the next line.
2197 selectionBoxInfo->minX = MAX_FLOAT;
2198 selectionBoxInfo->maxX = MIN_FLOAT;
2200 // Update the line's vertical offset.
2201 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2203 // The line height is the addition of the line ascender and the line descender.
2204 // However, the line descender has a negative value, hence the subtraction.
2205 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2210 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2211 // The final width is calculated after 'boxifying' the selection.
2212 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2213 endIt = selectionBoxLinesInfo.End();
2217 const SelectionBoxInfo& info = *it;
2219 // Update the size of the highlighted text.
2220 highLightSize.height += info.lineHeight;
2221 minHighlightX = std::min( minHighlightX, info.minX );
2222 maxHighlightX = std::max( maxHighlightX, info.maxX );
2225 // Add extra geometry to 'boxify' the selection.
2227 if( 1u < numberOfLines )
2229 // Boxify the first line.
2230 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2231 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2233 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2234 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2239 quad.y = firstSelectionBoxLineInfo.lineOffset;
2240 quad.z = firstSelectionBoxLineInfo.minX;
2241 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2243 // Boxify at the beginning of the line.
2244 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2246 ++actualNumberOfQuads;
2248 // Update the size of the highlighted text.
2249 minHighlightX = 0.f;
2254 quad.x = firstSelectionBoxLineInfo.maxX;
2255 quad.y = firstSelectionBoxLineInfo.lineOffset;
2256 quad.z = mModel->mVisualModel->mControlSize.width;
2257 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2259 // Boxify at the end of the line.
2260 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2262 ++actualNumberOfQuads;
2264 // Update the size of the highlighted text.
2265 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2268 // Boxify the central lines.
2269 if( 2u < numberOfLines )
2271 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2272 endIt = selectionBoxLinesInfo.End() - 1u;
2276 const SelectionBoxInfo& info = *it;
2279 quad.y = info.lineOffset;
2281 quad.w = info.lineOffset + info.lineHeight;
2283 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2285 ++actualNumberOfQuads;
2288 quad.y = info.lineOffset;
2289 quad.z = mModel->mVisualModel->mControlSize.width;
2290 quad.w = info.lineOffset + info.lineHeight;
2292 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2294 ++actualNumberOfQuads;
2297 // Update the size of the highlighted text.
2298 minHighlightX = 0.f;
2299 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2302 // Boxify the last line.
2303 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2304 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2306 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2307 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2312 quad.y = lastSelectionBoxLineInfo.lineOffset;
2313 quad.z = lastSelectionBoxLineInfo.minX;
2314 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2316 // Boxify at the beginning of the line.
2317 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2319 ++actualNumberOfQuads;
2321 // Update the size of the highlighted text.
2322 minHighlightX = 0.f;
2327 quad.x = lastSelectionBoxLineInfo.maxX;
2328 quad.y = lastSelectionBoxLineInfo.lineOffset;
2329 quad.z = mModel->mVisualModel->mControlSize.width;
2330 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2332 // Boxify at the end of the line.
2333 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2335 ++actualNumberOfQuads;
2337 // Update the size of the highlighted text.
2338 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2342 // Set the actual number of quads.
2343 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2345 // Sets the highlight's size and position. In decorator's coords.
2346 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2347 highLightSize.width = maxHighlightX - minHighlightX;
2349 highLightPosition.x = minHighlightX;
2350 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2351 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2353 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2355 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2357 CursorInfo primaryCursorInfo;
2358 GetCursorPosition( mEventData->mLeftSelectionPosition,
2359 primaryCursorInfo );
2361 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2363 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2365 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2366 primaryCursorInfo.lineHeight );
2368 CursorInfo secondaryCursorInfo;
2369 GetCursorPosition( mEventData->mRightSelectionPosition,
2370 secondaryCursorInfo );
2372 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2374 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2375 secondaryPosition.x,
2376 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2377 secondaryCursorInfo.lineHeight );
2380 // Set the flag to update the decorator.
2381 mEventData->mDecoratorUpdated = true;
2384 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2386 if( NULL == mEventData )
2388 // Nothing to do if there is no text input.
2392 if( IsShowingPlaceholderText() )
2394 // Nothing to do if there is the place-holder text.
2398 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2399 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2400 if( ( 0 == numberOfGlyphs ) ||
2401 ( 0 == numberOfLines ) )
2403 // Nothing to do if there is no text.
2407 // Find which word was selected
2408 CharacterIndex selectionStart( 0 );
2409 CharacterIndex selectionEnd( 0 );
2410 CharacterIndex noTextHitIndex( 0 );
2411 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2412 mModel->mLogicalModel,
2419 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2421 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2423 ChangeState( EventData::SELECTING );
2425 mEventData->mLeftSelectionPosition = selectionStart;
2426 mEventData->mRightSelectionPosition = selectionEnd;
2428 mEventData->mUpdateLeftSelectionPosition = true;
2429 mEventData->mUpdateRightSelectionPosition = true;
2430 mEventData->mUpdateHighlightBox = true;
2432 // It may happen an IMF commit event arrives before the selection event
2433 // if the IMF manager is in pre-edit state. The commit event will set the
2434 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2435 // to false, the highlight box won't be updated.
2436 mEventData->mUpdateCursorPosition = false;
2438 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2440 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2441 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2443 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2445 // Nothing to select. i.e. a white space, out of bounds
2446 ChangeState( EventData::EDITING_WITH_POPUP );
2448 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2450 mEventData->mUpdateCursorPosition = true;
2451 mEventData->mUpdateGrabHandlePosition = true;
2452 mEventData->mScrollAfterUpdatePosition = true;
2453 mEventData->mUpdateInputStyle = true;
2455 else if( Controller::NoTextTap::NO_ACTION == action )
2457 // Nothing to select. i.e. a white space, out of bounds
2458 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2460 mEventData->mUpdateCursorPosition = true;
2461 mEventData->mUpdateGrabHandlePosition = true;
2462 mEventData->mScrollAfterUpdatePosition = true;
2463 mEventData->mUpdateInputStyle = true;
2467 void Controller::Impl::SetPopupButtons()
2470 * Sets the Popup buttons to be shown depending on State.
2472 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2474 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2477 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2479 if( EventData::SELECTING == mEventData->mState )
2481 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2483 if( !IsClipboardEmpty() )
2485 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2486 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2489 if( !mEventData->mAllTextSelected )
2491 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2494 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2496 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2498 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2501 if( !IsClipboardEmpty() )
2503 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2504 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2507 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2509 if ( !IsClipboardEmpty() )
2511 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2512 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2516 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2519 void Controller::Impl::ChangeState( EventData::State newState )
2521 if( NULL == mEventData )
2523 // Nothing to do if there is no text input.
2527 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2529 if( mEventData->mState != newState )
2531 mEventData->mPreviousState = mEventData->mState;
2532 mEventData->mState = newState;
2534 switch( mEventData->mState )
2536 case EventData::INACTIVE:
2538 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2539 mEventData->mDecorator->StopCursorBlink();
2540 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2541 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2542 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2543 mEventData->mDecorator->SetHighlightActive( false );
2544 mEventData->mDecorator->SetPopupActive( false );
2545 mEventData->mDecoratorUpdated = true;
2548 case EventData::INTERRUPTED:
2550 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2551 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2552 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2553 mEventData->mDecorator->SetHighlightActive( false );
2554 mEventData->mDecorator->SetPopupActive( false );
2555 mEventData->mDecoratorUpdated = true;
2558 case EventData::SELECTING:
2560 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2561 mEventData->mDecorator->StopCursorBlink();
2562 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2563 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2564 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2565 mEventData->mDecorator->SetHighlightActive( true );
2566 if( mEventData->mGrabHandlePopupEnabled )
2569 mEventData->mDecorator->SetPopupActive( true );
2571 mEventData->mDecoratorUpdated = true;
2574 case EventData::EDITING:
2576 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2577 if( mEventData->mCursorBlinkEnabled )
2579 mEventData->mDecorator->StartCursorBlink();
2581 // Grab handle is not shown until a tap is received whilst EDITING
2582 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2583 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2584 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2585 mEventData->mDecorator->SetHighlightActive( false );
2586 if( mEventData->mGrabHandlePopupEnabled )
2588 mEventData->mDecorator->SetPopupActive( false );
2590 mEventData->mDecoratorUpdated = true;
2593 case EventData::EDITING_WITH_POPUP:
2595 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2597 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2598 if( mEventData->mCursorBlinkEnabled )
2600 mEventData->mDecorator->StartCursorBlink();
2602 if( mEventData->mSelectionEnabled )
2604 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2605 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2606 mEventData->mDecorator->SetHighlightActive( false );
2610 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2612 if( mEventData->mGrabHandlePopupEnabled )
2615 mEventData->mDecorator->SetPopupActive( true );
2617 mEventData->mDecoratorUpdated = true;
2620 case EventData::EDITING_WITH_GRAB_HANDLE:
2622 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2624 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2625 if( mEventData->mCursorBlinkEnabled )
2627 mEventData->mDecorator->StartCursorBlink();
2629 // Grab handle is not shown until a tap is received whilst EDITING
2630 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2631 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2632 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2633 mEventData->mDecorator->SetHighlightActive( false );
2634 if( mEventData->mGrabHandlePopupEnabled )
2636 mEventData->mDecorator->SetPopupActive( false );
2638 mEventData->mDecoratorUpdated = true;
2641 case EventData::SELECTION_HANDLE_PANNING:
2643 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2644 mEventData->mDecorator->StopCursorBlink();
2645 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2646 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2647 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2648 mEventData->mDecorator->SetHighlightActive( true );
2649 if( mEventData->mGrabHandlePopupEnabled )
2651 mEventData->mDecorator->SetPopupActive( false );
2653 mEventData->mDecoratorUpdated = true;
2656 case EventData::GRAB_HANDLE_PANNING:
2658 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2660 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2661 if( mEventData->mCursorBlinkEnabled )
2663 mEventData->mDecorator->StartCursorBlink();
2665 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2666 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2667 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2668 mEventData->mDecorator->SetHighlightActive( false );
2669 if( mEventData->mGrabHandlePopupEnabled )
2671 mEventData->mDecorator->SetPopupActive( false );
2673 mEventData->mDecoratorUpdated = true;
2676 case EventData::EDITING_WITH_PASTE_POPUP:
2678 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2680 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2681 if( mEventData->mCursorBlinkEnabled )
2683 mEventData->mDecorator->StartCursorBlink();
2686 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2687 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2688 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2689 mEventData->mDecorator->SetHighlightActive( false );
2691 if( mEventData->mGrabHandlePopupEnabled )
2694 mEventData->mDecorator->SetPopupActive( true );
2696 mEventData->mDecoratorUpdated = true;
2699 case EventData::TEXT_PANNING:
2701 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2702 mEventData->mDecorator->StopCursorBlink();
2703 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2704 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2705 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2707 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2708 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2709 mEventData->mDecorator->SetHighlightActive( true );
2712 if( mEventData->mGrabHandlePopupEnabled )
2714 mEventData->mDecorator->SetPopupActive( false );
2717 mEventData->mDecoratorUpdated = true;
2724 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2725 CursorInfo& cursorInfo )
2727 if( !IsShowingRealText() )
2729 // Do not want to use the place-holder text to set the cursor position.
2731 // Use the line's height of the font's family set to set the cursor's size.
2732 // If there is no font's family set, use the default font.
2733 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2735 cursorInfo.lineOffset = 0.f;
2736 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2737 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2739 switch( mModel->mHorizontalAlignment )
2741 case Text::HorizontalAlignment::BEGIN :
2743 cursorInfo.primaryPosition.x = 0.f;
2746 case Text::HorizontalAlignment::CENTER:
2748 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2751 case Text::HorizontalAlignment::END:
2753 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2758 // Nothing else to do.
2762 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2763 GetCursorPositionParameters parameters;
2764 parameters.visualModel = mModel->mVisualModel;
2765 parameters.logicalModel = mModel->mLogicalModel;
2766 parameters.metrics = mMetrics;
2767 parameters.logical = logical;
2768 parameters.isMultiline = isMultiLine;
2770 Text::GetCursorPosition( parameters,
2773 // Adds Outline offset.
2774 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2775 cursorInfo.primaryPosition.x += outlineWidth;
2776 cursorInfo.primaryPosition.y += outlineWidth;
2777 cursorInfo.secondaryPosition.x += outlineWidth;
2778 cursorInfo.secondaryPosition.y += outlineWidth;
2782 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2784 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2785 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2787 if( 0.f > cursorInfo.primaryPosition.x )
2789 cursorInfo.primaryPosition.x = 0.f;
2792 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2793 if( cursorInfo.primaryPosition.x > edgeWidth )
2795 cursorInfo.primaryPosition.x = edgeWidth;
2800 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2802 if( NULL == mEventData )
2804 // Nothing to do if there is no text input.
2808 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2810 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2811 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2813 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2814 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2816 if( numberOfCharacters > 1u )
2818 const Script script = mModel->mLogicalModel->GetScript( index );
2819 if( HasLigatureMustBreak( script ) )
2821 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2822 numberOfCharacters = 1u;
2827 while( 0u == numberOfCharacters )
2830 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2834 if( index < mEventData->mPrimaryCursorPosition )
2836 cursorIndex -= numberOfCharacters;
2840 cursorIndex += numberOfCharacters;
2843 // Will update the cursor hook position.
2844 mEventData->mUpdateCursorHookPosition = true;
2849 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2851 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2852 if( NULL == mEventData )
2854 // Nothing to do if there is no text input.
2855 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2859 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2861 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2863 // Sets the cursor position.
2864 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2867 cursorInfo.primaryCursorHeight,
2868 cursorInfo.lineHeight );
2869 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2871 if( mEventData->mUpdateGrabHandlePosition )
2873 // Sets the grab handle position.
2874 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2876 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2877 cursorInfo.lineHeight );
2880 if( cursorInfo.isSecondaryCursor )
2882 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2883 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2884 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2885 cursorInfo.secondaryCursorHeight,
2886 cursorInfo.lineHeight );
2887 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2890 // Set which cursors are active according the state.
2891 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2893 if( cursorInfo.isSecondaryCursor )
2895 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2899 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2904 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2907 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2910 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2911 const CursorInfo& cursorInfo )
2913 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2914 ( RIGHT_SELECTION_HANDLE != handleType ) )
2919 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2921 // Sets the handle's position.
2922 mEventData->mDecorator->SetPosition( handleType,
2924 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2925 cursorInfo.lineHeight );
2927 // If selection handle at start of the text and other at end of the text then all text is selected.
2928 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2929 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2930 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2933 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2935 // Clamp between -space & -alignment offset.
2937 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2939 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2940 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2941 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2943 mEventData->mDecoratorUpdated = true;
2947 mModel->mScrollPosition.x = 0.f;
2951 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2953 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2955 // Nothing to do if the text is single line.
2959 // Clamp between -space & 0.
2960 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2962 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2963 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2964 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2966 mEventData->mDecoratorUpdated = true;
2970 mModel->mScrollPosition.y = 0.f;
2974 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2976 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2978 // position is in actor's coords.
2979 const float positionEndX = position.x + cursorWidth;
2980 const float positionEndY = position.y + lineHeight;
2982 // Transform the position to decorator coords.
2983 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2984 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2986 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2987 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2989 if( decoratorPositionBeginX < 0.f )
2991 mModel->mScrollPosition.x = -position.x;
2993 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2995 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2998 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3000 if( decoratorPositionBeginY < 0.f )
3002 mModel->mScrollPosition.y = -position.y;
3004 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3006 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3011 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3013 // Get the current cursor position in decorator coords.
3014 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3016 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3020 // Calculate the offset to match the cursor position before the character was deleted.
3021 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3023 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3024 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3026 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3027 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3031 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3032 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3034 // Makes the new cursor position visible if needed.
3035 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3038 void Controller::Impl::RequestRelayout()
3040 if( NULL != mControlInterface )
3042 mControlInterface->RequestTextRelayout();
3048 } // namespace Toolkit