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 )
117 EventData::~EventData()
120 bool Controller::Impl::ProcessInputEvents()
122 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
123 if( NULL == mEventData )
125 // Nothing to do if there is no text input.
126 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
130 if( mEventData->mDecorator )
132 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
133 iter != mEventData->mEventQueue.end();
138 case Event::CURSOR_KEY_EVENT:
140 OnCursorKeyEvent( *iter );
143 case Event::TAP_EVENT:
148 case Event::LONG_PRESS_EVENT:
150 OnLongPressEvent( *iter );
153 case Event::PAN_EVENT:
158 case Event::GRAB_HANDLE_EVENT:
159 case Event::LEFT_SELECTION_HANDLE_EVENT:
160 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
162 OnHandleEvent( *iter );
167 OnSelectEvent( *iter );
170 case Event::SELECT_ALL:
179 if( mEventData->mUpdateCursorPosition ||
180 mEventData->mUpdateHighlightBox )
182 NotifyInputMethodContext();
185 // The cursor must also be repositioned after inserts into the model
186 if( mEventData->mUpdateCursorPosition )
188 // Updates the cursor position and scrolls the text to make it visible.
189 CursorInfo cursorInfo;
190 // Calculate the cursor position from the new cursor index.
191 GetCursorPosition( mEventData->mPrimaryCursorPosition,
194 if( mEventData->mUpdateCursorHookPosition )
196 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
197 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
198 mEventData->mUpdateCursorHookPosition = false;
201 // Scroll first the text after delete ...
202 if( mEventData->mScrollAfterDelete )
204 ScrollTextToMatchCursor( cursorInfo );
207 // ... then, text can be scrolled to make the cursor visible.
208 if( mEventData->mScrollAfterUpdatePosition )
210 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
211 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
213 mEventData->mScrollAfterUpdatePosition = false;
214 mEventData->mScrollAfterDelete = false;
216 UpdateCursorPosition( cursorInfo );
218 mEventData->mDecoratorUpdated = true;
219 mEventData->mUpdateCursorPosition = false;
220 mEventData->mUpdateGrabHandlePosition = false;
224 CursorInfo leftHandleInfo;
225 CursorInfo rightHandleInfo;
227 if( mEventData->mUpdateHighlightBox )
229 GetCursorPosition( mEventData->mLeftSelectionPosition,
232 GetCursorPosition( mEventData->mRightSelectionPosition,
235 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
237 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
239 CursorInfo& infoLeft = leftHandleInfo;
241 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
242 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
244 CursorInfo& infoRight = rightHandleInfo;
246 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
247 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
251 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
253 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
254 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
259 if( mEventData->mUpdateLeftSelectionPosition )
261 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
265 mEventData->mDecoratorUpdated = true;
266 mEventData->mUpdateLeftSelectionPosition = false;
269 if( mEventData->mUpdateRightSelectionPosition )
271 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
275 mEventData->mDecoratorUpdated = true;
276 mEventData->mUpdateRightSelectionPosition = false;
279 if( mEventData->mUpdateHighlightBox )
281 RepositionSelectionHandles();
283 mEventData->mUpdateLeftSelectionPosition = false;
284 mEventData->mUpdateRightSelectionPosition = false;
285 mEventData->mUpdateHighlightBox = false;
286 mEventData->mIsLeftHandleSelected = false;
287 mEventData->mIsRightHandleSelected = false;
290 mEventData->mScrollAfterUpdatePosition = false;
293 if( mEventData->mUpdateInputStyle )
295 // Keep a copy of the current input style.
296 InputStyle currentInputStyle;
297 currentInputStyle.Copy( mEventData->mInputStyle );
299 // Set the default style first.
300 RetrieveDefaultInputStyle( mEventData->mInputStyle );
302 // Get the character index from the cursor index.
303 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
305 // Retrieve the style from the style runs stored in the logical model.
306 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
308 // Compare if the input style has changed.
309 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
311 if( hasInputStyleChanged )
313 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
314 // Queue the input style changed signal.
315 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
318 mEventData->mUpdateInputStyle = false;
321 mEventData->mEventQueue.clear();
323 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
325 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
326 mEventData->mDecoratorUpdated = false;
328 return decoratorUpdated;
331 void Controller::Impl::NotifyInputMethodContext()
333 if( mEventData && mEventData->mInputMethodContext )
335 CharacterIndex cursorPosition = GetLogicalCursorPosition();
337 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
339 // Update the cursor position by removing the initial white spaces.
340 if( cursorPosition < numberOfWhiteSpaces )
346 cursorPosition -= numberOfWhiteSpaces;
349 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
350 mEventData->mInputMethodContext.NotifyCursorPosition();
354 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
356 if ( mEventData && mEventData->mInputMethodContext )
358 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
359 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
363 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
365 CharacterIndex cursorPosition = 0u;
369 if( ( EventData::SELECTING == mEventData->mState ) ||
370 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
372 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
376 cursorPosition = mEventData->mPrimaryCursorPosition;
380 return cursorPosition;
383 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
385 Length numberOfWhiteSpaces = 0u;
387 // Get the buffer to the text.
388 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
390 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
391 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
393 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
399 return numberOfWhiteSpaces;
402 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
404 // Get the total number of characters.
405 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
407 // Retrieve the text.
408 if( 0u != numberOfCharacters )
410 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
414 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
416 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
417 mTextUpdateInfo.mStartGlyphIndex = 0u;
418 mTextUpdateInfo.mStartLineIndex = 0u;
419 numberOfCharacters = 0u;
421 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
422 if( 0u == numberOfParagraphs )
424 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
425 numberOfCharacters = 0u;
427 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
429 // Nothing else to do if there are no paragraphs.
433 // Find the paragraphs to be updated.
434 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
435 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
437 // Text is being added at the end of the current text.
438 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
440 // Text is being added in a new paragraph after the last character of the text.
441 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
442 numberOfCharacters = 0u;
443 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
445 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
446 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
448 // Nothing else to do;
452 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
456 Length numberOfCharactersToUpdate = 0u;
457 if( mTextUpdateInfo.mFullRelayoutNeeded )
459 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
463 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
465 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
466 numberOfCharactersToUpdate,
467 paragraphsToBeUpdated );
470 if( 0u != paragraphsToBeUpdated.Count() )
472 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
473 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
474 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
476 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
477 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
479 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
480 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
481 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
482 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
484 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
485 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
487 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
495 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
496 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
499 void Controller::Impl::ClearFullModelData( OperationsMask operations )
501 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
503 mModel->mLogicalModel->mLineBreakInfo.Clear();
504 mModel->mLogicalModel->mParagraphInfo.Clear();
507 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
509 mModel->mLogicalModel->mLineBreakInfo.Clear();
512 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
514 mModel->mLogicalModel->mScriptRuns.Clear();
517 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
519 mModel->mLogicalModel->mFontRuns.Clear();
522 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
524 if( NO_OPERATION != ( BIDI_INFO & operations ) )
526 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
527 mModel->mLogicalModel->mCharacterDirections.Clear();
530 if( NO_OPERATION != ( REORDER & operations ) )
532 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
533 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
534 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
538 BidirectionalLineInfoRun& bidiLineInfo = *it;
540 free( bidiLineInfo.visualToLogicalMap );
541 bidiLineInfo.visualToLogicalMap = NULL;
543 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
547 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
549 mModel->mVisualModel->mGlyphs.Clear();
550 mModel->mVisualModel->mGlyphsToCharacters.Clear();
551 mModel->mVisualModel->mCharactersToGlyph.Clear();
552 mModel->mVisualModel->mCharactersPerGlyph.Clear();
553 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
554 mModel->mVisualModel->mGlyphPositions.Clear();
557 if( NO_OPERATION != ( LAYOUT & operations ) )
559 mModel->mVisualModel->mLines.Clear();
562 if( NO_OPERATION != ( COLOR & operations ) )
564 mModel->mVisualModel->mColorIndices.Clear();
568 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
570 const CharacterIndex endIndexPlusOne = endIndex + 1u;
572 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
574 // Clear the line break info.
575 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
577 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
578 lineBreakInfoBuffer + endIndexPlusOne );
580 // Clear the paragraphs.
581 ClearCharacterRuns( startIndex,
583 mModel->mLogicalModel->mParagraphInfo );
586 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
588 // Clear the word break info.
589 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
591 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
592 wordBreakInfoBuffer + endIndexPlusOne );
595 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
597 // Clear the scripts.
598 ClearCharacterRuns( startIndex,
600 mModel->mLogicalModel->mScriptRuns );
603 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
606 ClearCharacterRuns( startIndex,
608 mModel->mLogicalModel->mFontRuns );
611 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
613 if( NO_OPERATION != ( BIDI_INFO & operations ) )
615 // Clear the bidirectional paragraph info.
616 ClearCharacterRuns( startIndex,
618 mModel->mLogicalModel->mBidirectionalParagraphInfo );
620 // Clear the character's directions.
621 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
623 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
624 characterDirectionsBuffer + endIndexPlusOne );
627 if( NO_OPERATION != ( REORDER & operations ) )
629 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
630 uint32_t endRemoveIndex = startRemoveIndex;
631 ClearCharacterRuns( startIndex,
633 mModel->mLogicalModel->mBidirectionalLineInfo,
637 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
639 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
640 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
641 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
645 BidirectionalLineInfoRun& bidiLineInfo = *it;
647 free( bidiLineInfo.visualToLogicalMap );
648 bidiLineInfo.visualToLogicalMap = NULL;
651 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
652 bidirectionalLineInfoBuffer + endRemoveIndex );
657 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
659 const CharacterIndex endIndexPlusOne = endIndex + 1u;
660 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
662 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
663 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
664 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
666 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
667 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
669 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
671 // Update the character to glyph indices.
672 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
673 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
677 CharacterIndex& index = *it;
678 index -= numberOfGlyphsRemoved;
681 // Clear the character to glyph conversion table.
682 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
683 charactersToGlyphBuffer + endIndexPlusOne );
685 // Clear the glyphs per character table.
686 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
687 glyphsPerCharacterBuffer + endIndexPlusOne );
689 // Clear the glyphs buffer.
690 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
691 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
692 glyphsBuffer + endGlyphIndexPlusOne );
694 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
696 // Update the glyph to character indices.
697 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
698 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
702 CharacterIndex& index = *it;
703 index -= numberOfCharactersRemoved;
706 // Clear the glyphs to characters buffer.
707 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
708 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
710 // Clear the characters per glyph buffer.
711 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
712 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
713 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
715 // Clear the positions buffer.
716 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
717 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
718 positionsBuffer + endGlyphIndexPlusOne );
721 if( NO_OPERATION != ( LAYOUT & operations ) )
724 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
725 uint32_t endRemoveIndex = startRemoveIndex;
726 ClearCharacterRuns( startIndex,
728 mModel->mVisualModel->mLines,
732 // Will update the glyph runs.
733 startRemoveIndex = mModel->mVisualModel->mLines.Count();
734 endRemoveIndex = startRemoveIndex;
735 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
736 endGlyphIndexPlusOne - 1u,
737 mModel->mVisualModel->mLines,
741 // Set the line index from where to insert the new laid-out lines.
742 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
744 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
745 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
746 linesBuffer + endRemoveIndex );
749 if( NO_OPERATION != ( COLOR & operations ) )
751 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
753 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
754 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
755 colorIndexBuffer + endGlyphIndexPlusOne );
760 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
762 if( mTextUpdateInfo.mClearAll ||
763 ( ( 0u == startIndex ) &&
764 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
766 ClearFullModelData( operations );
770 // Clear the model data related with characters.
771 ClearCharacterModelData( startIndex, endIndex, operations );
773 // Clear the model data related with glyphs.
774 ClearGlyphModelData( startIndex, endIndex, operations );
777 // The estimated number of lines. Used to avoid reallocations when layouting.
778 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
780 mModel->mVisualModel->ClearCaches();
783 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
785 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
787 // Calculate the operations to be done.
788 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
790 if( NO_OPERATION == operations )
792 // Nothing to do if no operations are pending and required.
796 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
797 Vector<Character> displayCharacters;
798 bool useHiddenText = false;
799 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
801 mHiddenInput->Substitute( srcCharacters,displayCharacters );
802 useHiddenText = true;
805 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
806 const Length numberOfCharacters = utf32Characters.Count();
808 // Index to the first character of the first paragraph to be updated.
809 CharacterIndex startIndex = 0u;
810 // Number of characters of the paragraphs to be removed.
811 Length paragraphCharacters = 0u;
813 CalculateTextUpdateIndices( paragraphCharacters );
815 // Check whether the indices for updating the text is valid
816 if ( numberOfCharacters > 0u &&
817 ( mTextUpdateInfo.mParagraphCharacterIndex >= numberOfCharacters ||
818 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
820 std::string currentText;
821 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
823 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
824 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
826 // Dump mTextUpdateInfo
827 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
828 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
829 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
830 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
831 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
832 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
833 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
834 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
835 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
836 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
837 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
838 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
839 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
844 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
846 if( mTextUpdateInfo.mClearAll ||
847 ( 0u != paragraphCharacters ) )
849 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
852 mTextUpdateInfo.mClearAll = false;
854 // Whether the model is updated.
855 bool updated = false;
857 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
858 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
860 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
862 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
863 // calculate the bidirectional info for each 'paragraph'.
864 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
865 // is not shaped together).
866 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
868 SetLineBreakInfo( utf32Characters,
870 requestedNumberOfCharacters,
873 // Create the paragraph info.
874 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
875 requestedNumberOfCharacters );
879 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
880 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
882 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
883 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
885 SetWordBreakInfo( utf32Characters,
887 requestedNumberOfCharacters,
892 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
893 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
895 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
896 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
898 if( getScripts || validateFonts )
900 // Validates the fonts assigned by the application or assigns default ones.
901 // It makes sure all the characters are going to be rendered by the correct font.
902 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
906 // Retrieves the scripts used in the text.
907 multilanguageSupport.SetScripts( utf32Characters,
909 requestedNumberOfCharacters,
915 // Validate the fonts set through the mark-up string.
916 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
918 // Get the default font's description.
919 TextAbstraction::FontDescription defaultFontDescription;
920 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
922 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
924 // If the placeholder font is set specifically, only placeholder font is changed.
925 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
926 if( mEventData->mPlaceholderFont->sizeDefined )
928 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
931 else if( NULL != mFontDefaults )
933 // Set the normal font and the placeholder font.
934 defaultFontDescription = mFontDefaults->mFontDescription;
935 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
938 // Validates the fonts. If there is a character with no assigned font it sets a default one.
939 // After this call, fonts are validated.
940 multilanguageSupport.ValidateFonts( utf32Characters,
943 defaultFontDescription,
946 requestedNumberOfCharacters,
952 Vector<Character> mirroredUtf32Characters;
953 bool textMirrored = false;
954 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
955 if( NO_OPERATION != ( BIDI_INFO & operations ) )
957 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
958 bidirectionalInfo.Reserve( numberOfParagraphs );
960 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
961 SetBidirectionalInfo( utf32Characters,
965 requestedNumberOfCharacters,
967 mModel->mMatchSystemLanguageDirection,
970 if( 0u != bidirectionalInfo.Count() )
972 // Only set the character directions if there is right to left characters.
973 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
974 GetCharactersDirection( bidirectionalInfo,
977 requestedNumberOfCharacters,
980 // This paragraph has right to left text. Some characters may need to be mirrored.
981 // TODO: consider if the mirrored string can be stored as well.
983 textMirrored = GetMirroredText( utf32Characters,
987 requestedNumberOfCharacters,
988 mirroredUtf32Characters );
992 // There is no right to left characters. Clear the directions vector.
993 mModel->mLogicalModel->mCharacterDirections.Clear();
998 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
999 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1000 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1001 Vector<GlyphIndex> newParagraphGlyphs;
1002 newParagraphGlyphs.Reserve( numberOfParagraphs );
1004 const Length currentNumberOfGlyphs = glyphs.Count();
1005 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1007 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1009 ShapeText( textToShape,
1014 mTextUpdateInfo.mStartGlyphIndex,
1015 requestedNumberOfCharacters,
1017 glyphsToCharactersMap,
1019 newParagraphGlyphs );
1021 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1022 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1023 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1027 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1029 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1031 GlyphInfo* glyphsBuffer = glyphs.Begin();
1032 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1034 // Update the width and advance of all new paragraph characters.
1035 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1037 const GlyphIndex index = *it;
1038 GlyphInfo& glyph = *( glyphsBuffer + index );
1040 glyph.xBearing = 0.f;
1042 glyph.advance = 0.f;
1047 if( NO_OPERATION != ( COLOR & operations ) )
1049 // Set the color runs in glyphs.
1050 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1051 mModel->mVisualModel->mCharactersToGlyph,
1052 mModel->mVisualModel->mGlyphsPerCharacter,
1054 mTextUpdateInfo.mStartGlyphIndex,
1055 requestedNumberOfCharacters,
1056 mModel->mVisualModel->mColors,
1057 mModel->mVisualModel->mColorIndices );
1062 if( ( NULL != mEventData ) &&
1063 mEventData->mPreEditFlag &&
1064 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1066 // Add the underline for the pre-edit text.
1067 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1068 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1070 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1071 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1072 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1073 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1075 GlyphRun underlineRun;
1076 underlineRun.glyphIndex = glyphStart;
1077 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1079 // TODO: At the moment the underline runs are only for pre-edit.
1080 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1083 // The estimated number of lines. Used to avoid reallocations when layouting.
1084 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1086 // Set the previous number of characters for the next time the text is updated.
1087 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1092 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1094 // Sets the default text's color.
1095 inputStyle.textColor = mTextColor;
1096 inputStyle.isDefaultColor = true;
1098 inputStyle.familyName.clear();
1099 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1100 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1101 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1102 inputStyle.size = 0.f;
1104 inputStyle.lineSpacing = 0.f;
1106 inputStyle.underlineProperties.clear();
1107 inputStyle.shadowProperties.clear();
1108 inputStyle.embossProperties.clear();
1109 inputStyle.outlineProperties.clear();
1111 inputStyle.isFamilyDefined = false;
1112 inputStyle.isWeightDefined = false;
1113 inputStyle.isWidthDefined = false;
1114 inputStyle.isSlantDefined = false;
1115 inputStyle.isSizeDefined = false;
1117 inputStyle.isLineSpacingDefined = false;
1119 inputStyle.isUnderlineDefined = false;
1120 inputStyle.isShadowDefined = false;
1121 inputStyle.isEmbossDefined = false;
1122 inputStyle.isOutlineDefined = false;
1124 // Sets the default font's family name, weight, width, slant and size.
1127 if( mFontDefaults->familyDefined )
1129 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1130 inputStyle.isFamilyDefined = true;
1133 if( mFontDefaults->weightDefined )
1135 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1136 inputStyle.isWeightDefined = true;
1139 if( mFontDefaults->widthDefined )
1141 inputStyle.width = mFontDefaults->mFontDescription.width;
1142 inputStyle.isWidthDefined = true;
1145 if( mFontDefaults->slantDefined )
1147 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1148 inputStyle.isSlantDefined = true;
1151 if( mFontDefaults->sizeDefined )
1153 inputStyle.size = mFontDefaults->mDefaultPointSize;
1154 inputStyle.isSizeDefined = true;
1159 float Controller::Impl::GetDefaultFontLineHeight()
1161 FontId defaultFontId = 0u;
1162 if( NULL == mFontDefaults )
1164 TextAbstraction::FontDescription fontDescription;
1165 defaultFontId = mFontClient.GetFontId( fontDescription );
1169 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1172 Text::FontMetrics fontMetrics;
1173 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1175 return( fontMetrics.ascender - fontMetrics.descender );
1178 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1180 if( NULL == mEventData || !IsShowingRealText() )
1182 // Nothing to do if there is no text input.
1186 int keyCode = event.p1.mInt;
1187 bool isShiftModifier = event.p2.mBool;
1189 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1191 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1193 if( mEventData->mPrimaryCursorPosition > 0u )
1195 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1197 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1201 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1205 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1207 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1209 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1211 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1215 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1219 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1221 // Ignore Shift-Up for text selection for now.
1223 // Get first the line index of the current cursor position index.
1224 CharacterIndex characterIndex = 0u;
1226 if( mEventData->mPrimaryCursorPosition > 0u )
1228 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1231 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1232 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1234 // Retrieve the cursor position info.
1235 CursorInfo cursorInfo;
1236 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1239 // Get the line above.
1240 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1242 // Get the next hit 'y' point.
1243 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1245 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1246 bool matchedCharacter = false;
1247 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1248 mModel->mLogicalModel,
1250 mEventData->mCursorHookPositionX,
1252 CharacterHitTest::TAP,
1255 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1257 // Ignore Shift-Down for text selection for now.
1259 // Get first the line index of the current cursor position index.
1260 CharacterIndex characterIndex = 0u;
1262 if( mEventData->mPrimaryCursorPosition > 0u )
1264 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1267 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1269 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1271 // Retrieve the cursor position info.
1272 CursorInfo cursorInfo;
1273 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1276 // Get the line below.
1277 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1279 // Get the next hit 'y' point.
1280 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1282 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1283 bool matchedCharacter = false;
1284 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1285 mModel->mLogicalModel,
1287 mEventData->mCursorHookPositionX,
1289 CharacterHitTest::TAP,
1294 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1296 // Update selection position after moving the cursor
1297 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1298 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1301 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1303 // Handle text selection
1304 bool selecting = false;
1306 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1308 // Shift-Left/Right to select the text
1309 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1310 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1312 mEventData->mRightSelectionPosition += cursorPositionDelta;
1316 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1318 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1324 // Notify the cursor position to the InputMethodContext.
1325 if( mEventData->mInputMethodContext )
1327 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1328 mEventData->mInputMethodContext.NotifyCursorPosition();
1331 ChangeState( EventData::SELECTING );
1333 mEventData->mUpdateLeftSelectionPosition = true;
1334 mEventData->mUpdateRightSelectionPosition = true;
1335 mEventData->mUpdateGrabHandlePosition = true;
1336 mEventData->mUpdateHighlightBox = true;
1338 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1339 if( mEventData->mGrabHandlePopupEnabled )
1341 mEventData->mDecorator->SetPopupActive( false );
1347 // Handle normal cursor move
1348 ChangeState( EventData::EDITING );
1349 mEventData->mUpdateCursorPosition = true;
1352 mEventData->mUpdateInputStyle = true;
1353 mEventData->mScrollAfterUpdatePosition = true;
1356 void Controller::Impl::OnTapEvent( const Event& event )
1358 if( NULL != mEventData )
1360 const unsigned int tapCount = event.p1.mUint;
1362 if( 1u == tapCount )
1364 if( IsShowingRealText() )
1366 // Convert from control's coords to text's coords.
1367 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1368 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1370 // Keep the tap 'x' position. Used to move the cursor.
1371 mEventData->mCursorHookPositionX = xPosition;
1373 // Whether to touch point hits on a glyph.
1374 bool matchedCharacter = false;
1375 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1376 mModel->mLogicalModel,
1380 CharacterHitTest::TAP,
1383 // When the cursor position is changing, delay cursor blinking
1384 mEventData->mDecorator->DelayCursorBlink();
1388 mEventData->mPrimaryCursorPosition = 0u;
1391 // Update selection position after tapping
1392 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1393 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1395 mEventData->mUpdateCursorPosition = true;
1396 mEventData->mUpdateGrabHandlePosition = true;
1397 mEventData->mScrollAfterUpdatePosition = true;
1398 mEventData->mUpdateInputStyle = true;
1400 // Notify the cursor position to the InputMethodContext.
1401 if( mEventData->mInputMethodContext )
1403 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1404 mEventData->mInputMethodContext.NotifyCursorPosition();
1407 else if( 2u == tapCount )
1409 if( mEventData->mSelectionEnabled )
1411 // Convert from control's coords to text's coords.
1412 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1413 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1415 // Calculates the logical position from the x,y coords.
1416 RepositionSelectionHandles( xPosition,
1418 mEventData->mDoubleTapAction );
1424 void Controller::Impl::OnPanEvent( const Event& event )
1426 if( NULL == mEventData )
1428 // Nothing to do if there is no text input.
1432 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1433 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1435 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1437 // Nothing to do if scrolling is not enabled.
1441 const int state = event.p1.mInt;
1445 case Gesture::Started:
1447 // Will remove the cursor, handles or text's popup, ...
1448 ChangeState( EventData::TEXT_PANNING );
1451 case Gesture::Continuing:
1453 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1454 const Vector2 currentScroll = mModel->mScrollPosition;
1456 if( isHorizontalScrollEnabled )
1458 const float displacementX = event.p2.mFloat;
1459 mModel->mScrollPosition.x += displacementX;
1461 ClampHorizontalScroll( layoutSize );
1464 if( isVerticalScrollEnabled )
1466 const float displacementY = event.p3.mFloat;
1467 mModel->mScrollPosition.y += displacementY;
1469 ClampVerticalScroll( layoutSize );
1472 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1475 case Gesture::Finished:
1476 case Gesture::Cancelled: // FALLTHROUGH
1478 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1479 ChangeState( mEventData->mPreviousState );
1487 void Controller::Impl::OnLongPressEvent( const Event& event )
1489 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1491 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1493 ChangeState( EventData::EDITING_WITH_POPUP );
1494 mEventData->mDecoratorUpdated = true;
1495 mEventData->mUpdateInputStyle = true;
1499 if( mEventData->mSelectionEnabled )
1501 // Convert from control's coords to text's coords.
1502 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1503 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1505 // Calculates the logical position from the x,y coords.
1506 RepositionSelectionHandles( xPosition,
1508 mEventData->mLongPressAction );
1513 void Controller::Impl::OnHandleEvent( const Event& event )
1515 if( NULL == mEventData )
1517 // Nothing to do if there is no text input.
1521 const unsigned int state = event.p1.mUint;
1522 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1523 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1525 if( HANDLE_PRESSED == state )
1527 // Convert from decorator's coords to text's coords.
1528 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1529 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1531 // Need to calculate the handle's new position.
1532 bool matchedCharacter = false;
1533 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1534 mModel->mLogicalModel,
1538 CharacterHitTest::SCROLL,
1541 if( Event::GRAB_HANDLE_EVENT == event.type )
1543 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1545 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1547 // Updates the cursor position if the handle's new position is different than the current one.
1548 mEventData->mUpdateCursorPosition = true;
1549 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1550 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1551 mEventData->mPrimaryCursorPosition = handleNewPosition;
1554 // 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.
1555 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1557 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1559 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1561 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1562 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1564 // Updates the highlight box if the handle's new position is different than the current one.
1565 mEventData->mUpdateHighlightBox = true;
1566 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1567 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1568 mEventData->mLeftSelectionPosition = handleNewPosition;
1571 // 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.
1572 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1574 // Will define the order to scroll the text to match the handle position.
1575 mEventData->mIsLeftHandleSelected = true;
1576 mEventData->mIsRightHandleSelected = false;
1578 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1580 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1582 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1583 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1585 // Updates the highlight box if the handle's new position is different than the current one.
1586 mEventData->mUpdateHighlightBox = true;
1587 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1588 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1589 mEventData->mRightSelectionPosition = handleNewPosition;
1592 // 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.
1593 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1595 // Will define the order to scroll the text to match the handle position.
1596 mEventData->mIsLeftHandleSelected = false;
1597 mEventData->mIsRightHandleSelected = true;
1599 } // end ( HANDLE_PRESSED == state )
1600 else if( ( HANDLE_RELEASED == state ) ||
1601 handleStopScrolling )
1603 CharacterIndex handlePosition = 0u;
1604 if( handleStopScrolling || isSmoothHandlePanEnabled )
1606 // Convert from decorator's coords to text's coords.
1607 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1608 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1610 bool matchedCharacter = false;
1611 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1612 mModel->mLogicalModel,
1616 CharacterHitTest::SCROLL,
1620 if( Event::GRAB_HANDLE_EVENT == event.type )
1622 mEventData->mUpdateCursorPosition = true;
1623 mEventData->mUpdateGrabHandlePosition = true;
1624 mEventData->mUpdateInputStyle = true;
1626 if( !IsClipboardEmpty() )
1628 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1631 if( handleStopScrolling || isSmoothHandlePanEnabled )
1633 mEventData->mScrollAfterUpdatePosition = true;
1634 mEventData->mPrimaryCursorPosition = handlePosition;
1637 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1639 ChangeState( EventData::SELECTING );
1641 mEventData->mUpdateHighlightBox = true;
1642 mEventData->mUpdateLeftSelectionPosition = true;
1643 mEventData->mUpdateRightSelectionPosition = true;
1645 if( handleStopScrolling || isSmoothHandlePanEnabled )
1647 mEventData->mScrollAfterUpdatePosition = true;
1649 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1650 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1652 mEventData->mLeftSelectionPosition = handlePosition;
1656 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1658 ChangeState( EventData::SELECTING );
1660 mEventData->mUpdateHighlightBox = true;
1661 mEventData->mUpdateRightSelectionPosition = true;
1662 mEventData->mUpdateLeftSelectionPosition = true;
1664 if( handleStopScrolling || isSmoothHandlePanEnabled )
1666 mEventData->mScrollAfterUpdatePosition = true;
1667 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1668 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1670 mEventData->mRightSelectionPosition = handlePosition;
1675 mEventData->mDecoratorUpdated = true;
1676 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1677 else if( HANDLE_SCROLLING == state )
1679 const float xSpeed = event.p2.mFloat;
1680 const float ySpeed = event.p3.mFloat;
1681 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1682 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1684 mModel->mScrollPosition.x += xSpeed;
1685 mModel->mScrollPosition.y += ySpeed;
1687 ClampHorizontalScroll( layoutSize );
1688 ClampVerticalScroll( layoutSize );
1690 bool endOfScroll = false;
1691 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1693 // Notify the decorator there is no more text to scroll.
1694 // The decorator won't send more scroll events.
1695 mEventData->mDecorator->NotifyEndOfScroll();
1696 // Still need to set the position of the handle.
1700 // Set the position of the handle.
1701 const bool scrollRightDirection = xSpeed > 0.f;
1702 const bool scrollBottomDirection = ySpeed > 0.f;
1703 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1704 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1706 if( Event::GRAB_HANDLE_EVENT == event.type )
1708 ChangeState( EventData::GRAB_HANDLE_PANNING );
1710 // Get the grab handle position in decorator coords.
1711 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1713 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1715 // Position the grag handle close to either the left or right edge.
1716 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1719 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1721 position.x = mEventData->mCursorHookPositionX;
1723 // Position the grag handle close to either the top or bottom edge.
1724 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1727 // Get the new handle position.
1728 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1729 bool matchedCharacter = false;
1730 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1731 mModel->mLogicalModel,
1733 position.x - mModel->mScrollPosition.x,
1734 position.y - mModel->mScrollPosition.y,
1735 CharacterHitTest::SCROLL,
1738 if( mEventData->mPrimaryCursorPosition != handlePosition )
1740 mEventData->mUpdateCursorPosition = true;
1741 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1742 mEventData->mScrollAfterUpdatePosition = true;
1743 mEventData->mPrimaryCursorPosition = handlePosition;
1745 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1747 // Updates the decorator if the soft handle panning is enabled.
1748 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1750 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1752 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1754 // Get the selection handle position in decorator coords.
1755 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1757 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1759 // Position the selection handle close to either the left or right edge.
1760 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1763 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1765 position.x = mEventData->mCursorHookPositionX;
1767 // Position the grag handle close to either the top or bottom edge.
1768 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1771 // Get the new handle position.
1772 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1773 bool matchedCharacter = false;
1774 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1775 mModel->mLogicalModel,
1777 position.x - mModel->mScrollPosition.x,
1778 position.y - mModel->mScrollPosition.y,
1779 CharacterHitTest::SCROLL,
1782 if( leftSelectionHandleEvent )
1784 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1786 if( differentHandles || endOfScroll )
1788 mEventData->mUpdateHighlightBox = true;
1789 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1790 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1791 mEventData->mLeftSelectionPosition = handlePosition;
1796 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1797 if( differentHandles || endOfScroll )
1799 mEventData->mUpdateHighlightBox = true;
1800 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1801 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1802 mEventData->mRightSelectionPosition = handlePosition;
1806 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1808 RepositionSelectionHandles();
1810 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1813 mEventData->mDecoratorUpdated = true;
1814 } // end ( HANDLE_SCROLLING == state )
1817 void Controller::Impl::OnSelectEvent( const Event& event )
1819 if( NULL == mEventData )
1821 // Nothing to do if there is no text.
1825 if( mEventData->mSelectionEnabled )
1827 // Convert from control's coords to text's coords.
1828 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1829 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1831 // Calculates the logical position from the x,y coords.
1832 RepositionSelectionHandles( xPosition,
1834 Controller::NoTextTap::HIGHLIGHT );
1838 void Controller::Impl::OnSelectAllEvent()
1840 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1842 if( NULL == mEventData )
1844 // Nothing to do if there is no text.
1848 if( mEventData->mSelectionEnabled )
1850 ChangeState( EventData::SELECTING );
1852 mEventData->mLeftSelectionPosition = 0u;
1853 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1855 mEventData->mScrollAfterUpdatePosition = true;
1856 mEventData->mUpdateLeftSelectionPosition = true;
1857 mEventData->mUpdateRightSelectionPosition = true;
1858 mEventData->mUpdateHighlightBox = true;
1862 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1864 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1866 // Nothing to select if handles are in the same place.
1867 selectedText.clear();
1871 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1873 //Get start and end position of selection
1874 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1875 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1877 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1878 const Length numberOfCharacters = utf32Characters.Count();
1880 // Validate the start and end selection points
1881 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1883 //Get text as a UTF8 string
1884 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1886 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1888 // Keep a copy of the current input style.
1889 InputStyle currentInputStyle;
1890 currentInputStyle.Copy( mEventData->mInputStyle );
1892 // Set as input style the style of the first deleted character.
1893 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1895 // Compare if the input style has changed.
1896 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1898 if( hasInputStyleChanged )
1900 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1901 // Queue the input style changed signal.
1902 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1905 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1907 // Mark the paragraphs to be updated.
1908 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1910 mTextUpdateInfo.mCharacterIndex = 0;
1911 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1912 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1913 mTextUpdateInfo.mClearAll = true;
1917 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1918 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1921 // Delete text between handles
1922 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1923 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1924 utf32Characters.Erase( first, last );
1926 // Will show the cursor at the first character of the selection.
1927 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1931 // Will show the cursor at the last character of the selection.
1932 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1935 mEventData->mDecoratorUpdated = true;
1939 void Controller::Impl::ShowClipboard()
1943 mClipboard.ShowClipboard();
1947 void Controller::Impl::HideClipboard()
1949 if( mClipboard && mClipboardHideEnabled )
1951 mClipboard.HideClipboard();
1955 void Controller::Impl::SetClipboardHideEnable(bool enable)
1957 mClipboardHideEnabled = enable;
1960 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1962 //Send string to clipboard
1963 return ( mClipboard && mClipboard.SetItem( source ) );
1966 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1968 std::string selectedText;
1969 RetrieveSelection( selectedText, deleteAfterSending );
1970 CopyStringToClipboard( selectedText );
1971 ChangeState( EventData::EDITING );
1974 void Controller::Impl::RequestGetTextFromClipboard()
1978 mClipboard.RequestItem();
1982 void Controller::Impl::RepositionSelectionHandles()
1984 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1985 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1987 if( selectionStart == selectionEnd )
1989 // Nothing to select if handles are in the same place.
1990 // So, deactive Highlight box.
1991 mEventData->mDecorator->SetHighlightActive( false );
1995 mEventData->mDecorator->ClearHighlights();
1997 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1998 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1999 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2000 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2001 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2002 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2003 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2005 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2006 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2007 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2009 // Swap the indices if the start is greater than the end.
2010 const bool indicesSwapped = selectionStart > selectionEnd;
2012 // Tell the decorator to flip the selection handles if needed.
2013 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2015 if( indicesSwapped )
2017 std::swap( selectionStart, selectionEnd );
2020 // Get the indices to the first and last selected glyphs.
2021 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2022 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2023 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2024 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2026 // Get the lines where the glyphs are laid-out.
2027 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2029 LineIndex lineIndex = 0u;
2030 Length numberOfLines = 0u;
2031 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2032 1u + glyphEnd - glyphStart,
2035 const LineIndex firstLineIndex = lineIndex;
2037 // Create the structure to store some selection box info.
2038 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2039 selectionBoxLinesInfo.Resize( numberOfLines );
2041 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2042 selectionBoxInfo->minX = MAX_FLOAT;
2043 selectionBoxInfo->maxX = MIN_FLOAT;
2045 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2046 float minHighlightX = std::numeric_limits<float>::max();
2047 float maxHighlightX = std::numeric_limits<float>::min();
2049 Vector2 highLightPosition; // The highlight position in decorator's coords.
2051 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2053 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2054 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2057 // Transform to decorator's (control) coords.
2058 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2060 lineRun += firstLineIndex;
2062 // The line height is the addition of the line ascender and the line descender.
2063 // However, the line descender has a negative value, hence the subtraction.
2064 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2066 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2068 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2069 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2070 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2072 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2073 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2074 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2076 // The number of quads of the selection box.
2077 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2078 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2080 // Count the actual number of quads.
2081 unsigned int actualNumberOfQuads = 0u;
2084 // Traverse the glyphs.
2085 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2087 const GlyphInfo& glyph = *( glyphsBuffer + index );
2088 const Vector2& position = *( positionsBuffer + index );
2090 if( splitStartGlyph )
2092 // 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.
2094 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2095 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2096 // Get the direction of the character.
2097 CharacterDirection isCurrentRightToLeft = false;
2098 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2100 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2103 // The end point could be in the middle of the ligature.
2104 // Calculate the number of characters selected.
2105 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2107 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2108 quad.y = selectionBoxInfo->lineOffset;
2109 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2110 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2112 // Store the min and max 'x' for each line.
2113 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2114 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2116 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2117 ++actualNumberOfQuads;
2119 splitStartGlyph = false;
2123 if( splitEndGlyph && ( index == glyphEnd ) )
2125 // 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.
2127 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2128 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2129 // Get the direction of the character.
2130 CharacterDirection isCurrentRightToLeft = false;
2131 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2133 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2136 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2138 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2139 quad.y = selectionBoxInfo->lineOffset;
2140 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2141 quad.w = quad.y + selectionBoxInfo->lineHeight;
2143 // Store the min and max 'x' for each line.
2144 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2145 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2147 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2149 ++actualNumberOfQuads;
2151 splitEndGlyph = false;
2155 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2156 quad.y = selectionBoxInfo->lineOffset;
2157 quad.z = quad.x + glyph.advance;
2158 quad.w = quad.y + selectionBoxInfo->lineHeight;
2160 // Store the min and max 'x' for each line.
2161 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2162 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2164 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2166 ++actualNumberOfQuads;
2168 // Whether to retrieve the next line.
2169 if( index == lastGlyphOfLine )
2172 if( lineIndex < firstLineIndex + numberOfLines )
2174 // Retrieve the next line.
2177 // Get the last glyph of the new line.
2178 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2180 // Keep the offset and height of the current selection box.
2181 const float currentLineOffset = selectionBoxInfo->lineOffset;
2182 const float currentLineHeight = selectionBoxInfo->lineHeight;
2184 // Get the selection box info for the next line.
2187 selectionBoxInfo->minX = MAX_FLOAT;
2188 selectionBoxInfo->maxX = MIN_FLOAT;
2190 // Update the line's vertical offset.
2191 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2193 // The line height is the addition of the line ascender and the line descender.
2194 // However, the line descender has a negative value, hence the subtraction.
2195 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2200 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2201 // The final width is calculated after 'boxifying' the selection.
2202 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2203 endIt = selectionBoxLinesInfo.End();
2207 const SelectionBoxInfo& info = *it;
2209 // Update the size of the highlighted text.
2210 highLightSize.height += info.lineHeight;
2211 minHighlightX = std::min( minHighlightX, info.minX );
2212 maxHighlightX = std::max( maxHighlightX, info.maxX );
2215 // Add extra geometry to 'boxify' the selection.
2217 if( 1u < numberOfLines )
2219 // Boxify the first line.
2220 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2221 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2223 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2224 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2229 quad.y = firstSelectionBoxLineInfo.lineOffset;
2230 quad.z = firstSelectionBoxLineInfo.minX;
2231 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2233 // Boxify at the beginning of the line.
2234 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2236 ++actualNumberOfQuads;
2238 // Update the size of the highlighted text.
2239 minHighlightX = 0.f;
2244 quad.x = firstSelectionBoxLineInfo.maxX;
2245 quad.y = firstSelectionBoxLineInfo.lineOffset;
2246 quad.z = mModel->mVisualModel->mControlSize.width;
2247 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2249 // Boxify at the end of the line.
2250 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2252 ++actualNumberOfQuads;
2254 // Update the size of the highlighted text.
2255 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2258 // Boxify the central lines.
2259 if( 2u < numberOfLines )
2261 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2262 endIt = selectionBoxLinesInfo.End() - 1u;
2266 const SelectionBoxInfo& info = *it;
2269 quad.y = info.lineOffset;
2271 quad.w = info.lineOffset + info.lineHeight;
2273 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2275 ++actualNumberOfQuads;
2278 quad.y = info.lineOffset;
2279 quad.z = mModel->mVisualModel->mControlSize.width;
2280 quad.w = info.lineOffset + info.lineHeight;
2282 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2284 ++actualNumberOfQuads;
2287 // Update the size of the highlighted text.
2288 minHighlightX = 0.f;
2289 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2292 // Boxify the last line.
2293 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2294 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2296 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2297 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2302 quad.y = lastSelectionBoxLineInfo.lineOffset;
2303 quad.z = lastSelectionBoxLineInfo.minX;
2304 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2306 // Boxify at the beginning of the line.
2307 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2309 ++actualNumberOfQuads;
2311 // Update the size of the highlighted text.
2312 minHighlightX = 0.f;
2317 quad.x = lastSelectionBoxLineInfo.maxX;
2318 quad.y = lastSelectionBoxLineInfo.lineOffset;
2319 quad.z = mModel->mVisualModel->mControlSize.width;
2320 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2322 // Boxify at the end of the line.
2323 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2325 ++actualNumberOfQuads;
2327 // Update the size of the highlighted text.
2328 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2332 // Set the actual number of quads.
2333 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2335 // Sets the highlight's size and position. In decorator's coords.
2336 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2337 highLightSize.width = maxHighlightX - minHighlightX;
2339 highLightPosition.x = minHighlightX;
2340 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2341 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2343 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2345 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2347 CursorInfo primaryCursorInfo;
2348 GetCursorPosition( mEventData->mLeftSelectionPosition,
2349 primaryCursorInfo );
2351 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2353 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2355 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2356 primaryCursorInfo.lineHeight );
2358 CursorInfo secondaryCursorInfo;
2359 GetCursorPosition( mEventData->mRightSelectionPosition,
2360 secondaryCursorInfo );
2362 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2364 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2365 secondaryPosition.x,
2366 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2367 secondaryCursorInfo.lineHeight );
2370 // Set the flag to update the decorator.
2371 mEventData->mDecoratorUpdated = true;
2374 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2376 if( NULL == mEventData )
2378 // Nothing to do if there is no text input.
2382 if( IsShowingPlaceholderText() )
2384 // Nothing to do if there is the place-holder text.
2388 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2389 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2390 if( ( 0 == numberOfGlyphs ) ||
2391 ( 0 == numberOfLines ) )
2393 // Nothing to do if there is no text.
2397 // Find which word was selected
2398 CharacterIndex selectionStart( 0 );
2399 CharacterIndex selectionEnd( 0 );
2400 CharacterIndex noTextHitIndex( 0 );
2401 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2402 mModel->mLogicalModel,
2409 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2411 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2413 ChangeState( EventData::SELECTING );
2415 mEventData->mLeftSelectionPosition = selectionStart;
2416 mEventData->mRightSelectionPosition = selectionEnd;
2418 mEventData->mUpdateLeftSelectionPosition = true;
2419 mEventData->mUpdateRightSelectionPosition = true;
2420 mEventData->mUpdateHighlightBox = true;
2422 // It may happen an InputMethodContext commit event arrives before the selection event
2423 // if the InputMethodContext is in pre-edit state. The commit event will set the
2424 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2425 // to false, the highlight box won't be updated.
2426 mEventData->mUpdateCursorPosition = false;
2428 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2430 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2431 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2433 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2435 // Nothing to select. i.e. a white space, out of bounds
2436 ChangeState( EventData::EDITING_WITH_POPUP );
2438 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2440 mEventData->mUpdateCursorPosition = true;
2441 mEventData->mUpdateGrabHandlePosition = true;
2442 mEventData->mScrollAfterUpdatePosition = true;
2443 mEventData->mUpdateInputStyle = true;
2445 else if( Controller::NoTextTap::NO_ACTION == action )
2447 // Nothing to select. i.e. a white space, out of bounds
2448 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2450 mEventData->mUpdateCursorPosition = true;
2451 mEventData->mUpdateGrabHandlePosition = true;
2452 mEventData->mScrollAfterUpdatePosition = true;
2453 mEventData->mUpdateInputStyle = true;
2457 void Controller::Impl::SetPopupButtons()
2460 * Sets the Popup buttons to be shown depending on State.
2462 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2464 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2467 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2469 if( EventData::SELECTING == mEventData->mState )
2471 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2473 if( !IsClipboardEmpty() )
2475 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2476 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2479 if( !mEventData->mAllTextSelected )
2481 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2484 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2486 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2488 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2491 if( !IsClipboardEmpty() )
2493 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2494 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2497 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2499 if ( !IsClipboardEmpty() )
2501 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2502 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2506 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2509 void Controller::Impl::ChangeState( EventData::State newState )
2511 if( NULL == mEventData )
2513 // Nothing to do if there is no text input.
2517 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2519 if( mEventData->mState != newState )
2521 mEventData->mPreviousState = mEventData->mState;
2522 mEventData->mState = newState;
2524 switch( mEventData->mState )
2526 case EventData::INACTIVE:
2528 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2529 mEventData->mDecorator->StopCursorBlink();
2530 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2531 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2532 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2533 mEventData->mDecorator->SetHighlightActive( false );
2534 mEventData->mDecorator->SetPopupActive( false );
2535 mEventData->mDecoratorUpdated = true;
2538 case EventData::INTERRUPTED:
2540 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2541 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2542 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2543 mEventData->mDecorator->SetHighlightActive( false );
2544 mEventData->mDecorator->SetPopupActive( false );
2545 mEventData->mDecoratorUpdated = true;
2548 case EventData::SELECTING:
2550 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2551 mEventData->mDecorator->StopCursorBlink();
2552 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2553 if ( mEventData->mGrabHandleEnabled )
2555 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2556 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2558 mEventData->mDecorator->SetHighlightActive( true );
2559 if( mEventData->mGrabHandlePopupEnabled )
2562 mEventData->mDecorator->SetPopupActive( true );
2564 mEventData->mDecoratorUpdated = true;
2567 case EventData::EDITING:
2569 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2570 if( mEventData->mCursorBlinkEnabled )
2572 mEventData->mDecorator->StartCursorBlink();
2574 // Grab handle is not shown until a tap is received whilst EDITING
2575 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2576 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2577 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2578 mEventData->mDecorator->SetHighlightActive( false );
2579 if( mEventData->mGrabHandlePopupEnabled )
2581 mEventData->mDecorator->SetPopupActive( false );
2583 mEventData->mDecoratorUpdated = true;
2586 case EventData::EDITING_WITH_POPUP:
2588 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2590 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2591 if( mEventData->mCursorBlinkEnabled )
2593 mEventData->mDecorator->StartCursorBlink();
2595 if( mEventData->mSelectionEnabled )
2597 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2598 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2599 mEventData->mDecorator->SetHighlightActive( false );
2601 else if ( mEventData->mGrabHandleEnabled )
2603 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2605 if( mEventData->mGrabHandlePopupEnabled )
2608 mEventData->mDecorator->SetPopupActive( true );
2610 mEventData->mDecoratorUpdated = true;
2613 case EventData::EDITING_WITH_GRAB_HANDLE:
2615 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2617 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2618 if( mEventData->mCursorBlinkEnabled )
2620 mEventData->mDecorator->StartCursorBlink();
2622 // Grab handle is not shown until a tap is received whilst EDITING
2623 if ( mEventData->mGrabHandleEnabled )
2625 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2627 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2628 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2629 mEventData->mDecorator->SetHighlightActive( false );
2630 if( mEventData->mGrabHandlePopupEnabled )
2632 mEventData->mDecorator->SetPopupActive( false );
2634 mEventData->mDecoratorUpdated = true;
2637 case EventData::SELECTION_HANDLE_PANNING:
2639 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2640 mEventData->mDecorator->StopCursorBlink();
2641 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2642 if ( mEventData->mGrabHandleEnabled )
2644 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2645 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2647 mEventData->mDecorator->SetHighlightActive( true );
2648 if( mEventData->mGrabHandlePopupEnabled )
2650 mEventData->mDecorator->SetPopupActive( false );
2652 mEventData->mDecoratorUpdated = true;
2655 case EventData::GRAB_HANDLE_PANNING:
2657 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2659 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2660 if( mEventData->mCursorBlinkEnabled )
2662 mEventData->mDecorator->StartCursorBlink();
2664 if ( mEventData->mGrabHandleEnabled )
2666 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2668 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2669 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2670 mEventData->mDecorator->SetHighlightActive( false );
2671 if( mEventData->mGrabHandlePopupEnabled )
2673 mEventData->mDecorator->SetPopupActive( false );
2675 mEventData->mDecoratorUpdated = true;
2678 case EventData::EDITING_WITH_PASTE_POPUP:
2680 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2682 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2683 if( mEventData->mCursorBlinkEnabled )
2685 mEventData->mDecorator->StartCursorBlink();
2688 if ( mEventData->mGrabHandleEnabled )
2690 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2692 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2693 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2694 mEventData->mDecorator->SetHighlightActive( false );
2696 if( mEventData->mGrabHandlePopupEnabled )
2699 mEventData->mDecorator->SetPopupActive( true );
2701 mEventData->mDecoratorUpdated = true;
2704 case EventData::TEXT_PANNING:
2706 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2707 mEventData->mDecorator->StopCursorBlink();
2708 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2709 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2710 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2712 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2713 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2714 mEventData->mDecorator->SetHighlightActive( true );
2717 if( mEventData->mGrabHandlePopupEnabled )
2719 mEventData->mDecorator->SetPopupActive( false );
2722 mEventData->mDecoratorUpdated = true;
2729 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2730 CursorInfo& cursorInfo )
2732 if( !IsShowingRealText() )
2734 // Do not want to use the place-holder text to set the cursor position.
2736 // Use the line's height of the font's family set to set the cursor's size.
2737 // If there is no font's family set, use the default font.
2738 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2740 cursorInfo.lineOffset = 0.f;
2741 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2742 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2745 if( mModel->mMatchSystemLanguageDirection )
2747 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2750 switch( mModel->mHorizontalAlignment )
2752 case Text::HorizontalAlignment::BEGIN :
2756 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2760 cursorInfo.primaryPosition.x = 0.f;
2764 case Text::HorizontalAlignment::CENTER:
2766 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2769 case Text::HorizontalAlignment::END:
2773 cursorInfo.primaryPosition.x = 0.f;
2777 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2783 // Nothing else to do.
2787 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2788 GetCursorPositionParameters parameters;
2789 parameters.visualModel = mModel->mVisualModel;
2790 parameters.logicalModel = mModel->mLogicalModel;
2791 parameters.metrics = mMetrics;
2792 parameters.logical = logical;
2793 parameters.isMultiline = isMultiLine;
2795 Text::GetCursorPosition( parameters,
2798 // Adds Outline offset.
2799 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2800 cursorInfo.primaryPosition.x += outlineWidth;
2801 cursorInfo.primaryPosition.y += outlineWidth;
2802 cursorInfo.secondaryPosition.x += outlineWidth;
2803 cursorInfo.secondaryPosition.y += outlineWidth;
2807 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2809 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2810 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2812 if( 0.f > cursorInfo.primaryPosition.x )
2814 cursorInfo.primaryPosition.x = 0.f;
2817 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2818 if( cursorInfo.primaryPosition.x > edgeWidth )
2820 cursorInfo.primaryPosition.x = edgeWidth;
2825 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2827 if( NULL == mEventData )
2829 // Nothing to do if there is no text input.
2833 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2835 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2836 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2838 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2839 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2841 if( numberOfCharacters > 1u )
2843 const Script script = mModel->mLogicalModel->GetScript( index );
2844 if( HasLigatureMustBreak( script ) )
2846 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2847 numberOfCharacters = 1u;
2852 while( 0u == numberOfCharacters )
2855 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2859 if( index < mEventData->mPrimaryCursorPosition )
2861 cursorIndex -= numberOfCharacters;
2865 cursorIndex += numberOfCharacters;
2868 // Will update the cursor hook position.
2869 mEventData->mUpdateCursorHookPosition = true;
2874 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2876 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2877 if( NULL == mEventData )
2879 // Nothing to do if there is no text input.
2880 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2884 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2886 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2888 // Sets the cursor position.
2889 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2892 cursorInfo.primaryCursorHeight,
2893 cursorInfo.lineHeight );
2894 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2896 if( mEventData->mUpdateGrabHandlePosition )
2898 // Sets the grab handle position.
2899 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2901 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2902 cursorInfo.lineHeight );
2905 if( cursorInfo.isSecondaryCursor )
2907 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2908 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2909 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2910 cursorInfo.secondaryCursorHeight,
2911 cursorInfo.lineHeight );
2912 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2915 // Set which cursors are active according the state.
2916 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2918 if( cursorInfo.isSecondaryCursor )
2920 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2924 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2929 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2932 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2935 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2936 const CursorInfo& cursorInfo )
2938 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2939 ( RIGHT_SELECTION_HANDLE != handleType ) )
2944 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2946 // Sets the handle's position.
2947 mEventData->mDecorator->SetPosition( handleType,
2949 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2950 cursorInfo.lineHeight );
2952 // If selection handle at start of the text and other at end of the text then all text is selected.
2953 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2954 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2955 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2958 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2960 // Clamp between -space & -alignment offset.
2962 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2964 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2965 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2966 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2968 mEventData->mDecoratorUpdated = true;
2972 mModel->mScrollPosition.x = 0.f;
2976 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2978 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2980 // Nothing to do if the text is single line.
2984 // Clamp between -space & 0.
2985 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2987 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2988 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2989 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2991 mEventData->mDecoratorUpdated = true;
2995 mModel->mScrollPosition.y = 0.f;
2999 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3001 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3003 // position is in actor's coords.
3004 const float positionEndX = position.x + cursorWidth;
3005 const float positionEndY = position.y + lineHeight;
3007 // Transform the position to decorator coords.
3008 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3009 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3011 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3012 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3014 if( decoratorPositionBeginX < 0.f )
3016 mModel->mScrollPosition.x = -position.x;
3018 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3020 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3023 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3025 if( decoratorPositionBeginY < 0.f )
3027 mModel->mScrollPosition.y = -position.y;
3029 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3031 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3036 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3038 // Get the current cursor position in decorator coords.
3039 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3041 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3045 // Calculate the offset to match the cursor position before the character was deleted.
3046 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3048 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3049 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3051 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3052 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3056 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3057 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3059 // Makes the new cursor position visible if needed.
3060 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3063 void Controller::Impl::RequestRelayout()
3065 if( NULL != mControlInterface )
3067 mControlInterface->RequestTextRelayout();
3073 } // namespace Toolkit