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;
936 if( mTextFitEnabled )
938 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
942 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
946 // Validates the fonts. If there is a character with no assigned font it sets a default one.
947 // After this call, fonts are validated.
948 multilanguageSupport.ValidateFonts( utf32Characters,
951 defaultFontDescription,
954 requestedNumberOfCharacters,
960 Vector<Character> mirroredUtf32Characters;
961 bool textMirrored = false;
962 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
963 if( NO_OPERATION != ( BIDI_INFO & operations ) )
965 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
966 bidirectionalInfo.Reserve( numberOfParagraphs );
968 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
969 SetBidirectionalInfo( utf32Characters,
973 requestedNumberOfCharacters,
975 mModel->mMatchSystemLanguageDirection,
978 if( 0u != bidirectionalInfo.Count() )
980 // Only set the character directions if there is right to left characters.
981 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
982 GetCharactersDirection( bidirectionalInfo,
985 requestedNumberOfCharacters,
988 // This paragraph has right to left text. Some characters may need to be mirrored.
989 // TODO: consider if the mirrored string can be stored as well.
991 textMirrored = GetMirroredText( utf32Characters,
995 requestedNumberOfCharacters,
996 mirroredUtf32Characters );
1000 // There is no right to left characters. Clear the directions vector.
1001 mModel->mLogicalModel->mCharacterDirections.Clear();
1006 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1007 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1008 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1009 Vector<GlyphIndex> newParagraphGlyphs;
1010 newParagraphGlyphs.Reserve( numberOfParagraphs );
1012 const Length currentNumberOfGlyphs = glyphs.Count();
1013 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1015 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1017 ShapeText( textToShape,
1022 mTextUpdateInfo.mStartGlyphIndex,
1023 requestedNumberOfCharacters,
1025 glyphsToCharactersMap,
1027 newParagraphGlyphs );
1029 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1030 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1031 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1035 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1037 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1039 GlyphInfo* glyphsBuffer = glyphs.Begin();
1040 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1042 // Update the width and advance of all new paragraph characters.
1043 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1045 const GlyphIndex index = *it;
1046 GlyphInfo& glyph = *( glyphsBuffer + index );
1048 glyph.xBearing = 0.f;
1050 glyph.advance = 0.f;
1055 if( NO_OPERATION != ( COLOR & operations ) )
1057 // Set the color runs in glyphs.
1058 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1059 mModel->mVisualModel->mCharactersToGlyph,
1060 mModel->mVisualModel->mGlyphsPerCharacter,
1062 mTextUpdateInfo.mStartGlyphIndex,
1063 requestedNumberOfCharacters,
1064 mModel->mVisualModel->mColors,
1065 mModel->mVisualModel->mColorIndices );
1070 if( ( NULL != mEventData ) &&
1071 mEventData->mPreEditFlag &&
1072 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1074 // Add the underline for the pre-edit text.
1075 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1076 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1078 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1079 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1080 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1081 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1083 GlyphRun underlineRun;
1084 underlineRun.glyphIndex = glyphStart;
1085 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1087 // TODO: At the moment the underline runs are only for pre-edit.
1088 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1091 // The estimated number of lines. Used to avoid reallocations when layouting.
1092 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1094 // Set the previous number of characters for the next time the text is updated.
1095 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1100 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1102 // Sets the default text's color.
1103 inputStyle.textColor = mTextColor;
1104 inputStyle.isDefaultColor = true;
1106 inputStyle.familyName.clear();
1107 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1108 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1109 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1110 inputStyle.size = 0.f;
1112 inputStyle.lineSpacing = 0.f;
1114 inputStyle.underlineProperties.clear();
1115 inputStyle.shadowProperties.clear();
1116 inputStyle.embossProperties.clear();
1117 inputStyle.outlineProperties.clear();
1119 inputStyle.isFamilyDefined = false;
1120 inputStyle.isWeightDefined = false;
1121 inputStyle.isWidthDefined = false;
1122 inputStyle.isSlantDefined = false;
1123 inputStyle.isSizeDefined = false;
1125 inputStyle.isLineSpacingDefined = false;
1127 inputStyle.isUnderlineDefined = false;
1128 inputStyle.isShadowDefined = false;
1129 inputStyle.isEmbossDefined = false;
1130 inputStyle.isOutlineDefined = false;
1132 // Sets the default font's family name, weight, width, slant and size.
1135 if( mFontDefaults->familyDefined )
1137 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1138 inputStyle.isFamilyDefined = true;
1141 if( mFontDefaults->weightDefined )
1143 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1144 inputStyle.isWeightDefined = true;
1147 if( mFontDefaults->widthDefined )
1149 inputStyle.width = mFontDefaults->mFontDescription.width;
1150 inputStyle.isWidthDefined = true;
1153 if( mFontDefaults->slantDefined )
1155 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1156 inputStyle.isSlantDefined = true;
1159 if( mFontDefaults->sizeDefined )
1161 inputStyle.size = mFontDefaults->mDefaultPointSize;
1162 inputStyle.isSizeDefined = true;
1167 float Controller::Impl::GetDefaultFontLineHeight()
1169 FontId defaultFontId = 0u;
1170 if( NULL == mFontDefaults )
1172 TextAbstraction::FontDescription fontDescription;
1173 defaultFontId = mFontClient.GetFontId( fontDescription );
1177 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1180 Text::FontMetrics fontMetrics;
1181 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1183 return( fontMetrics.ascender - fontMetrics.descender );
1186 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1188 if( NULL == mEventData || !IsShowingRealText() )
1190 // Nothing to do if there is no text input.
1194 int keyCode = event.p1.mInt;
1195 bool isShiftModifier = event.p2.mBool;
1197 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1199 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1201 if( mEventData->mPrimaryCursorPosition > 0u )
1203 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1205 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1209 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1213 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1215 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1217 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1219 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1223 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1227 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1229 // Ignore Shift-Up for text selection for now.
1231 // Get first the line index of the current cursor position index.
1232 CharacterIndex characterIndex = 0u;
1234 if( mEventData->mPrimaryCursorPosition > 0u )
1236 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1239 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1240 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1242 // Retrieve the cursor position info.
1243 CursorInfo cursorInfo;
1244 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1247 // Get the line above.
1248 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1250 // Get the next hit 'y' point.
1251 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1253 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1254 bool matchedCharacter = false;
1255 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1256 mModel->mLogicalModel,
1258 mEventData->mCursorHookPositionX,
1260 CharacterHitTest::TAP,
1263 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1265 // Ignore Shift-Down for text selection for now.
1267 // Get first the line index of the current cursor position index.
1268 CharacterIndex characterIndex = 0u;
1270 if( mEventData->mPrimaryCursorPosition > 0u )
1272 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1275 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1277 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1279 // Retrieve the cursor position info.
1280 CursorInfo cursorInfo;
1281 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1284 // Get the line below.
1285 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1287 // Get the next hit 'y' point.
1288 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1290 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1291 bool matchedCharacter = false;
1292 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1293 mModel->mLogicalModel,
1295 mEventData->mCursorHookPositionX,
1297 CharacterHitTest::TAP,
1302 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1304 // Update selection position after moving the cursor
1305 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1306 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1309 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1311 // Handle text selection
1312 bool selecting = false;
1314 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1316 // Shift-Left/Right to select the text
1317 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1318 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1320 mEventData->mRightSelectionPosition += cursorPositionDelta;
1324 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1326 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1332 // Notify the cursor position to the InputMethodContext.
1333 if( mEventData->mInputMethodContext )
1335 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1336 mEventData->mInputMethodContext.NotifyCursorPosition();
1339 ChangeState( EventData::SELECTING );
1341 mEventData->mUpdateLeftSelectionPosition = true;
1342 mEventData->mUpdateRightSelectionPosition = true;
1343 mEventData->mUpdateGrabHandlePosition = true;
1344 mEventData->mUpdateHighlightBox = true;
1346 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1347 if( mEventData->mGrabHandlePopupEnabled )
1349 mEventData->mDecorator->SetPopupActive( false );
1355 // Handle normal cursor move
1356 ChangeState( EventData::EDITING );
1357 mEventData->mUpdateCursorPosition = true;
1360 mEventData->mUpdateInputStyle = true;
1361 mEventData->mScrollAfterUpdatePosition = true;
1364 void Controller::Impl::OnTapEvent( const Event& event )
1366 if( NULL != mEventData )
1368 const unsigned int tapCount = event.p1.mUint;
1370 if( 1u == tapCount )
1372 if( IsShowingRealText() )
1374 // Convert from control's coords to text's coords.
1375 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1376 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1378 // Keep the tap 'x' position. Used to move the cursor.
1379 mEventData->mCursorHookPositionX = xPosition;
1381 // Whether to touch point hits on a glyph.
1382 bool matchedCharacter = false;
1383 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1384 mModel->mLogicalModel,
1388 CharacterHitTest::TAP,
1391 // When the cursor position is changing, delay cursor blinking
1392 mEventData->mDecorator->DelayCursorBlink();
1396 mEventData->mPrimaryCursorPosition = 0u;
1399 // Update selection position after tapping
1400 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1401 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1403 mEventData->mUpdateCursorPosition = true;
1404 mEventData->mUpdateGrabHandlePosition = true;
1405 mEventData->mScrollAfterUpdatePosition = true;
1406 mEventData->mUpdateInputStyle = true;
1408 // Notify the cursor position to the InputMethodContext.
1409 if( mEventData->mInputMethodContext )
1411 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1412 mEventData->mInputMethodContext.NotifyCursorPosition();
1415 else if( 2u == tapCount )
1417 if( mEventData->mSelectionEnabled )
1419 // Convert from control's coords to text's coords.
1420 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1421 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1423 // Calculates the logical position from the x,y coords.
1424 RepositionSelectionHandles( xPosition,
1426 mEventData->mDoubleTapAction );
1432 void Controller::Impl::OnPanEvent( const Event& event )
1434 if( NULL == mEventData )
1436 // Nothing to do if there is no text input.
1440 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1441 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1443 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1445 // Nothing to do if scrolling is not enabled.
1449 const int state = event.p1.mInt;
1453 case Gesture::Started:
1455 // Will remove the cursor, handles or text's popup, ...
1456 ChangeState( EventData::TEXT_PANNING );
1459 case Gesture::Continuing:
1461 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1462 const Vector2 currentScroll = mModel->mScrollPosition;
1464 if( isHorizontalScrollEnabled )
1466 const float displacementX = event.p2.mFloat;
1467 mModel->mScrollPosition.x += displacementX;
1469 ClampHorizontalScroll( layoutSize );
1472 if( isVerticalScrollEnabled )
1474 const float displacementY = event.p3.mFloat;
1475 mModel->mScrollPosition.y += displacementY;
1477 ClampVerticalScroll( layoutSize );
1480 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1483 case Gesture::Finished:
1484 case Gesture::Cancelled: // FALLTHROUGH
1486 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1487 ChangeState( mEventData->mPreviousState );
1495 void Controller::Impl::OnLongPressEvent( const Event& event )
1497 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1499 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1501 ChangeState( EventData::EDITING_WITH_POPUP );
1502 mEventData->mDecoratorUpdated = true;
1503 mEventData->mUpdateInputStyle = true;
1507 if( mEventData->mSelectionEnabled )
1509 // Convert from control's coords to text's coords.
1510 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1511 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1513 // Calculates the logical position from the x,y coords.
1514 RepositionSelectionHandles( xPosition,
1516 mEventData->mLongPressAction );
1521 void Controller::Impl::OnHandleEvent( const Event& event )
1523 if( NULL == mEventData )
1525 // Nothing to do if there is no text input.
1529 const unsigned int state = event.p1.mUint;
1530 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1531 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1533 if( HANDLE_PRESSED == state )
1535 // Convert from decorator's coords to text's coords.
1536 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1537 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1539 // Need to calculate the handle's new position.
1540 bool matchedCharacter = false;
1541 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1542 mModel->mLogicalModel,
1546 CharacterHitTest::SCROLL,
1549 if( Event::GRAB_HANDLE_EVENT == event.type )
1551 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1553 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1555 // Updates the cursor position if the handle's new position is different than the current one.
1556 mEventData->mUpdateCursorPosition = true;
1557 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1558 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1559 mEventData->mPrimaryCursorPosition = handleNewPosition;
1562 // 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.
1563 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1565 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1567 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1569 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1570 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1572 // Updates the highlight box if the handle's new position is different than the current one.
1573 mEventData->mUpdateHighlightBox = true;
1574 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1575 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1576 mEventData->mLeftSelectionPosition = handleNewPosition;
1579 // 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.
1580 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1582 // Will define the order to scroll the text to match the handle position.
1583 mEventData->mIsLeftHandleSelected = true;
1584 mEventData->mIsRightHandleSelected = false;
1586 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1588 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1590 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1591 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1593 // Updates the highlight box if the handle's new position is different than the current one.
1594 mEventData->mUpdateHighlightBox = true;
1595 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1596 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1597 mEventData->mRightSelectionPosition = handleNewPosition;
1600 // 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.
1601 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1603 // Will define the order to scroll the text to match the handle position.
1604 mEventData->mIsLeftHandleSelected = false;
1605 mEventData->mIsRightHandleSelected = true;
1607 } // end ( HANDLE_PRESSED == state )
1608 else if( ( HANDLE_RELEASED == state ) ||
1609 handleStopScrolling )
1611 CharacterIndex handlePosition = 0u;
1612 if( handleStopScrolling || isSmoothHandlePanEnabled )
1614 // Convert from decorator's coords to text's coords.
1615 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1616 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1618 bool matchedCharacter = false;
1619 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1620 mModel->mLogicalModel,
1624 CharacterHitTest::SCROLL,
1628 if( Event::GRAB_HANDLE_EVENT == event.type )
1630 mEventData->mUpdateCursorPosition = true;
1631 mEventData->mUpdateGrabHandlePosition = true;
1632 mEventData->mUpdateInputStyle = true;
1634 if( !IsClipboardEmpty() )
1636 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1639 if( handleStopScrolling || isSmoothHandlePanEnabled )
1641 mEventData->mScrollAfterUpdatePosition = true;
1642 mEventData->mPrimaryCursorPosition = handlePosition;
1645 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1647 ChangeState( EventData::SELECTING );
1649 mEventData->mUpdateHighlightBox = true;
1650 mEventData->mUpdateLeftSelectionPosition = true;
1651 mEventData->mUpdateRightSelectionPosition = true;
1653 if( handleStopScrolling || isSmoothHandlePanEnabled )
1655 mEventData->mScrollAfterUpdatePosition = true;
1657 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1658 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1660 mEventData->mLeftSelectionPosition = handlePosition;
1664 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1666 ChangeState( EventData::SELECTING );
1668 mEventData->mUpdateHighlightBox = true;
1669 mEventData->mUpdateRightSelectionPosition = true;
1670 mEventData->mUpdateLeftSelectionPosition = true;
1672 if( handleStopScrolling || isSmoothHandlePanEnabled )
1674 mEventData->mScrollAfterUpdatePosition = true;
1675 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1676 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1678 mEventData->mRightSelectionPosition = handlePosition;
1683 mEventData->mDecoratorUpdated = true;
1684 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1685 else if( HANDLE_SCROLLING == state )
1687 const float xSpeed = event.p2.mFloat;
1688 const float ySpeed = event.p3.mFloat;
1689 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1690 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1692 mModel->mScrollPosition.x += xSpeed;
1693 mModel->mScrollPosition.y += ySpeed;
1695 ClampHorizontalScroll( layoutSize );
1696 ClampVerticalScroll( layoutSize );
1698 bool endOfScroll = false;
1699 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1701 // Notify the decorator there is no more text to scroll.
1702 // The decorator won't send more scroll events.
1703 mEventData->mDecorator->NotifyEndOfScroll();
1704 // Still need to set the position of the handle.
1708 // Set the position of the handle.
1709 const bool scrollRightDirection = xSpeed > 0.f;
1710 const bool scrollBottomDirection = ySpeed > 0.f;
1711 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1712 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1714 if( Event::GRAB_HANDLE_EVENT == event.type )
1716 ChangeState( EventData::GRAB_HANDLE_PANNING );
1718 // Get the grab handle position in decorator coords.
1719 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1721 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1723 // Position the grag handle close to either the left or right edge.
1724 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1727 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1729 position.x = mEventData->mCursorHookPositionX;
1731 // Position the grag handle close to either the top or bottom edge.
1732 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1735 // Get the new handle position.
1736 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1737 bool matchedCharacter = false;
1738 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1739 mModel->mLogicalModel,
1741 position.x - mModel->mScrollPosition.x,
1742 position.y - mModel->mScrollPosition.y,
1743 CharacterHitTest::SCROLL,
1746 if( mEventData->mPrimaryCursorPosition != handlePosition )
1748 mEventData->mUpdateCursorPosition = true;
1749 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1750 mEventData->mScrollAfterUpdatePosition = true;
1751 mEventData->mPrimaryCursorPosition = handlePosition;
1753 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1755 // Updates the decorator if the soft handle panning is enabled.
1756 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1758 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1760 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1762 // Get the selection handle position in decorator coords.
1763 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1765 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1767 // Position the selection handle close to either the left or right edge.
1768 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1771 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1773 position.x = mEventData->mCursorHookPositionX;
1775 // Position the grag handle close to either the top or bottom edge.
1776 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1779 // Get the new handle position.
1780 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1781 bool matchedCharacter = false;
1782 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1783 mModel->mLogicalModel,
1785 position.x - mModel->mScrollPosition.x,
1786 position.y - mModel->mScrollPosition.y,
1787 CharacterHitTest::SCROLL,
1790 if( leftSelectionHandleEvent )
1792 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1794 if( differentHandles || endOfScroll )
1796 mEventData->mUpdateHighlightBox = true;
1797 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1798 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1799 mEventData->mLeftSelectionPosition = handlePosition;
1804 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1805 if( differentHandles || endOfScroll )
1807 mEventData->mUpdateHighlightBox = true;
1808 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1809 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1810 mEventData->mRightSelectionPosition = handlePosition;
1814 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1816 RepositionSelectionHandles();
1818 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1821 mEventData->mDecoratorUpdated = true;
1822 } // end ( HANDLE_SCROLLING == state )
1825 void Controller::Impl::OnSelectEvent( const Event& event )
1827 if( NULL == mEventData )
1829 // Nothing to do if there is no text.
1833 if( mEventData->mSelectionEnabled )
1835 // Convert from control's coords to text's coords.
1836 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1837 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1839 // Calculates the logical position from the x,y coords.
1840 RepositionSelectionHandles( xPosition,
1842 Controller::NoTextTap::HIGHLIGHT );
1846 void Controller::Impl::OnSelectAllEvent()
1848 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1850 if( NULL == mEventData )
1852 // Nothing to do if there is no text.
1856 if( mEventData->mSelectionEnabled )
1858 // Calculates the logical position from the start.
1859 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
1860 0.f - mModel->mScrollPosition.y,
1861 Controller::NoTextTap::HIGHLIGHT );
1863 mEventData->mLeftSelectionPosition = 0u;
1864 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1868 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1870 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1872 // Nothing to select if handles are in the same place.
1873 selectedText.clear();
1877 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1879 //Get start and end position of selection
1880 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1881 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1883 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1884 const Length numberOfCharacters = utf32Characters.Count();
1886 // Validate the start and end selection points
1887 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1889 //Get text as a UTF8 string
1890 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1892 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1894 // Keep a copy of the current input style.
1895 InputStyle currentInputStyle;
1896 currentInputStyle.Copy( mEventData->mInputStyle );
1898 // Set as input style the style of the first deleted character.
1899 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1901 // Compare if the input style has changed.
1902 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1904 if( hasInputStyleChanged )
1906 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1907 // Queue the input style changed signal.
1908 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1911 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1913 // Mark the paragraphs to be updated.
1914 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1916 mTextUpdateInfo.mCharacterIndex = 0;
1917 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1918 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1919 mTextUpdateInfo.mClearAll = true;
1923 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1924 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1927 // Delete text between handles
1928 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1929 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1930 utf32Characters.Erase( first, last );
1932 // Will show the cursor at the first character of the selection.
1933 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1937 // Will show the cursor at the last character of the selection.
1938 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1941 mEventData->mDecoratorUpdated = true;
1945 void Controller::Impl::ShowClipboard()
1949 mClipboard.ShowClipboard();
1953 void Controller::Impl::HideClipboard()
1955 if( mClipboard && mClipboardHideEnabled )
1957 mClipboard.HideClipboard();
1961 void Controller::Impl::SetClipboardHideEnable(bool enable)
1963 mClipboardHideEnabled = enable;
1966 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1968 //Send string to clipboard
1969 return ( mClipboard && mClipboard.SetItem( source ) );
1972 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1974 std::string selectedText;
1975 RetrieveSelection( selectedText, deleteAfterSending );
1976 CopyStringToClipboard( selectedText );
1977 ChangeState( EventData::EDITING );
1980 void Controller::Impl::RequestGetTextFromClipboard()
1984 mClipboard.RequestItem();
1988 void Controller::Impl::RepositionSelectionHandles()
1990 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1991 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1993 if( selectionStart == selectionEnd )
1995 // Nothing to select if handles are in the same place.
1996 // So, deactive Highlight box.
1997 mEventData->mDecorator->SetHighlightActive( false );
2001 mEventData->mDecorator->ClearHighlights();
2003 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2004 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2005 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2006 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2007 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2008 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2009 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2011 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2012 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2013 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2015 // Swap the indices if the start is greater than the end.
2016 const bool indicesSwapped = selectionStart > selectionEnd;
2018 // Tell the decorator to flip the selection handles if needed.
2019 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2021 if( indicesSwapped )
2023 std::swap( selectionStart, selectionEnd );
2026 // Get the indices to the first and last selected glyphs.
2027 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2028 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2029 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2030 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2032 // Get the lines where the glyphs are laid-out.
2033 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2035 LineIndex lineIndex = 0u;
2036 Length numberOfLines = 0u;
2037 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2038 1u + glyphEnd - glyphStart,
2041 const LineIndex firstLineIndex = lineIndex;
2043 // Create the structure to store some selection box info.
2044 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2045 selectionBoxLinesInfo.Resize( numberOfLines );
2047 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2048 selectionBoxInfo->minX = MAX_FLOAT;
2049 selectionBoxInfo->maxX = MIN_FLOAT;
2051 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2052 float minHighlightX = std::numeric_limits<float>::max();
2053 float maxHighlightX = std::numeric_limits<float>::min();
2055 Vector2 highLightPosition; // The highlight position in decorator's coords.
2057 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2059 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2060 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2063 // Transform to decorator's (control) coords.
2064 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2066 lineRun += firstLineIndex;
2068 // The line height is the addition of the line ascender and the line descender.
2069 // However, the line descender has a negative value, hence the subtraction.
2070 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2072 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2074 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2075 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2076 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2078 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2079 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2080 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2082 // The number of quads of the selection box.
2083 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2084 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2086 // Count the actual number of quads.
2087 unsigned int actualNumberOfQuads = 0u;
2090 // Traverse the glyphs.
2091 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2093 const GlyphInfo& glyph = *( glyphsBuffer + index );
2094 const Vector2& position = *( positionsBuffer + index );
2096 if( splitStartGlyph )
2098 // 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.
2100 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2101 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2102 // Get the direction of the character.
2103 CharacterDirection isCurrentRightToLeft = false;
2104 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2106 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2109 // The end point could be in the middle of the ligature.
2110 // Calculate the number of characters selected.
2111 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2113 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2114 quad.y = selectionBoxInfo->lineOffset;
2115 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2116 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2118 // Store the min and max 'x' for each line.
2119 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2120 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2122 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2123 ++actualNumberOfQuads;
2125 splitStartGlyph = false;
2129 if( splitEndGlyph && ( index == glyphEnd ) )
2131 // 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.
2133 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2134 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2135 // Get the direction of the character.
2136 CharacterDirection isCurrentRightToLeft = false;
2137 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2139 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2142 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2144 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2145 quad.y = selectionBoxInfo->lineOffset;
2146 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2147 quad.w = quad.y + selectionBoxInfo->lineHeight;
2149 // Store the min and max 'x' for each line.
2150 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2151 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2153 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2155 ++actualNumberOfQuads;
2157 splitEndGlyph = false;
2161 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2162 quad.y = selectionBoxInfo->lineOffset;
2163 quad.z = quad.x + glyph.advance;
2164 quad.w = quad.y + selectionBoxInfo->lineHeight;
2166 // Store the min and max 'x' for each line.
2167 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2168 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2170 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2172 ++actualNumberOfQuads;
2174 // Whether to retrieve the next line.
2175 if( index == lastGlyphOfLine )
2178 if( lineIndex < firstLineIndex + numberOfLines )
2180 // Retrieve the next line.
2183 // Get the last glyph of the new line.
2184 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2186 // Keep the offset and height of the current selection box.
2187 const float currentLineOffset = selectionBoxInfo->lineOffset;
2188 const float currentLineHeight = selectionBoxInfo->lineHeight;
2190 // Get the selection box info for the next line.
2193 selectionBoxInfo->minX = MAX_FLOAT;
2194 selectionBoxInfo->maxX = MIN_FLOAT;
2196 // Update the line's vertical offset.
2197 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2199 // The line height is the addition of the line ascender and the line descender.
2200 // However, the line descender has a negative value, hence the subtraction.
2201 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2206 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2207 // The final width is calculated after 'boxifying' the selection.
2208 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2209 endIt = selectionBoxLinesInfo.End();
2213 const SelectionBoxInfo& info = *it;
2215 // Update the size of the highlighted text.
2216 highLightSize.height += info.lineHeight;
2217 minHighlightX = std::min( minHighlightX, info.minX );
2218 maxHighlightX = std::max( maxHighlightX, info.maxX );
2221 // Add extra geometry to 'boxify' the selection.
2223 if( 1u < numberOfLines )
2225 // Boxify the first line.
2226 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2227 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2229 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2230 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2235 quad.y = firstSelectionBoxLineInfo.lineOffset;
2236 quad.z = firstSelectionBoxLineInfo.minX;
2237 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2239 // Boxify at the beginning of the line.
2240 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2242 ++actualNumberOfQuads;
2244 // Update the size of the highlighted text.
2245 minHighlightX = 0.f;
2250 quad.x = firstSelectionBoxLineInfo.maxX;
2251 quad.y = firstSelectionBoxLineInfo.lineOffset;
2252 quad.z = mModel->mVisualModel->mControlSize.width;
2253 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2255 // Boxify at the end of the line.
2256 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2258 ++actualNumberOfQuads;
2260 // Update the size of the highlighted text.
2261 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2264 // Boxify the central lines.
2265 if( 2u < numberOfLines )
2267 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2268 endIt = selectionBoxLinesInfo.End() - 1u;
2272 const SelectionBoxInfo& info = *it;
2275 quad.y = info.lineOffset;
2277 quad.w = info.lineOffset + info.lineHeight;
2279 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2281 ++actualNumberOfQuads;
2284 quad.y = info.lineOffset;
2285 quad.z = mModel->mVisualModel->mControlSize.width;
2286 quad.w = info.lineOffset + info.lineHeight;
2288 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2290 ++actualNumberOfQuads;
2293 // Update the size of the highlighted text.
2294 minHighlightX = 0.f;
2295 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2298 // Boxify the last line.
2299 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2300 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2302 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2303 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2308 quad.y = lastSelectionBoxLineInfo.lineOffset;
2309 quad.z = lastSelectionBoxLineInfo.minX;
2310 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2312 // Boxify at the beginning of the line.
2313 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2315 ++actualNumberOfQuads;
2317 // Update the size of the highlighted text.
2318 minHighlightX = 0.f;
2323 quad.x = lastSelectionBoxLineInfo.maxX;
2324 quad.y = lastSelectionBoxLineInfo.lineOffset;
2325 quad.z = mModel->mVisualModel->mControlSize.width;
2326 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2328 // Boxify at the end of the line.
2329 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2331 ++actualNumberOfQuads;
2333 // Update the size of the highlighted text.
2334 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2338 // Set the actual number of quads.
2339 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2341 // Sets the highlight's size and position. In decorator's coords.
2342 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2343 highLightSize.width = maxHighlightX - minHighlightX;
2345 highLightPosition.x = minHighlightX;
2346 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2347 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2349 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2351 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2353 CursorInfo primaryCursorInfo;
2354 GetCursorPosition( mEventData->mLeftSelectionPosition,
2355 primaryCursorInfo );
2357 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2359 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2361 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2362 primaryCursorInfo.lineHeight );
2364 CursorInfo secondaryCursorInfo;
2365 GetCursorPosition( mEventData->mRightSelectionPosition,
2366 secondaryCursorInfo );
2368 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2370 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2371 secondaryPosition.x,
2372 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2373 secondaryCursorInfo.lineHeight );
2376 // Set the flag to update the decorator.
2377 mEventData->mDecoratorUpdated = true;
2380 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2382 if( NULL == mEventData )
2384 // Nothing to do if there is no text input.
2388 if( IsShowingPlaceholderText() )
2390 // Nothing to do if there is the place-holder text.
2394 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2395 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2396 if( ( 0 == numberOfGlyphs ) ||
2397 ( 0 == numberOfLines ) )
2399 // Nothing to do if there is no text.
2403 // Find which word was selected
2404 CharacterIndex selectionStart( 0 );
2405 CharacterIndex selectionEnd( 0 );
2406 CharacterIndex noTextHitIndex( 0 );
2407 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2408 mModel->mLogicalModel,
2415 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2417 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2419 ChangeState( EventData::SELECTING );
2421 mEventData->mLeftSelectionPosition = selectionStart;
2422 mEventData->mRightSelectionPosition = selectionEnd;
2424 mEventData->mUpdateLeftSelectionPosition = true;
2425 mEventData->mUpdateRightSelectionPosition = true;
2426 mEventData->mUpdateHighlightBox = true;
2428 // It may happen an InputMethodContext commit event arrives before the selection event
2429 // if the InputMethodContext is in pre-edit state. The commit event will set the
2430 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2431 // to false, the highlight box won't be updated.
2432 mEventData->mUpdateCursorPosition = false;
2434 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2436 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2437 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2439 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2441 // Nothing to select. i.e. a white space, out of bounds
2442 ChangeState( EventData::EDITING_WITH_POPUP );
2444 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2446 mEventData->mUpdateCursorPosition = true;
2447 mEventData->mUpdateGrabHandlePosition = true;
2448 mEventData->mScrollAfterUpdatePosition = true;
2449 mEventData->mUpdateInputStyle = true;
2451 else if( Controller::NoTextTap::NO_ACTION == action )
2453 // Nothing to select. i.e. a white space, out of bounds
2454 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2456 mEventData->mUpdateCursorPosition = true;
2457 mEventData->mUpdateGrabHandlePosition = true;
2458 mEventData->mScrollAfterUpdatePosition = true;
2459 mEventData->mUpdateInputStyle = true;
2463 void Controller::Impl::SetPopupButtons()
2466 * Sets the Popup buttons to be shown depending on State.
2468 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2470 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2473 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2475 if( EventData::SELECTING == mEventData->mState )
2477 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2479 if( !IsClipboardEmpty() )
2481 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2482 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2485 if( !mEventData->mAllTextSelected )
2487 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2490 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2492 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2494 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2497 if( !IsClipboardEmpty() )
2499 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2500 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2503 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2505 if ( !IsClipboardEmpty() )
2507 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2508 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2512 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2515 void Controller::Impl::ChangeState( EventData::State newState )
2517 if( NULL == mEventData )
2519 // Nothing to do if there is no text input.
2523 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2525 if( mEventData->mState != newState )
2527 mEventData->mPreviousState = mEventData->mState;
2528 mEventData->mState = newState;
2530 switch( mEventData->mState )
2532 case EventData::INACTIVE:
2534 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2535 mEventData->mDecorator->StopCursorBlink();
2536 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2537 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2538 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2539 mEventData->mDecorator->SetHighlightActive( false );
2540 mEventData->mDecorator->SetPopupActive( false );
2541 mEventData->mDecoratorUpdated = true;
2544 case EventData::INTERRUPTED:
2546 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2547 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2548 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2549 mEventData->mDecorator->SetHighlightActive( false );
2550 mEventData->mDecorator->SetPopupActive( false );
2551 mEventData->mDecoratorUpdated = true;
2554 case EventData::SELECTING:
2556 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2557 mEventData->mDecorator->StopCursorBlink();
2558 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2559 if ( mEventData->mGrabHandleEnabled )
2561 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2562 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2564 mEventData->mDecorator->SetHighlightActive( true );
2565 if( mEventData->mGrabHandlePopupEnabled )
2568 mEventData->mDecorator->SetPopupActive( true );
2570 mEventData->mDecoratorUpdated = true;
2573 case EventData::EDITING:
2575 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2576 if( mEventData->mCursorBlinkEnabled )
2578 mEventData->mDecorator->StartCursorBlink();
2580 // Grab handle is not shown until a tap is received whilst EDITING
2581 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2582 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2583 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2584 mEventData->mDecorator->SetHighlightActive( false );
2585 if( mEventData->mGrabHandlePopupEnabled )
2587 mEventData->mDecorator->SetPopupActive( false );
2589 mEventData->mDecoratorUpdated = true;
2592 case EventData::EDITING_WITH_POPUP:
2594 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2596 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2597 if( mEventData->mCursorBlinkEnabled )
2599 mEventData->mDecorator->StartCursorBlink();
2601 if( mEventData->mSelectionEnabled )
2603 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2604 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2605 mEventData->mDecorator->SetHighlightActive( false );
2607 else if ( mEventData->mGrabHandleEnabled )
2609 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2611 if( mEventData->mGrabHandlePopupEnabled )
2614 mEventData->mDecorator->SetPopupActive( true );
2616 mEventData->mDecoratorUpdated = true;
2619 case EventData::EDITING_WITH_GRAB_HANDLE:
2621 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2623 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2624 if( mEventData->mCursorBlinkEnabled )
2626 mEventData->mDecorator->StartCursorBlink();
2628 // Grab handle is not shown until a tap is received whilst EDITING
2629 if ( mEventData->mGrabHandleEnabled )
2631 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2633 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2634 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2635 mEventData->mDecorator->SetHighlightActive( false );
2636 if( mEventData->mGrabHandlePopupEnabled )
2638 mEventData->mDecorator->SetPopupActive( false );
2640 mEventData->mDecoratorUpdated = true;
2643 case EventData::SELECTION_HANDLE_PANNING:
2645 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2646 mEventData->mDecorator->StopCursorBlink();
2647 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2648 if ( mEventData->mGrabHandleEnabled )
2650 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2651 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2653 mEventData->mDecorator->SetHighlightActive( true );
2654 if( mEventData->mGrabHandlePopupEnabled )
2656 mEventData->mDecorator->SetPopupActive( false );
2658 mEventData->mDecoratorUpdated = true;
2661 case EventData::GRAB_HANDLE_PANNING:
2663 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2665 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2666 if( mEventData->mCursorBlinkEnabled )
2668 mEventData->mDecorator->StartCursorBlink();
2670 if ( mEventData->mGrabHandleEnabled )
2672 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2674 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2675 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2676 mEventData->mDecorator->SetHighlightActive( false );
2677 if( mEventData->mGrabHandlePopupEnabled )
2679 mEventData->mDecorator->SetPopupActive( false );
2681 mEventData->mDecoratorUpdated = true;
2684 case EventData::EDITING_WITH_PASTE_POPUP:
2686 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2688 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2689 if( mEventData->mCursorBlinkEnabled )
2691 mEventData->mDecorator->StartCursorBlink();
2694 if ( mEventData->mGrabHandleEnabled )
2696 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2698 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2699 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2700 mEventData->mDecorator->SetHighlightActive( false );
2702 if( mEventData->mGrabHandlePopupEnabled )
2705 mEventData->mDecorator->SetPopupActive( true );
2707 mEventData->mDecoratorUpdated = true;
2710 case EventData::TEXT_PANNING:
2712 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2713 mEventData->mDecorator->StopCursorBlink();
2714 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2715 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2716 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2718 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2719 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2720 mEventData->mDecorator->SetHighlightActive( true );
2723 if( mEventData->mGrabHandlePopupEnabled )
2725 mEventData->mDecorator->SetPopupActive( false );
2728 mEventData->mDecoratorUpdated = true;
2735 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2736 CursorInfo& cursorInfo )
2738 if( !IsShowingRealText() )
2740 // Do not want to use the place-holder text to set the cursor position.
2742 // Use the line's height of the font's family set to set the cursor's size.
2743 // If there is no font's family set, use the default font.
2744 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2746 cursorInfo.lineOffset = 0.f;
2747 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2748 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2751 if( mModel->mMatchSystemLanguageDirection )
2753 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2756 switch( mModel->mHorizontalAlignment )
2758 case Text::HorizontalAlignment::BEGIN :
2762 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2766 cursorInfo.primaryPosition.x = 0.f;
2770 case Text::HorizontalAlignment::CENTER:
2772 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2775 case Text::HorizontalAlignment::END:
2779 cursorInfo.primaryPosition.x = 0.f;
2783 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2789 // Nothing else to do.
2793 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2794 GetCursorPositionParameters parameters;
2795 parameters.visualModel = mModel->mVisualModel;
2796 parameters.logicalModel = mModel->mLogicalModel;
2797 parameters.metrics = mMetrics;
2798 parameters.logical = logical;
2799 parameters.isMultiline = isMultiLine;
2801 Text::GetCursorPosition( parameters,
2804 // Adds Outline offset.
2805 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2806 cursorInfo.primaryPosition.x += outlineWidth;
2807 cursorInfo.primaryPosition.y += outlineWidth;
2808 cursorInfo.secondaryPosition.x += outlineWidth;
2809 cursorInfo.secondaryPosition.y += outlineWidth;
2813 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2815 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2816 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2818 if( 0.f > cursorInfo.primaryPosition.x )
2820 cursorInfo.primaryPosition.x = 0.f;
2823 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2824 if( cursorInfo.primaryPosition.x > edgeWidth )
2826 cursorInfo.primaryPosition.x = edgeWidth;
2831 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2833 if( NULL == mEventData )
2835 // Nothing to do if there is no text input.
2839 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2841 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2842 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2844 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2845 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2847 if( numberOfCharacters > 1u )
2849 const Script script = mModel->mLogicalModel->GetScript( index );
2850 if( HasLigatureMustBreak( script ) )
2852 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2853 numberOfCharacters = 1u;
2858 while( 0u == numberOfCharacters )
2861 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2865 if( index < mEventData->mPrimaryCursorPosition )
2867 cursorIndex -= numberOfCharacters;
2871 cursorIndex += numberOfCharacters;
2874 // Will update the cursor hook position.
2875 mEventData->mUpdateCursorHookPosition = true;
2880 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2883 if( NULL == mEventData )
2885 // Nothing to do if there is no text input.
2886 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2890 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2892 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2894 // Sets the cursor position.
2895 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2898 cursorInfo.primaryCursorHeight,
2899 cursorInfo.lineHeight );
2900 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2902 if( mEventData->mUpdateGrabHandlePosition )
2904 // Sets the grab handle position.
2905 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2907 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2908 cursorInfo.lineHeight );
2911 if( cursorInfo.isSecondaryCursor )
2913 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2914 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2915 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2916 cursorInfo.secondaryCursorHeight,
2917 cursorInfo.lineHeight );
2918 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2921 // Set which cursors are active according the state.
2922 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2924 if( cursorInfo.isSecondaryCursor )
2926 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2930 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2935 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2938 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2941 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2942 const CursorInfo& cursorInfo )
2944 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2945 ( RIGHT_SELECTION_HANDLE != handleType ) )
2950 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2952 // Sets the handle's position.
2953 mEventData->mDecorator->SetPosition( handleType,
2955 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2956 cursorInfo.lineHeight );
2958 // If selection handle at start of the text and other at end of the text then all text is selected.
2959 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2960 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2961 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2964 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2966 // Clamp between -space & -alignment offset.
2968 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2970 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2971 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2972 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2974 mEventData->mDecoratorUpdated = true;
2978 mModel->mScrollPosition.x = 0.f;
2982 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2984 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2986 // Nothing to do if the text is single line.
2990 // Clamp between -space & 0.
2991 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2993 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2994 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2995 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2997 mEventData->mDecoratorUpdated = true;
3001 mModel->mScrollPosition.y = 0.f;
3005 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3007 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3009 // position is in actor's coords.
3010 const float positionEndX = position.x + cursorWidth;
3011 const float positionEndY = position.y + lineHeight;
3013 // Transform the position to decorator coords.
3014 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3015 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3017 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3018 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3020 if( decoratorPositionBeginX < 0.f )
3022 mModel->mScrollPosition.x = -position.x;
3024 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3026 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3029 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3031 if( decoratorPositionBeginY < 0.f )
3033 mModel->mScrollPosition.y = -position.y;
3035 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3037 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3042 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3044 // Get the current cursor position in decorator coords.
3045 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3047 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3051 // Calculate the offset to match the cursor position before the character was deleted.
3052 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3054 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3055 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3057 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3058 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3062 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3063 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3065 // Makes the new cursor position visible if needed.
3066 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3069 void Controller::Impl::RequestRelayout()
3071 if( NULL != mControlInterface )
3073 mControlInterface->RequestTextRelayout();
3079 } // namespace Toolkit