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, InputMethodContext& inputMethodContext )
71 : mDecorator( decorator ),
72 mInputMethodContext( inputMethodContext ),
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 ),
114 mUpdateAlignment( false )
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 )
183 NotifyInputMethodContext();
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::NotifyInputMethodContext()
334 if( mEventData && mEventData->mInputMethodContext )
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->mInputMethodContext.SetCursorPosition( cursorPosition );
351 mEventData->mInputMethodContext.NotifyCursorPosition();
355 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
357 if ( mEventData && mEventData->mInputMethodContext )
359 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
360 mEventData->mInputMethodContext.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 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
430 // Nothing else to do if there are no paragraphs.
434 // Find the paragraphs to be updated.
435 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
436 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
438 // Text is being added at the end of the current text.
439 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
441 // Text is being added in a new paragraph after the last character of the text.
442 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
443 numberOfCharacters = 0u;
444 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
446 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
447 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
449 // Nothing else to do;
453 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
457 Length numberOfCharactersToUpdate = 0u;
458 if( mTextUpdateInfo.mFullRelayoutNeeded )
460 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
464 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
466 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
467 numberOfCharactersToUpdate,
468 paragraphsToBeUpdated );
471 if( 0u != paragraphsToBeUpdated.Count() )
473 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
474 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
475 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
477 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
478 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
480 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
481 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
482 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
483 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
485 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
486 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
488 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
492 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
496 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
497 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
500 void Controller::Impl::ClearFullModelData( OperationsMask operations )
502 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
504 mModel->mLogicalModel->mLineBreakInfo.Clear();
505 mModel->mLogicalModel->mParagraphInfo.Clear();
508 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
510 mModel->mLogicalModel->mLineBreakInfo.Clear();
513 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
515 mModel->mLogicalModel->mScriptRuns.Clear();
518 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
520 mModel->mLogicalModel->mFontRuns.Clear();
523 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
525 if( NO_OPERATION != ( BIDI_INFO & operations ) )
527 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
528 mModel->mLogicalModel->mCharacterDirections.Clear();
531 if( NO_OPERATION != ( REORDER & operations ) )
533 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
534 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
535 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
539 BidirectionalLineInfoRun& bidiLineInfo = *it;
541 free( bidiLineInfo.visualToLogicalMap );
542 bidiLineInfo.visualToLogicalMap = NULL;
544 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
548 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
550 mModel->mVisualModel->mGlyphs.Clear();
551 mModel->mVisualModel->mGlyphsToCharacters.Clear();
552 mModel->mVisualModel->mCharactersToGlyph.Clear();
553 mModel->mVisualModel->mCharactersPerGlyph.Clear();
554 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
555 mModel->mVisualModel->mGlyphPositions.Clear();
558 if( NO_OPERATION != ( LAYOUT & operations ) )
560 mModel->mVisualModel->mLines.Clear();
563 if( NO_OPERATION != ( COLOR & operations ) )
565 mModel->mVisualModel->mColorIndices.Clear();
569 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
571 const CharacterIndex endIndexPlusOne = endIndex + 1u;
573 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
575 // Clear the line break info.
576 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
578 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
579 lineBreakInfoBuffer + endIndexPlusOne );
581 // Clear the paragraphs.
582 ClearCharacterRuns( startIndex,
584 mModel->mLogicalModel->mParagraphInfo );
587 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
589 // Clear the word break info.
590 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
592 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
593 wordBreakInfoBuffer + endIndexPlusOne );
596 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
598 // Clear the scripts.
599 ClearCharacterRuns( startIndex,
601 mModel->mLogicalModel->mScriptRuns );
604 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
607 ClearCharacterRuns( startIndex,
609 mModel->mLogicalModel->mFontRuns );
612 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
614 if( NO_OPERATION != ( BIDI_INFO & operations ) )
616 // Clear the bidirectional paragraph info.
617 ClearCharacterRuns( startIndex,
619 mModel->mLogicalModel->mBidirectionalParagraphInfo );
621 // Clear the character's directions.
622 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
624 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
625 characterDirectionsBuffer + endIndexPlusOne );
628 if( NO_OPERATION != ( REORDER & operations ) )
630 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
631 uint32_t endRemoveIndex = startRemoveIndex;
632 ClearCharacterRuns( startIndex,
634 mModel->mLogicalModel->mBidirectionalLineInfo,
638 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
640 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
641 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
642 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
646 BidirectionalLineInfoRun& bidiLineInfo = *it;
648 free( bidiLineInfo.visualToLogicalMap );
649 bidiLineInfo.visualToLogicalMap = NULL;
652 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
653 bidirectionalLineInfoBuffer + endRemoveIndex );
658 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
660 const CharacterIndex endIndexPlusOne = endIndex + 1u;
661 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
663 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
664 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
665 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
667 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
668 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
670 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
672 // Update the character to glyph indices.
673 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
674 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
678 CharacterIndex& index = *it;
679 index -= numberOfGlyphsRemoved;
682 // Clear the character to glyph conversion table.
683 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
684 charactersToGlyphBuffer + endIndexPlusOne );
686 // Clear the glyphs per character table.
687 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
688 glyphsPerCharacterBuffer + endIndexPlusOne );
690 // Clear the glyphs buffer.
691 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
692 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
693 glyphsBuffer + endGlyphIndexPlusOne );
695 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
697 // Update the glyph to character indices.
698 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
699 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
703 CharacterIndex& index = *it;
704 index -= numberOfCharactersRemoved;
707 // Clear the glyphs to characters buffer.
708 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
709 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
711 // Clear the characters per glyph buffer.
712 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
713 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
714 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
716 // Clear the positions buffer.
717 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
718 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
719 positionsBuffer + endGlyphIndexPlusOne );
722 if( NO_OPERATION != ( LAYOUT & operations ) )
725 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
726 uint32_t endRemoveIndex = startRemoveIndex;
727 ClearCharacterRuns( startIndex,
729 mModel->mVisualModel->mLines,
733 // Will update the glyph runs.
734 startRemoveIndex = mModel->mVisualModel->mLines.Count();
735 endRemoveIndex = startRemoveIndex;
736 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
737 endGlyphIndexPlusOne - 1u,
738 mModel->mVisualModel->mLines,
742 // Set the line index from where to insert the new laid-out lines.
743 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
745 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
746 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
747 linesBuffer + endRemoveIndex );
750 if( NO_OPERATION != ( COLOR & operations ) )
752 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
754 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
755 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
756 colorIndexBuffer + endGlyphIndexPlusOne );
761 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
763 if( mTextUpdateInfo.mClearAll ||
764 ( ( 0u == startIndex ) &&
765 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
767 ClearFullModelData( operations );
771 // Clear the model data related with characters.
772 ClearCharacterModelData( startIndex, endIndex, operations );
774 // Clear the model data related with glyphs.
775 ClearGlyphModelData( startIndex, endIndex, operations );
778 // The estimated number of lines. Used to avoid reallocations when layouting.
779 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
781 mModel->mVisualModel->ClearCaches();
784 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
786 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
788 // Calculate the operations to be done.
789 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
791 if( NO_OPERATION == operations )
793 // Nothing to do if no operations are pending and required.
797 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
798 Vector<Character> displayCharacters;
799 bool useHiddenText = false;
800 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
802 mHiddenInput->Substitute( srcCharacters,displayCharacters );
803 useHiddenText = true;
806 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
807 const Length numberOfCharacters = utf32Characters.Count();
809 // Index to the first character of the first paragraph to be updated.
810 CharacterIndex startIndex = 0u;
811 // Number of characters of the paragraphs to be removed.
812 Length paragraphCharacters = 0u;
814 CalculateTextUpdateIndices( paragraphCharacters );
816 // Check whether the indices for updating the text is valid
817 if ( numberOfCharacters > 0u &&
818 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
819 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
821 std::string currentText;
822 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
824 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
825 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
827 // Dump mTextUpdateInfo
828 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
829 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
830 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
831 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
832 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
833 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
834 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
835 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
836 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
837 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
838 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
839 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
840 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
845 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
847 if( mTextUpdateInfo.mClearAll ||
848 ( 0u != paragraphCharacters ) )
850 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
853 mTextUpdateInfo.mClearAll = false;
855 // Whether the model is updated.
856 bool updated = false;
858 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
859 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
861 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
863 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
864 // calculate the bidirectional info for each 'paragraph'.
865 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
866 // is not shaped together).
867 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
869 SetLineBreakInfo( utf32Characters,
871 requestedNumberOfCharacters,
874 // Create the paragraph info.
875 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
876 requestedNumberOfCharacters );
880 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
881 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
883 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
884 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
886 SetWordBreakInfo( utf32Characters,
888 requestedNumberOfCharacters,
893 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
894 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
896 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
897 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
899 if( getScripts || validateFonts )
901 // Validates the fonts assigned by the application or assigns default ones.
902 // It makes sure all the characters are going to be rendered by the correct font.
903 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
907 // Retrieves the scripts used in the text.
908 multilanguageSupport.SetScripts( utf32Characters,
910 requestedNumberOfCharacters,
916 // Validate the fonts set through the mark-up string.
917 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
919 // Get the default font's description.
920 TextAbstraction::FontDescription defaultFontDescription;
921 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
923 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
925 // If the placeholder font is set specifically, only placeholder font is changed.
926 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
927 if( mEventData->mPlaceholderFont->sizeDefined )
929 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
932 else if( NULL != mFontDefaults )
934 // Set the normal font and the placeholder font.
935 defaultFontDescription = mFontDefaults->mFontDescription;
937 if( mTextFitEnabled )
939 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
943 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
947 // Validates the fonts. If there is a character with no assigned font it sets a default one.
948 // After this call, fonts are validated.
949 multilanguageSupport.ValidateFonts( utf32Characters,
952 defaultFontDescription,
955 requestedNumberOfCharacters,
961 Vector<Character> mirroredUtf32Characters;
962 bool textMirrored = false;
963 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
964 if( NO_OPERATION != ( BIDI_INFO & operations ) )
966 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
967 bidirectionalInfo.Reserve( numberOfParagraphs );
969 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
970 SetBidirectionalInfo( utf32Characters,
974 requestedNumberOfCharacters,
976 mModel->mMatchSystemLanguageDirection,
979 if( 0u != bidirectionalInfo.Count() )
981 // Only set the character directions if there is right to left characters.
982 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
983 GetCharactersDirection( bidirectionalInfo,
986 requestedNumberOfCharacters,
989 // This paragraph has right to left text. Some characters may need to be mirrored.
990 // TODO: consider if the mirrored string can be stored as well.
992 textMirrored = GetMirroredText( utf32Characters,
996 requestedNumberOfCharacters,
997 mirroredUtf32Characters );
1001 // There is no right to left characters. Clear the directions vector.
1002 mModel->mLogicalModel->mCharacterDirections.Clear();
1007 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1008 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1009 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1010 Vector<GlyphIndex> newParagraphGlyphs;
1011 newParagraphGlyphs.Reserve( numberOfParagraphs );
1013 const Length currentNumberOfGlyphs = glyphs.Count();
1014 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1016 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1018 ShapeText( textToShape,
1023 mTextUpdateInfo.mStartGlyphIndex,
1024 requestedNumberOfCharacters,
1026 glyphsToCharactersMap,
1028 newParagraphGlyphs );
1030 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1031 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1032 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1036 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1038 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1040 GlyphInfo* glyphsBuffer = glyphs.Begin();
1041 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1043 // Update the width and advance of all new paragraph characters.
1044 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1046 const GlyphIndex index = *it;
1047 GlyphInfo& glyph = *( glyphsBuffer + index );
1049 glyph.xBearing = 0.f;
1051 glyph.advance = 0.f;
1056 if( NO_OPERATION != ( COLOR & operations ) )
1058 // Set the color runs in glyphs.
1059 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1060 mModel->mVisualModel->mCharactersToGlyph,
1061 mModel->mVisualModel->mGlyphsPerCharacter,
1063 mTextUpdateInfo.mStartGlyphIndex,
1064 requestedNumberOfCharacters,
1065 mModel->mVisualModel->mColors,
1066 mModel->mVisualModel->mColorIndices );
1071 if( ( NULL != mEventData ) &&
1072 mEventData->mPreEditFlag &&
1073 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1075 // Add the underline for the pre-edit text.
1076 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1077 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1079 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1080 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1081 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1082 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1084 GlyphRun underlineRun;
1085 underlineRun.glyphIndex = glyphStart;
1086 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1088 // TODO: At the moment the underline runs are only for pre-edit.
1089 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1092 // The estimated number of lines. Used to avoid reallocations when layouting.
1093 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1095 // Set the previous number of characters for the next time the text is updated.
1096 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1101 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1103 // Sets the default text's color.
1104 inputStyle.textColor = mTextColor;
1105 inputStyle.isDefaultColor = true;
1107 inputStyle.familyName.clear();
1108 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1109 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1110 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1111 inputStyle.size = 0.f;
1113 inputStyle.lineSpacing = 0.f;
1115 inputStyle.underlineProperties.clear();
1116 inputStyle.shadowProperties.clear();
1117 inputStyle.embossProperties.clear();
1118 inputStyle.outlineProperties.clear();
1120 inputStyle.isFamilyDefined = false;
1121 inputStyle.isWeightDefined = false;
1122 inputStyle.isWidthDefined = false;
1123 inputStyle.isSlantDefined = false;
1124 inputStyle.isSizeDefined = false;
1126 inputStyle.isLineSpacingDefined = false;
1128 inputStyle.isUnderlineDefined = false;
1129 inputStyle.isShadowDefined = false;
1130 inputStyle.isEmbossDefined = false;
1131 inputStyle.isOutlineDefined = false;
1133 // Sets the default font's family name, weight, width, slant and size.
1136 if( mFontDefaults->familyDefined )
1138 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1139 inputStyle.isFamilyDefined = true;
1142 if( mFontDefaults->weightDefined )
1144 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1145 inputStyle.isWeightDefined = true;
1148 if( mFontDefaults->widthDefined )
1150 inputStyle.width = mFontDefaults->mFontDescription.width;
1151 inputStyle.isWidthDefined = true;
1154 if( mFontDefaults->slantDefined )
1156 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1157 inputStyle.isSlantDefined = true;
1160 if( mFontDefaults->sizeDefined )
1162 inputStyle.size = mFontDefaults->mDefaultPointSize;
1163 inputStyle.isSizeDefined = true;
1168 float Controller::Impl::GetDefaultFontLineHeight()
1170 FontId defaultFontId = 0u;
1171 if( NULL == mFontDefaults )
1173 TextAbstraction::FontDescription fontDescription;
1174 defaultFontId = mFontClient.GetFontId( fontDescription );
1178 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1181 Text::FontMetrics fontMetrics;
1182 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1184 return( fontMetrics.ascender - fontMetrics.descender );
1187 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1189 if( NULL == mEventData || !IsShowingRealText() )
1191 // Nothing to do if there is no text input.
1195 int keyCode = event.p1.mInt;
1196 bool isShiftModifier = event.p2.mBool;
1198 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1200 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1202 if( mEventData->mPrimaryCursorPosition > 0u )
1204 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1206 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1210 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1214 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1216 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1218 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1220 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1224 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1228 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1230 // Ignore Shift-Up for text selection for now.
1232 // Get first the line index of the current cursor position index.
1233 CharacterIndex characterIndex = 0u;
1235 if( mEventData->mPrimaryCursorPosition > 0u )
1237 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1240 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1241 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1243 // Retrieve the cursor position info.
1244 CursorInfo cursorInfo;
1245 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1248 // Get the line above.
1249 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1251 // Get the next hit 'y' point.
1252 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1254 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1255 bool matchedCharacter = false;
1256 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1257 mModel->mLogicalModel,
1259 mEventData->mCursorHookPositionX,
1261 CharacterHitTest::TAP,
1264 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1266 // Ignore Shift-Down for text selection for now.
1268 // Get first the line index of the current cursor position index.
1269 CharacterIndex characterIndex = 0u;
1271 if( mEventData->mPrimaryCursorPosition > 0u )
1273 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1276 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1278 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1280 // Retrieve the cursor position info.
1281 CursorInfo cursorInfo;
1282 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1285 // Get the line below.
1286 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1288 // Get the next hit 'y' point.
1289 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1291 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1292 bool matchedCharacter = false;
1293 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1294 mModel->mLogicalModel,
1296 mEventData->mCursorHookPositionX,
1298 CharacterHitTest::TAP,
1303 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1305 // Update selection position after moving the cursor
1306 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1307 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1310 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1312 // Handle text selection
1313 bool selecting = false;
1315 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1317 // Shift-Left/Right to select the text
1318 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1319 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1321 mEventData->mRightSelectionPosition += cursorPositionDelta;
1325 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1327 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1333 // Notify the cursor position to the InputMethodContext.
1334 if( mEventData->mInputMethodContext )
1336 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1337 mEventData->mInputMethodContext.NotifyCursorPosition();
1340 ChangeState( EventData::SELECTING );
1342 mEventData->mUpdateLeftSelectionPosition = true;
1343 mEventData->mUpdateRightSelectionPosition = true;
1344 mEventData->mUpdateGrabHandlePosition = true;
1345 mEventData->mUpdateHighlightBox = true;
1347 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1348 if( mEventData->mGrabHandlePopupEnabled )
1350 mEventData->mDecorator->SetPopupActive( false );
1356 // Handle normal cursor move
1357 ChangeState( EventData::EDITING );
1358 mEventData->mUpdateCursorPosition = true;
1361 mEventData->mUpdateInputStyle = true;
1362 mEventData->mScrollAfterUpdatePosition = true;
1365 void Controller::Impl::OnTapEvent( const Event& event )
1367 if( NULL != mEventData )
1369 const unsigned int tapCount = event.p1.mUint;
1371 if( 1u == tapCount )
1373 if( IsShowingRealText() )
1375 // Convert from control's coords to text's coords.
1376 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1377 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1379 // Keep the tap 'x' position. Used to move the cursor.
1380 mEventData->mCursorHookPositionX = xPosition;
1382 // Whether to touch point hits on a glyph.
1383 bool matchedCharacter = false;
1384 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1385 mModel->mLogicalModel,
1389 CharacterHitTest::TAP,
1392 // When the cursor position is changing, delay cursor blinking
1393 mEventData->mDecorator->DelayCursorBlink();
1397 mEventData->mPrimaryCursorPosition = 0u;
1400 // Update selection position after tapping
1401 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1402 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1404 mEventData->mUpdateCursorPosition = true;
1405 mEventData->mUpdateGrabHandlePosition = true;
1406 mEventData->mScrollAfterUpdatePosition = true;
1407 mEventData->mUpdateInputStyle = true;
1409 // Notify the cursor position to the InputMethodContext.
1410 if( mEventData->mInputMethodContext )
1412 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1413 mEventData->mInputMethodContext.NotifyCursorPosition();
1416 else if( 2u == tapCount )
1418 if( mEventData->mSelectionEnabled )
1420 // Convert from control's coords to text's coords.
1421 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1422 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1424 // Calculates the logical position from the x,y coords.
1425 RepositionSelectionHandles( xPosition,
1427 mEventData->mDoubleTapAction );
1433 void Controller::Impl::OnPanEvent( const Event& event )
1435 if( NULL == mEventData )
1437 // Nothing to do if there is no text input.
1441 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1442 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1444 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1446 // Nothing to do if scrolling is not enabled.
1450 const int state = event.p1.mInt;
1454 case Gesture::Started:
1456 // Will remove the cursor, handles or text's popup, ...
1457 ChangeState( EventData::TEXT_PANNING );
1460 case Gesture::Continuing:
1462 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1463 const Vector2 currentScroll = mModel->mScrollPosition;
1465 if( isHorizontalScrollEnabled )
1467 const float displacementX = event.p2.mFloat;
1468 mModel->mScrollPosition.x += displacementX;
1470 ClampHorizontalScroll( layoutSize );
1473 if( isVerticalScrollEnabled )
1475 const float displacementY = event.p3.mFloat;
1476 mModel->mScrollPosition.y += displacementY;
1478 ClampVerticalScroll( layoutSize );
1481 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1484 case Gesture::Finished:
1485 case Gesture::Cancelled: // FALLTHROUGH
1487 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1488 ChangeState( mEventData->mPreviousState );
1496 void Controller::Impl::OnLongPressEvent( const Event& event )
1498 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1500 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1502 ChangeState( EventData::EDITING_WITH_POPUP );
1503 mEventData->mDecoratorUpdated = true;
1504 mEventData->mUpdateInputStyle = true;
1508 if( mEventData->mSelectionEnabled )
1510 // Convert from control's coords to text's coords.
1511 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1512 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1514 // Calculates the logical position from the x,y coords.
1515 RepositionSelectionHandles( xPosition,
1517 mEventData->mLongPressAction );
1522 void Controller::Impl::OnHandleEvent( const Event& event )
1524 if( NULL == mEventData )
1526 // Nothing to do if there is no text input.
1530 const unsigned int state = event.p1.mUint;
1531 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1532 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1534 if( HANDLE_PRESSED == state )
1536 // Convert from decorator's coords to text's coords.
1537 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1538 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1540 // Need to calculate the handle's new position.
1541 bool matchedCharacter = false;
1542 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1543 mModel->mLogicalModel,
1547 CharacterHitTest::SCROLL,
1550 if( Event::GRAB_HANDLE_EVENT == event.type )
1552 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1554 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1556 // Updates the cursor position if the handle's new position is different than the current one.
1557 mEventData->mUpdateCursorPosition = true;
1558 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1559 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1560 mEventData->mPrimaryCursorPosition = handleNewPosition;
1563 // 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.
1564 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1566 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1568 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1570 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1571 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1573 // Updates the highlight box if the handle's new position is different than the current one.
1574 mEventData->mUpdateHighlightBox = true;
1575 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1576 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1577 mEventData->mLeftSelectionPosition = handleNewPosition;
1580 // 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.
1581 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1583 // Will define the order to scroll the text to match the handle position.
1584 mEventData->mIsLeftHandleSelected = true;
1585 mEventData->mIsRightHandleSelected = false;
1587 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1589 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1591 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1592 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1594 // Updates the highlight box if the handle's new position is different than the current one.
1595 mEventData->mUpdateHighlightBox = true;
1596 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1597 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1598 mEventData->mRightSelectionPosition = handleNewPosition;
1601 // 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.
1602 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1604 // Will define the order to scroll the text to match the handle position.
1605 mEventData->mIsLeftHandleSelected = false;
1606 mEventData->mIsRightHandleSelected = true;
1608 } // end ( HANDLE_PRESSED == state )
1609 else if( ( HANDLE_RELEASED == state ) ||
1610 handleStopScrolling )
1612 CharacterIndex handlePosition = 0u;
1613 if( handleStopScrolling || isSmoothHandlePanEnabled )
1615 // Convert from decorator's coords to text's coords.
1616 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1617 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1619 bool matchedCharacter = false;
1620 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1621 mModel->mLogicalModel,
1625 CharacterHitTest::SCROLL,
1629 if( Event::GRAB_HANDLE_EVENT == event.type )
1631 mEventData->mUpdateCursorPosition = true;
1632 mEventData->mUpdateGrabHandlePosition = true;
1633 mEventData->mUpdateInputStyle = true;
1635 if( !IsClipboardEmpty() )
1637 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1640 if( handleStopScrolling || isSmoothHandlePanEnabled )
1642 mEventData->mScrollAfterUpdatePosition = true;
1643 mEventData->mPrimaryCursorPosition = handlePosition;
1646 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1648 ChangeState( EventData::SELECTING );
1650 mEventData->mUpdateHighlightBox = true;
1651 mEventData->mUpdateLeftSelectionPosition = true;
1652 mEventData->mUpdateRightSelectionPosition = true;
1654 if( handleStopScrolling || isSmoothHandlePanEnabled )
1656 mEventData->mScrollAfterUpdatePosition = true;
1658 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1659 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1661 mEventData->mLeftSelectionPosition = handlePosition;
1665 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1667 ChangeState( EventData::SELECTING );
1669 mEventData->mUpdateHighlightBox = true;
1670 mEventData->mUpdateRightSelectionPosition = true;
1671 mEventData->mUpdateLeftSelectionPosition = true;
1673 if( handleStopScrolling || isSmoothHandlePanEnabled )
1675 mEventData->mScrollAfterUpdatePosition = true;
1676 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1677 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1679 mEventData->mRightSelectionPosition = handlePosition;
1684 mEventData->mDecoratorUpdated = true;
1685 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1686 else if( HANDLE_SCROLLING == state )
1688 const float xSpeed = event.p2.mFloat;
1689 const float ySpeed = event.p3.mFloat;
1690 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1691 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1693 mModel->mScrollPosition.x += xSpeed;
1694 mModel->mScrollPosition.y += ySpeed;
1696 ClampHorizontalScroll( layoutSize );
1697 ClampVerticalScroll( layoutSize );
1699 bool endOfScroll = false;
1700 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1702 // Notify the decorator there is no more text to scroll.
1703 // The decorator won't send more scroll events.
1704 mEventData->mDecorator->NotifyEndOfScroll();
1705 // Still need to set the position of the handle.
1709 // Set the position of the handle.
1710 const bool scrollRightDirection = xSpeed > 0.f;
1711 const bool scrollBottomDirection = ySpeed > 0.f;
1712 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1713 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1715 if( Event::GRAB_HANDLE_EVENT == event.type )
1717 ChangeState( EventData::GRAB_HANDLE_PANNING );
1719 // Get the grab handle position in decorator coords.
1720 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1722 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1724 // Position the grag handle close to either the left or right edge.
1725 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1728 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1730 position.x = mEventData->mCursorHookPositionX;
1732 // Position the grag handle close to either the top or bottom edge.
1733 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1736 // Get the new handle position.
1737 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1738 bool matchedCharacter = false;
1739 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1740 mModel->mLogicalModel,
1742 position.x - mModel->mScrollPosition.x,
1743 position.y - mModel->mScrollPosition.y,
1744 CharacterHitTest::SCROLL,
1747 if( mEventData->mPrimaryCursorPosition != handlePosition )
1749 mEventData->mUpdateCursorPosition = true;
1750 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1751 mEventData->mScrollAfterUpdatePosition = true;
1752 mEventData->mPrimaryCursorPosition = handlePosition;
1754 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1756 // Updates the decorator if the soft handle panning is enabled.
1757 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1759 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1761 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1763 // Get the selection handle position in decorator coords.
1764 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1766 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1768 // Position the selection handle close to either the left or right edge.
1769 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1772 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1774 position.x = mEventData->mCursorHookPositionX;
1776 // Position the grag handle close to either the top or bottom edge.
1777 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1780 // Get the new handle position.
1781 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1782 bool matchedCharacter = false;
1783 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1784 mModel->mLogicalModel,
1786 position.x - mModel->mScrollPosition.x,
1787 position.y - mModel->mScrollPosition.y,
1788 CharacterHitTest::SCROLL,
1791 if( leftSelectionHandleEvent )
1793 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1795 if( differentHandles || endOfScroll )
1797 mEventData->mUpdateHighlightBox = true;
1798 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1799 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1800 mEventData->mLeftSelectionPosition = handlePosition;
1805 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1806 if( differentHandles || endOfScroll )
1808 mEventData->mUpdateHighlightBox = true;
1809 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1810 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1811 mEventData->mRightSelectionPosition = handlePosition;
1815 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1817 RepositionSelectionHandles();
1819 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1822 mEventData->mDecoratorUpdated = true;
1823 } // end ( HANDLE_SCROLLING == state )
1826 void Controller::Impl::OnSelectEvent( const Event& event )
1828 if( NULL == mEventData )
1830 // Nothing to do if there is no text.
1834 if( mEventData->mSelectionEnabled )
1836 // Convert from control's coords to text's coords.
1837 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1838 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1840 // Calculates the logical position from the x,y coords.
1841 RepositionSelectionHandles( xPosition,
1843 Controller::NoTextTap::HIGHLIGHT );
1847 void Controller::Impl::OnSelectAllEvent()
1849 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1851 if( NULL == mEventData )
1853 // Nothing to do if there is no text.
1857 if( mEventData->mSelectionEnabled )
1859 // Calculates the logical position from the start.
1860 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
1861 0.f - mModel->mScrollPosition.y,
1862 Controller::NoTextTap::HIGHLIGHT );
1864 mEventData->mLeftSelectionPosition = 0u;
1865 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1869 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1871 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1873 // Nothing to select if handles are in the same place.
1874 selectedText.clear();
1878 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1880 //Get start and end position of selection
1881 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1882 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1884 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1885 const Length numberOfCharacters = utf32Characters.Count();
1887 // Validate the start and end selection points
1888 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1890 //Get text as a UTF8 string
1891 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1893 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1895 // Keep a copy of the current input style.
1896 InputStyle currentInputStyle;
1897 currentInputStyle.Copy( mEventData->mInputStyle );
1899 // Set as input style the style of the first deleted character.
1900 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1902 // Compare if the input style has changed.
1903 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1905 if( hasInputStyleChanged )
1907 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1908 // Queue the input style changed signal.
1909 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1912 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1914 // Mark the paragraphs to be updated.
1915 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1917 mTextUpdateInfo.mCharacterIndex = 0;
1918 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1919 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1920 mTextUpdateInfo.mClearAll = true;
1924 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1925 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1928 // Delete text between handles
1929 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1930 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1931 utf32Characters.Erase( first, last );
1933 // Will show the cursor at the first character of the selection.
1934 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1938 // Will show the cursor at the last character of the selection.
1939 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1942 mEventData->mDecoratorUpdated = true;
1946 void Controller::Impl::ShowClipboard()
1950 mClipboard.ShowClipboard();
1954 void Controller::Impl::HideClipboard()
1956 if( mClipboard && mClipboardHideEnabled )
1958 mClipboard.HideClipboard();
1962 void Controller::Impl::SetClipboardHideEnable(bool enable)
1964 mClipboardHideEnabled = enable;
1967 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1969 //Send string to clipboard
1970 return ( mClipboard && mClipboard.SetItem( source ) );
1973 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1975 std::string selectedText;
1976 RetrieveSelection( selectedText, deleteAfterSending );
1977 CopyStringToClipboard( selectedText );
1978 ChangeState( EventData::EDITING );
1981 void Controller::Impl::RequestGetTextFromClipboard()
1985 mClipboard.RequestItem();
1989 void Controller::Impl::RepositionSelectionHandles()
1991 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1992 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1994 if( selectionStart == selectionEnd )
1996 // Nothing to select if handles are in the same place.
1997 // So, deactive Highlight box.
1998 mEventData->mDecorator->SetHighlightActive( false );
2002 mEventData->mDecorator->ClearHighlights();
2004 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2005 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2006 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2007 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2008 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2009 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2010 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2012 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2013 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2014 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2016 // Swap the indices if the start is greater than the end.
2017 const bool indicesSwapped = selectionStart > selectionEnd;
2019 // Tell the decorator to flip the selection handles if needed.
2020 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2022 if( indicesSwapped )
2024 std::swap( selectionStart, selectionEnd );
2027 // Get the indices to the first and last selected glyphs.
2028 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2029 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2030 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2031 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2033 // Get the lines where the glyphs are laid-out.
2034 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2036 LineIndex lineIndex = 0u;
2037 Length numberOfLines = 0u;
2038 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2039 1u + glyphEnd - glyphStart,
2042 const LineIndex firstLineIndex = lineIndex;
2044 // Create the structure to store some selection box info.
2045 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2046 selectionBoxLinesInfo.Resize( numberOfLines );
2048 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2049 selectionBoxInfo->minX = MAX_FLOAT;
2050 selectionBoxInfo->maxX = MIN_FLOAT;
2052 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2053 float minHighlightX = std::numeric_limits<float>::max();
2054 float maxHighlightX = std::numeric_limits<float>::min();
2056 Vector2 highLightPosition; // The highlight position in decorator's coords.
2058 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2060 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2061 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2064 // Transform to decorator's (control) coords.
2065 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2067 lineRun += firstLineIndex;
2069 // The line height is the addition of the line ascender and the line descender.
2070 // However, the line descender has a negative value, hence the subtraction.
2071 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2073 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2075 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2076 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2077 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2079 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2080 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2081 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2083 // The number of quads of the selection box.
2084 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2085 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2087 // Count the actual number of quads.
2088 unsigned int actualNumberOfQuads = 0u;
2091 // Traverse the glyphs.
2092 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2094 const GlyphInfo& glyph = *( glyphsBuffer + index );
2095 const Vector2& position = *( positionsBuffer + index );
2097 if( splitStartGlyph )
2099 // 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.
2101 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2102 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2103 // Get the direction of the character.
2104 CharacterDirection isCurrentRightToLeft = false;
2105 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2107 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2110 // The end point could be in the middle of the ligature.
2111 // Calculate the number of characters selected.
2112 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2114 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2115 quad.y = selectionBoxInfo->lineOffset;
2116 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2117 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2119 // Store the min and max 'x' for each line.
2120 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2121 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2123 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2124 ++actualNumberOfQuads;
2126 splitStartGlyph = false;
2130 if( splitEndGlyph && ( index == glyphEnd ) )
2132 // 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.
2134 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2135 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2136 // Get the direction of the character.
2137 CharacterDirection isCurrentRightToLeft = false;
2138 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2140 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2143 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2145 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2146 quad.y = selectionBoxInfo->lineOffset;
2147 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2148 quad.w = quad.y + selectionBoxInfo->lineHeight;
2150 // Store the min and max 'x' for each line.
2151 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2152 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2154 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2156 ++actualNumberOfQuads;
2158 splitEndGlyph = false;
2162 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2163 quad.y = selectionBoxInfo->lineOffset;
2164 quad.z = quad.x + glyph.advance;
2165 quad.w = quad.y + selectionBoxInfo->lineHeight;
2167 // Store the min and max 'x' for each line.
2168 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2169 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2171 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2173 ++actualNumberOfQuads;
2175 // Whether to retrieve the next line.
2176 if( index == lastGlyphOfLine )
2179 if( lineIndex < firstLineIndex + numberOfLines )
2181 // Retrieve the next line.
2184 // Get the last glyph of the new line.
2185 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2187 // Keep the offset and height of the current selection box.
2188 const float currentLineOffset = selectionBoxInfo->lineOffset;
2189 const float currentLineHeight = selectionBoxInfo->lineHeight;
2191 // Get the selection box info for the next line.
2194 selectionBoxInfo->minX = MAX_FLOAT;
2195 selectionBoxInfo->maxX = MIN_FLOAT;
2197 // Update the line's vertical offset.
2198 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2200 // The line height is the addition of the line ascender and the line descender.
2201 // However, the line descender has a negative value, hence the subtraction.
2202 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2207 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2208 // The final width is calculated after 'boxifying' the selection.
2209 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2210 endIt = selectionBoxLinesInfo.End();
2214 const SelectionBoxInfo& info = *it;
2216 // Update the size of the highlighted text.
2217 highLightSize.height += info.lineHeight;
2218 minHighlightX = std::min( minHighlightX, info.minX );
2219 maxHighlightX = std::max( maxHighlightX, info.maxX );
2222 // Add extra geometry to 'boxify' the selection.
2224 if( 1u < numberOfLines )
2226 // Boxify the first line.
2227 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2228 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2230 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2231 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2236 quad.y = firstSelectionBoxLineInfo.lineOffset;
2237 quad.z = firstSelectionBoxLineInfo.minX;
2238 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2240 // Boxify at the beginning of the line.
2241 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2243 ++actualNumberOfQuads;
2245 // Update the size of the highlighted text.
2246 minHighlightX = 0.f;
2251 quad.x = firstSelectionBoxLineInfo.maxX;
2252 quad.y = firstSelectionBoxLineInfo.lineOffset;
2253 quad.z = mModel->mVisualModel->mControlSize.width;
2254 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2256 // Boxify at the end of the line.
2257 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2259 ++actualNumberOfQuads;
2261 // Update the size of the highlighted text.
2262 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2265 // Boxify the central lines.
2266 if( 2u < numberOfLines )
2268 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2269 endIt = selectionBoxLinesInfo.End() - 1u;
2273 const SelectionBoxInfo& info = *it;
2276 quad.y = info.lineOffset;
2278 quad.w = info.lineOffset + info.lineHeight;
2280 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2282 ++actualNumberOfQuads;
2285 quad.y = info.lineOffset;
2286 quad.z = mModel->mVisualModel->mControlSize.width;
2287 quad.w = info.lineOffset + info.lineHeight;
2289 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2291 ++actualNumberOfQuads;
2294 // Update the size of the highlighted text.
2295 minHighlightX = 0.f;
2296 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2299 // Boxify the last line.
2300 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2301 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2303 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2304 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2309 quad.y = lastSelectionBoxLineInfo.lineOffset;
2310 quad.z = lastSelectionBoxLineInfo.minX;
2311 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2313 // Boxify at the beginning of the line.
2314 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2316 ++actualNumberOfQuads;
2318 // Update the size of the highlighted text.
2319 minHighlightX = 0.f;
2324 quad.x = lastSelectionBoxLineInfo.maxX;
2325 quad.y = lastSelectionBoxLineInfo.lineOffset;
2326 quad.z = mModel->mVisualModel->mControlSize.width;
2327 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2329 // Boxify at the end of the line.
2330 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2332 ++actualNumberOfQuads;
2334 // Update the size of the highlighted text.
2335 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2339 // Set the actual number of quads.
2340 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2342 // Sets the highlight's size and position. In decorator's coords.
2343 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2344 highLightSize.width = maxHighlightX - minHighlightX;
2346 highLightPosition.x = minHighlightX;
2347 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2348 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2350 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2352 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2354 CursorInfo primaryCursorInfo;
2355 GetCursorPosition( mEventData->mLeftSelectionPosition,
2356 primaryCursorInfo );
2358 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2360 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2362 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2363 primaryCursorInfo.lineHeight );
2365 CursorInfo secondaryCursorInfo;
2366 GetCursorPosition( mEventData->mRightSelectionPosition,
2367 secondaryCursorInfo );
2369 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2371 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2372 secondaryPosition.x,
2373 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2374 secondaryCursorInfo.lineHeight );
2377 // Set the flag to update the decorator.
2378 mEventData->mDecoratorUpdated = true;
2381 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2383 if( NULL == mEventData )
2385 // Nothing to do if there is no text input.
2389 if( IsShowingPlaceholderText() )
2391 // Nothing to do if there is the place-holder text.
2395 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2396 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2397 if( ( 0 == numberOfGlyphs ) ||
2398 ( 0 == numberOfLines ) )
2400 // Nothing to do if there is no text.
2404 // Find which word was selected
2405 CharacterIndex selectionStart( 0 );
2406 CharacterIndex selectionEnd( 0 );
2407 CharacterIndex noTextHitIndex( 0 );
2408 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2409 mModel->mLogicalModel,
2416 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2418 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2420 ChangeState( EventData::SELECTING );
2422 mEventData->mLeftSelectionPosition = selectionStart;
2423 mEventData->mRightSelectionPosition = selectionEnd;
2425 mEventData->mUpdateLeftSelectionPosition = true;
2426 mEventData->mUpdateRightSelectionPosition = true;
2427 mEventData->mUpdateHighlightBox = true;
2429 // It may happen an InputMethodContext commit event arrives before the selection event
2430 // if the InputMethodContext is in pre-edit state. The commit event will set the
2431 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2432 // to false, the highlight box won't be updated.
2433 mEventData->mUpdateCursorPosition = false;
2435 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2437 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2438 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2440 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2442 // Nothing to select. i.e. a white space, out of bounds
2443 ChangeState( EventData::EDITING_WITH_POPUP );
2445 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2447 mEventData->mUpdateCursorPosition = true;
2448 mEventData->mUpdateGrabHandlePosition = true;
2449 mEventData->mScrollAfterUpdatePosition = true;
2450 mEventData->mUpdateInputStyle = true;
2452 else if( Controller::NoTextTap::NO_ACTION == action )
2454 // Nothing to select. i.e. a white space, out of bounds
2455 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2457 mEventData->mUpdateCursorPosition = true;
2458 mEventData->mUpdateGrabHandlePosition = true;
2459 mEventData->mScrollAfterUpdatePosition = true;
2460 mEventData->mUpdateInputStyle = true;
2464 void Controller::Impl::SetPopupButtons()
2467 * Sets the Popup buttons to be shown depending on State.
2469 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2471 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2474 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2476 if( EventData::SELECTING == mEventData->mState )
2478 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2480 if( !IsClipboardEmpty() )
2482 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2483 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2486 if( !mEventData->mAllTextSelected )
2488 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2491 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2493 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2495 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2498 if( !IsClipboardEmpty() )
2500 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2501 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2504 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2506 if ( !IsClipboardEmpty() )
2508 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2509 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2513 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2516 void Controller::Impl::ChangeState( EventData::State newState )
2518 if( NULL == mEventData )
2520 // Nothing to do if there is no text input.
2524 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2526 if( mEventData->mState != newState )
2528 mEventData->mPreviousState = mEventData->mState;
2529 mEventData->mState = newState;
2531 switch( mEventData->mState )
2533 case EventData::INACTIVE:
2535 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2536 mEventData->mDecorator->StopCursorBlink();
2537 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2538 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2539 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2540 mEventData->mDecorator->SetHighlightActive( false );
2541 mEventData->mDecorator->SetPopupActive( false );
2542 mEventData->mDecoratorUpdated = true;
2545 case EventData::INTERRUPTED:
2547 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2548 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2549 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2550 mEventData->mDecorator->SetHighlightActive( false );
2551 mEventData->mDecorator->SetPopupActive( false );
2552 mEventData->mDecoratorUpdated = true;
2555 case EventData::SELECTING:
2557 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2558 mEventData->mDecorator->StopCursorBlink();
2559 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2560 if ( mEventData->mGrabHandleEnabled )
2562 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2563 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 );
2608 else if ( mEventData->mGrabHandleEnabled )
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 if ( mEventData->mGrabHandleEnabled )
2632 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2634 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2635 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2636 mEventData->mDecorator->SetHighlightActive( false );
2637 if( mEventData->mGrabHandlePopupEnabled )
2639 mEventData->mDecorator->SetPopupActive( false );
2641 mEventData->mDecoratorUpdated = true;
2644 case EventData::SELECTION_HANDLE_PANNING:
2646 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2647 mEventData->mDecorator->StopCursorBlink();
2648 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2649 if ( mEventData->mGrabHandleEnabled )
2651 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2652 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2654 mEventData->mDecorator->SetHighlightActive( true );
2655 if( mEventData->mGrabHandlePopupEnabled )
2657 mEventData->mDecorator->SetPopupActive( false );
2659 mEventData->mDecoratorUpdated = true;
2662 case EventData::GRAB_HANDLE_PANNING:
2664 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2666 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2667 if( mEventData->mCursorBlinkEnabled )
2669 mEventData->mDecorator->StartCursorBlink();
2671 if ( mEventData->mGrabHandleEnabled )
2673 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2675 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2676 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2677 mEventData->mDecorator->SetHighlightActive( false );
2678 if( mEventData->mGrabHandlePopupEnabled )
2680 mEventData->mDecorator->SetPopupActive( false );
2682 mEventData->mDecoratorUpdated = true;
2685 case EventData::EDITING_WITH_PASTE_POPUP:
2687 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2689 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2690 if( mEventData->mCursorBlinkEnabled )
2692 mEventData->mDecorator->StartCursorBlink();
2695 if ( mEventData->mGrabHandleEnabled )
2697 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2699 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2700 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2701 mEventData->mDecorator->SetHighlightActive( false );
2703 if( mEventData->mGrabHandlePopupEnabled )
2706 mEventData->mDecorator->SetPopupActive( true );
2708 mEventData->mDecoratorUpdated = true;
2711 case EventData::TEXT_PANNING:
2713 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2714 mEventData->mDecorator->StopCursorBlink();
2715 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2716 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2717 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2719 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2720 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2721 mEventData->mDecorator->SetHighlightActive( true );
2724 if( mEventData->mGrabHandlePopupEnabled )
2726 mEventData->mDecorator->SetPopupActive( false );
2729 mEventData->mDecoratorUpdated = true;
2736 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2737 CursorInfo& cursorInfo )
2739 if( !IsShowingRealText() )
2741 // Do not want to use the place-holder text to set the cursor position.
2743 // Use the line's height of the font's family set to set the cursor's size.
2744 // If there is no font's family set, use the default font.
2745 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2747 cursorInfo.lineOffset = 0.f;
2748 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2749 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2752 if( mModel->mMatchSystemLanguageDirection )
2754 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2757 switch( mModel->mHorizontalAlignment )
2759 case Text::HorizontalAlignment::BEGIN :
2763 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2767 cursorInfo.primaryPosition.x = 0.f;
2771 case Text::HorizontalAlignment::CENTER:
2773 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2776 case Text::HorizontalAlignment::END:
2780 cursorInfo.primaryPosition.x = 0.f;
2784 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2790 // Nothing else to do.
2794 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2795 GetCursorPositionParameters parameters;
2796 parameters.visualModel = mModel->mVisualModel;
2797 parameters.logicalModel = mModel->mLogicalModel;
2798 parameters.metrics = mMetrics;
2799 parameters.logical = logical;
2800 parameters.isMultiline = isMultiLine;
2802 Text::GetCursorPosition( parameters,
2805 // Adds Outline offset.
2806 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2807 cursorInfo.primaryPosition.x += outlineWidth;
2808 cursorInfo.primaryPosition.y += outlineWidth;
2809 cursorInfo.secondaryPosition.x += outlineWidth;
2810 cursorInfo.secondaryPosition.y += outlineWidth;
2814 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2816 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2817 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2819 if( 0.f > cursorInfo.primaryPosition.x )
2821 cursorInfo.primaryPosition.x = 0.f;
2824 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2825 if( cursorInfo.primaryPosition.x > edgeWidth )
2827 cursorInfo.primaryPosition.x = edgeWidth;
2832 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2834 if( NULL == mEventData )
2836 // Nothing to do if there is no text input.
2840 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2842 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2843 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2845 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2846 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2848 if( numberOfCharacters > 1u )
2850 const Script script = mModel->mLogicalModel->GetScript( index );
2851 if( HasLigatureMustBreak( script ) )
2853 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2854 numberOfCharacters = 1u;
2859 while( 0u == numberOfCharacters )
2862 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2866 if( index < mEventData->mPrimaryCursorPosition )
2868 cursorIndex -= numberOfCharacters;
2872 cursorIndex += numberOfCharacters;
2875 // Will update the cursor hook position.
2876 mEventData->mUpdateCursorHookPosition = true;
2881 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2883 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2884 if( NULL == mEventData )
2886 // Nothing to do if there is no text input.
2887 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2891 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2893 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2895 // Sets the cursor position.
2896 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2899 cursorInfo.primaryCursorHeight,
2900 cursorInfo.lineHeight );
2901 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2903 if( mEventData->mUpdateGrabHandlePosition )
2905 // Sets the grab handle position.
2906 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2908 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2909 cursorInfo.lineHeight );
2912 if( cursorInfo.isSecondaryCursor )
2914 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2915 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2916 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2917 cursorInfo.secondaryCursorHeight,
2918 cursorInfo.lineHeight );
2919 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2922 // Set which cursors are active according the state.
2923 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2925 if( cursorInfo.isSecondaryCursor )
2927 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2931 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2936 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2939 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2942 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2943 const CursorInfo& cursorInfo )
2945 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2946 ( RIGHT_SELECTION_HANDLE != handleType ) )
2951 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2953 // Sets the handle's position.
2954 mEventData->mDecorator->SetPosition( handleType,
2956 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2957 cursorInfo.lineHeight );
2959 // If selection handle at start of the text and other at end of the text then all text is selected.
2960 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2961 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2962 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2965 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2967 // Clamp between -space & -alignment offset.
2969 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2971 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2972 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2973 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2975 mEventData->mDecoratorUpdated = true;
2979 mModel->mScrollPosition.x = 0.f;
2983 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2985 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2987 // Nothing to do if the text is single line.
2991 // Clamp between -space & 0.
2992 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2994 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2995 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2996 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2998 mEventData->mDecoratorUpdated = true;
3002 mModel->mScrollPosition.y = 0.f;
3006 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3008 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3010 // position is in actor's coords.
3011 const float positionEndX = position.x + cursorWidth;
3012 const float positionEndY = position.y + lineHeight;
3014 // Transform the position to decorator coords.
3015 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3016 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3018 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3019 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3021 if( decoratorPositionBeginX < 0.f )
3023 mModel->mScrollPosition.x = -position.x;
3025 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3027 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3030 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3032 if( decoratorPositionBeginY < 0.f )
3034 mModel->mScrollPosition.y = -position.y;
3036 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3038 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3043 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3045 // Get the current cursor position in decorator coords.
3046 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3048 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3052 // Calculate the offset to match the cursor position before the character was deleted.
3053 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3055 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3056 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3058 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3059 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3063 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3064 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3066 // Makes the new cursor position visible if needed.
3067 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3070 void Controller::Impl::RequestRelayout()
3072 if( NULL != mControlInterface )
3074 mControlInterface->RequestTextRelayout();
3080 } // namespace Toolkit