2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderFont( NULL ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false ),
109 mIsPlaceholderPixelSize( false ),
110 mIsPlaceholderElideEnabled( false ),
111 mPlaceholderEllipsisFlag( false )
113 mImfManager = ImfManager::Get();
116 EventData::~EventData()
119 bool Controller::Impl::ProcessInputEvents()
121 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
122 if( NULL == mEventData )
124 // Nothing to do if there is no text input.
125 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
129 if( mEventData->mDecorator )
131 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
132 iter != mEventData->mEventQueue.end();
137 case Event::CURSOR_KEY_EVENT:
139 OnCursorKeyEvent( *iter );
142 case Event::TAP_EVENT:
147 case Event::LONG_PRESS_EVENT:
149 OnLongPressEvent( *iter );
152 case Event::PAN_EVENT:
157 case Event::GRAB_HANDLE_EVENT:
158 case Event::LEFT_SELECTION_HANDLE_EVENT:
159 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
161 OnHandleEvent( *iter );
166 OnSelectEvent( *iter );
169 case Event::SELECT_ALL:
178 if( mEventData->mUpdateCursorPosition ||
179 mEventData->mUpdateHighlightBox )
184 // The cursor must also be repositioned after inserts into the model
185 if( mEventData->mUpdateCursorPosition )
187 // Updates the cursor position and scrolls the text to make it visible.
188 CursorInfo cursorInfo;
189 // Calculate the cursor position from the new cursor index.
190 GetCursorPosition( mEventData->mPrimaryCursorPosition,
193 if( mEventData->mUpdateCursorHookPosition )
195 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
196 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
197 mEventData->mUpdateCursorHookPosition = false;
200 // Scroll first the text after delete ...
201 if( mEventData->mScrollAfterDelete )
203 ScrollTextToMatchCursor( cursorInfo );
206 // ... then, text can be scrolled to make the cursor visible.
207 if( mEventData->mScrollAfterUpdatePosition )
209 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
210 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
212 mEventData->mScrollAfterUpdatePosition = false;
213 mEventData->mScrollAfterDelete = false;
215 UpdateCursorPosition( cursorInfo );
217 mEventData->mDecoratorUpdated = true;
218 mEventData->mUpdateCursorPosition = false;
219 mEventData->mUpdateGrabHandlePosition = false;
223 CursorInfo leftHandleInfo;
224 CursorInfo rightHandleInfo;
226 if( mEventData->mUpdateHighlightBox )
228 GetCursorPosition( mEventData->mLeftSelectionPosition,
231 GetCursorPosition( mEventData->mRightSelectionPosition,
234 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
236 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
238 CursorInfo& infoLeft = leftHandleInfo;
240 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
241 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
243 CursorInfo& infoRight = rightHandleInfo;
245 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
246 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
250 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
252 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
253 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
258 if( mEventData->mUpdateLeftSelectionPosition )
260 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
264 mEventData->mDecoratorUpdated = true;
265 mEventData->mUpdateLeftSelectionPosition = false;
268 if( mEventData->mUpdateRightSelectionPosition )
270 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
274 mEventData->mDecoratorUpdated = true;
275 mEventData->mUpdateRightSelectionPosition = false;
278 if( mEventData->mUpdateHighlightBox )
280 RepositionSelectionHandles();
282 mEventData->mUpdateLeftSelectionPosition = false;
283 mEventData->mUpdateRightSelectionPosition = false;
284 mEventData->mUpdateHighlightBox = false;
285 mEventData->mIsLeftHandleSelected = false;
286 mEventData->mIsRightHandleSelected = false;
289 mEventData->mScrollAfterUpdatePosition = false;
292 if( mEventData->mUpdateInputStyle )
294 // Keep a copy of the current input style.
295 InputStyle currentInputStyle;
296 currentInputStyle.Copy( mEventData->mInputStyle );
298 // Set the default style first.
299 RetrieveDefaultInputStyle( mEventData->mInputStyle );
301 // Get the character index from the cursor index.
302 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
304 // Retrieve the style from the style runs stored in the logical model.
305 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
307 // Compare if the input style has changed.
308 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
310 if( hasInputStyleChanged )
312 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
313 // Queue the input style changed signal.
314 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
317 mEventData->mUpdateInputStyle = false;
320 mEventData->mEventQueue.clear();
322 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
324 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
325 mEventData->mDecoratorUpdated = false;
327 return decoratorUpdated;
330 void Controller::Impl::NotifyImfManager()
332 if( mEventData && mEventData->mImfManager )
334 CharacterIndex cursorPosition = GetLogicalCursorPosition();
336 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
338 // Update the cursor position by removing the initial white spaces.
339 if( cursorPosition < numberOfWhiteSpaces )
345 cursorPosition -= numberOfWhiteSpaces;
348 mEventData->mImfManager.SetCursorPosition( cursorPosition );
349 mEventData->mImfManager.NotifyCursorPosition();
353 void Controller::Impl::NotifyImfMultiLineStatus()
357 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
358 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
362 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
364 CharacterIndex cursorPosition = 0u;
368 if( ( EventData::SELECTING == mEventData->mState ) ||
369 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
371 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
375 cursorPosition = mEventData->mPrimaryCursorPosition;
379 return cursorPosition;
382 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
384 Length numberOfWhiteSpaces = 0u;
386 // Get the buffer to the text.
387 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
389 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
390 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
392 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
398 return numberOfWhiteSpaces;
401 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
403 // Get the total number of characters.
404 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
406 // Retrieve the text.
407 if( 0u != numberOfCharacters )
409 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
413 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
415 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
416 mTextUpdateInfo.mStartGlyphIndex = 0u;
417 mTextUpdateInfo.mStartLineIndex = 0u;
418 numberOfCharacters = 0u;
420 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
421 if( 0u == numberOfParagraphs )
423 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
424 numberOfCharacters = 0u;
426 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
427 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : 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;
444 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
445 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
447 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
448 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
450 // Nothing else to do;
454 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
458 Length numberOfCharactersToUpdate = 0u;
459 if( mTextUpdateInfo.mFullRelayoutNeeded )
461 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
465 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
467 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
468 numberOfCharactersToUpdate,
469 paragraphsToBeUpdated );
472 if( 0u != paragraphsToBeUpdated.Count() )
474 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
475 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
476 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
478 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
479 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
481 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
482 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
483 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
484 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
486 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
487 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
489 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
493 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
497 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
498 if( numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove )
500 mTextUpdateInfo.mRequestedNumberOfCharacters = 0u;
504 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
506 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
509 void Controller::Impl::ClearFullModelData( OperationsMask operations )
511 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
513 mModel->mLogicalModel->mLineBreakInfo.Clear();
514 mModel->mLogicalModel->mParagraphInfo.Clear();
517 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
519 mModel->mLogicalModel->mLineBreakInfo.Clear();
522 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
524 mModel->mLogicalModel->mScriptRuns.Clear();
527 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
529 mModel->mLogicalModel->mFontRuns.Clear();
532 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
534 if( NO_OPERATION != ( BIDI_INFO & operations ) )
536 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
537 mModel->mLogicalModel->mCharacterDirections.Clear();
540 if( NO_OPERATION != ( REORDER & operations ) )
542 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
543 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
544 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
548 BidirectionalLineInfoRun& bidiLineInfo = *it;
550 free( bidiLineInfo.visualToLogicalMap );
551 bidiLineInfo.visualToLogicalMap = NULL;
553 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
557 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
559 mModel->mVisualModel->mGlyphs.Clear();
560 mModel->mVisualModel->mGlyphsToCharacters.Clear();
561 mModel->mVisualModel->mCharactersToGlyph.Clear();
562 mModel->mVisualModel->mCharactersPerGlyph.Clear();
563 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
564 mModel->mVisualModel->mGlyphPositions.Clear();
567 if( NO_OPERATION != ( LAYOUT & operations ) )
569 mModel->mVisualModel->mLines.Clear();
572 if( NO_OPERATION != ( COLOR & operations ) )
574 mModel->mVisualModel->mColorIndices.Clear();
578 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
580 const CharacterIndex endIndexPlusOne = endIndex + 1u;
582 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
584 // Clear the line break info.
585 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
587 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
588 lineBreakInfoBuffer + endIndexPlusOne );
590 // Clear the paragraphs.
591 ClearCharacterRuns( startIndex,
593 mModel->mLogicalModel->mParagraphInfo );
596 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
598 // Clear the word break info.
599 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
601 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
602 wordBreakInfoBuffer + endIndexPlusOne );
605 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
607 // Clear the scripts.
608 ClearCharacterRuns( startIndex,
610 mModel->mLogicalModel->mScriptRuns );
613 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
616 ClearCharacterRuns( startIndex,
618 mModel->mLogicalModel->mFontRuns );
621 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
623 if( NO_OPERATION != ( BIDI_INFO & operations ) )
625 // Clear the bidirectional paragraph info.
626 ClearCharacterRuns( startIndex,
628 mModel->mLogicalModel->mBidirectionalParagraphInfo );
630 // Clear the character's directions.
631 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
633 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
634 characterDirectionsBuffer + endIndexPlusOne );
637 if( NO_OPERATION != ( REORDER & operations ) )
639 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
640 uint32_t endRemoveIndex = startRemoveIndex;
641 ClearCharacterRuns( startIndex,
643 mModel->mLogicalModel->mBidirectionalLineInfo,
647 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
649 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
650 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
651 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
655 BidirectionalLineInfoRun& bidiLineInfo = *it;
657 free( bidiLineInfo.visualToLogicalMap );
658 bidiLineInfo.visualToLogicalMap = NULL;
661 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
662 bidirectionalLineInfoBuffer + endRemoveIndex );
667 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
669 const CharacterIndex endIndexPlusOne = endIndex + 1u;
670 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
672 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
673 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
674 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
676 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
677 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
679 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
681 // Update the character to glyph indices.
682 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
683 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
687 CharacterIndex& index = *it;
688 index -= numberOfGlyphsRemoved;
691 // Clear the character to glyph conversion table.
692 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
693 charactersToGlyphBuffer + endIndexPlusOne );
695 // Clear the glyphs per character table.
696 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
697 glyphsPerCharacterBuffer + endIndexPlusOne );
699 // Clear the glyphs buffer.
700 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
701 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
702 glyphsBuffer + endGlyphIndexPlusOne );
704 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
706 // Update the glyph to character indices.
707 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
708 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
712 CharacterIndex& index = *it;
713 index -= numberOfCharactersRemoved;
716 // Clear the glyphs to characters buffer.
717 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
718 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
720 // Clear the characters per glyph buffer.
721 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
722 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
723 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
725 // Clear the positions buffer.
726 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
727 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
728 positionsBuffer + endGlyphIndexPlusOne );
731 if( NO_OPERATION != ( LAYOUT & operations ) )
734 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
735 uint32_t endRemoveIndex = startRemoveIndex;
736 ClearCharacterRuns( startIndex,
738 mModel->mVisualModel->mLines,
742 // Will update the glyph runs.
743 startRemoveIndex = mModel->mVisualModel->mLines.Count();
744 endRemoveIndex = startRemoveIndex;
745 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
746 endGlyphIndexPlusOne - 1u,
747 mModel->mVisualModel->mLines,
751 // Set the line index from where to insert the new laid-out lines.
752 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
754 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
755 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
756 linesBuffer + endRemoveIndex );
759 if( NO_OPERATION != ( COLOR & operations ) )
761 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
763 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
764 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
765 colorIndexBuffer + endGlyphIndexPlusOne );
770 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
772 if( mTextUpdateInfo.mClearAll ||
773 ( ( 0u == startIndex ) &&
774 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
776 ClearFullModelData( operations );
780 // Clear the model data related with characters.
781 ClearCharacterModelData( startIndex, endIndex, operations );
783 // Clear the model data related with glyphs.
784 ClearGlyphModelData( startIndex, endIndex, operations );
787 // The estimated number of lines. Used to avoid reallocations when layouting.
788 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
790 mModel->mVisualModel->ClearCaches();
793 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
795 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
797 // Calculate the operations to be done.
798 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
800 if( NO_OPERATION == operations )
802 // Nothing to do if no operations are pending and required.
806 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
807 Vector<Character> displayCharacters;
808 bool useHiddenText = false;
809 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
811 mHiddenInput->Substitute( srcCharacters,displayCharacters );
812 useHiddenText = true;
815 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
816 const Length numberOfCharacters = utf32Characters.Count();
818 // Index to the first character of the first paragraph to be updated.
819 CharacterIndex startIndex = 0u;
820 // Number of characters of the paragraphs to be removed.
821 Length paragraphCharacters = 0u;
823 CalculateTextUpdateIndices( paragraphCharacters );
824 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
826 if( mTextUpdateInfo.mClearAll ||
827 ( 0u != paragraphCharacters ) )
829 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
832 mTextUpdateInfo.mClearAll = false;
834 // Whether the model is updated.
835 bool updated = false;
837 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
838 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
840 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
842 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
843 // calculate the bidirectional info for each 'paragraph'.
844 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
845 // is not shaped together).
846 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
848 SetLineBreakInfo( utf32Characters,
850 requestedNumberOfCharacters,
853 // Create the paragraph info.
854 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
855 requestedNumberOfCharacters );
859 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
860 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
862 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
863 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
865 SetWordBreakInfo( utf32Characters,
867 requestedNumberOfCharacters,
872 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
873 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
875 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
876 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
878 if( getScripts || validateFonts )
880 // Validates the fonts assigned by the application or assigns default ones.
881 // It makes sure all the characters are going to be rendered by the correct font.
882 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
886 // Retrieves the scripts used in the text.
887 multilanguageSupport.SetScripts( utf32Characters,
889 requestedNumberOfCharacters,
895 // Validate the fonts set through the mark-up string.
896 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
898 // Get the default font's description.
899 TextAbstraction::FontDescription defaultFontDescription;
900 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
902 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
904 // If the placeholder font is set specifically, only placeholder font is changed.
905 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
906 if( mEventData->mPlaceholderFont->sizeDefined )
908 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
911 else if( NULL != mFontDefaults )
913 // Set the normal font and the placeholder font.
914 defaultFontDescription = mFontDefaults->mFontDescription;
915 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
918 // Validates the fonts. If there is a character with no assigned font it sets a default one.
919 // After this call, fonts are validated.
920 multilanguageSupport.ValidateFonts( utf32Characters,
923 defaultFontDescription,
926 requestedNumberOfCharacters,
932 Vector<Character> mirroredUtf32Characters;
933 bool textMirrored = false;
934 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
935 if( NO_OPERATION != ( BIDI_INFO & operations ) )
937 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
938 bidirectionalInfo.Reserve( numberOfParagraphs );
940 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
941 SetBidirectionalInfo( utf32Characters,
945 requestedNumberOfCharacters,
948 if( 0u != bidirectionalInfo.Count() )
950 // Only set the character directions if there is right to left characters.
951 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
952 GetCharactersDirection( bidirectionalInfo,
955 requestedNumberOfCharacters,
958 // This paragraph has right to left text. Some characters may need to be mirrored.
959 // TODO: consider if the mirrored string can be stored as well.
961 textMirrored = GetMirroredText( utf32Characters,
965 requestedNumberOfCharacters,
966 mirroredUtf32Characters );
970 // There is no right to left characters. Clear the directions vector.
971 mModel->mLogicalModel->mCharacterDirections.Clear();
976 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
977 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
978 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
979 Vector<GlyphIndex> newParagraphGlyphs;
980 newParagraphGlyphs.Reserve( numberOfParagraphs );
982 const Length currentNumberOfGlyphs = glyphs.Count();
983 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
985 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
987 ShapeText( textToShape,
992 mTextUpdateInfo.mStartGlyphIndex,
993 requestedNumberOfCharacters,
995 glyphsToCharactersMap,
997 newParagraphGlyphs );
999 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1000 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1001 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1005 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1007 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1009 GlyphInfo* glyphsBuffer = glyphs.Begin();
1010 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1012 // Update the width and advance of all new paragraph characters.
1013 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1015 const GlyphIndex index = *it;
1016 GlyphInfo& glyph = *( glyphsBuffer + index );
1018 glyph.xBearing = 0.f;
1020 glyph.advance = 0.f;
1025 if( NO_OPERATION != ( COLOR & operations ) )
1027 // Set the color runs in glyphs.
1028 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1029 mModel->mVisualModel->mCharactersToGlyph,
1030 mModel->mVisualModel->mGlyphsPerCharacter,
1032 mTextUpdateInfo.mStartGlyphIndex,
1033 requestedNumberOfCharacters,
1034 mModel->mVisualModel->mColors,
1035 mModel->mVisualModel->mColorIndices );
1040 if( ( NULL != mEventData ) &&
1041 mEventData->mPreEditFlag &&
1042 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1044 // Add the underline for the pre-edit text.
1045 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1046 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1048 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1049 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1050 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1051 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1053 GlyphRun underlineRun;
1054 underlineRun.glyphIndex = glyphStart;
1055 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1057 // TODO: At the moment the underline runs are only for pre-edit.
1058 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1061 // The estimated number of lines. Used to avoid reallocations when layouting.
1062 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1064 // Set the previous number of characters for the next time the text is updated.
1065 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1070 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1072 // Sets the default text's color.
1073 inputStyle.textColor = mTextColor;
1074 inputStyle.isDefaultColor = true;
1076 inputStyle.familyName.clear();
1077 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1078 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1079 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1080 inputStyle.size = 0.f;
1082 inputStyle.lineSpacing = 0.f;
1084 inputStyle.underlineProperties.clear();
1085 inputStyle.shadowProperties.clear();
1086 inputStyle.embossProperties.clear();
1087 inputStyle.outlineProperties.clear();
1089 inputStyle.isFamilyDefined = false;
1090 inputStyle.isWeightDefined = false;
1091 inputStyle.isWidthDefined = false;
1092 inputStyle.isSlantDefined = false;
1093 inputStyle.isSizeDefined = false;
1095 inputStyle.isLineSpacingDefined = false;
1097 inputStyle.isUnderlineDefined = false;
1098 inputStyle.isShadowDefined = false;
1099 inputStyle.isEmbossDefined = false;
1100 inputStyle.isOutlineDefined = false;
1102 // Sets the default font's family name, weight, width, slant and size.
1105 if( mFontDefaults->familyDefined )
1107 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1108 inputStyle.isFamilyDefined = true;
1111 if( mFontDefaults->weightDefined )
1113 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1114 inputStyle.isWeightDefined = true;
1117 if( mFontDefaults->widthDefined )
1119 inputStyle.width = mFontDefaults->mFontDescription.width;
1120 inputStyle.isWidthDefined = true;
1123 if( mFontDefaults->slantDefined )
1125 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1126 inputStyle.isSlantDefined = true;
1129 if( mFontDefaults->sizeDefined )
1131 inputStyle.size = mFontDefaults->mDefaultPointSize;
1132 inputStyle.isSizeDefined = true;
1137 float Controller::Impl::GetDefaultFontLineHeight()
1139 FontId defaultFontId = 0u;
1140 if( NULL == mFontDefaults )
1142 TextAbstraction::FontDescription fontDescription;
1143 defaultFontId = mFontClient.GetFontId( fontDescription );
1147 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1150 Text::FontMetrics fontMetrics;
1151 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1153 return( fontMetrics.ascender - fontMetrics.descender );
1156 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1158 if( NULL == mEventData )
1160 // Nothing to do if there is no text input.
1164 int keyCode = event.p1.mInt;
1165 bool isShiftModifier = event.p2.mBool;
1167 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1169 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1171 if( mEventData->mPrimaryCursorPosition > 0u )
1173 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1176 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1178 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1180 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1183 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1185 // Ignore Shift-Up for text selection for now.
1187 // Get first the line index of the current cursor position index.
1188 CharacterIndex characterIndex = 0u;
1190 if( mEventData->mPrimaryCursorPosition > 0u )
1192 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1195 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1196 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1198 // Retrieve the cursor position info.
1199 CursorInfo cursorInfo;
1200 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1203 // Get the line above.
1204 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1206 // Get the next hit 'y' point.
1207 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1209 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1210 bool matchedCharacter = false;
1211 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1212 mModel->mLogicalModel,
1214 mEventData->mCursorHookPositionX,
1216 CharacterHitTest::TAP,
1219 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1221 // Ignore Shift-Down for text selection for now.
1223 // Get first the line index of the current cursor position index.
1224 CharacterIndex characterIndex = 0u;
1226 if( mEventData->mPrimaryCursorPosition > 0u )
1228 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1231 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1233 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1235 // Retrieve the cursor position info.
1236 CursorInfo cursorInfo;
1237 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1240 // Get the line below.
1241 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1243 // Get the next hit 'y' point.
1244 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1246 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1247 bool matchedCharacter = false;
1248 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1249 mModel->mLogicalModel,
1251 mEventData->mCursorHookPositionX,
1253 CharacterHitTest::TAP,
1258 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1260 // Update selection position after moving the cursor
1261 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1262 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1265 if ( isShiftModifier && IsShowingRealText() )
1267 // Handle text selection
1268 bool selecting = false;
1270 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1272 // Shift-Left/Right to select the text
1273 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1274 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1276 mEventData->mRightSelectionPosition += cursorPositionDelta;
1280 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1282 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1288 // Notify the cursor position to the imf manager.
1289 if( mEventData->mImfManager )
1291 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1292 mEventData->mImfManager.NotifyCursorPosition();
1295 ChangeState( EventData::SELECTING );
1297 mEventData->mUpdateLeftSelectionPosition = true;
1298 mEventData->mUpdateRightSelectionPosition = true;
1299 mEventData->mUpdateGrabHandlePosition = true;
1300 mEventData->mUpdateHighlightBox = true;
1302 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1303 if( mEventData->mGrabHandlePopupEnabled )
1305 mEventData->mDecorator->SetPopupActive( false );
1311 // Handle normal cursor move
1312 ChangeState( EventData::EDITING );
1313 mEventData->mUpdateCursorPosition = true;
1316 mEventData->mUpdateInputStyle = true;
1317 mEventData->mScrollAfterUpdatePosition = true;
1320 void Controller::Impl::OnTapEvent( const Event& event )
1322 if( NULL != mEventData )
1324 const unsigned int tapCount = event.p1.mUint;
1326 if( 1u == tapCount )
1328 if( IsShowingRealText() )
1330 // Convert from control's coords to text's coords.
1331 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1332 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1334 // Keep the tap 'x' position. Used to move the cursor.
1335 mEventData->mCursorHookPositionX = xPosition;
1337 // Whether to touch point hits on a glyph.
1338 bool matchedCharacter = false;
1339 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1340 mModel->mLogicalModel,
1344 CharacterHitTest::TAP,
1347 // When the cursor position is changing, delay cursor blinking
1348 mEventData->mDecorator->DelayCursorBlink();
1352 mEventData->mPrimaryCursorPosition = 0u;
1355 // Update selection position after tapping
1356 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1357 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1359 mEventData->mUpdateCursorPosition = true;
1360 mEventData->mUpdateGrabHandlePosition = true;
1361 mEventData->mScrollAfterUpdatePosition = true;
1362 mEventData->mUpdateInputStyle = true;
1364 // Notify the cursor position to the imf manager.
1365 if( mEventData->mImfManager )
1367 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1368 mEventData->mImfManager.NotifyCursorPosition();
1371 else if( 2u == tapCount )
1373 if( mEventData->mSelectionEnabled )
1375 // Convert from control's coords to text's coords.
1376 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1377 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1379 // Calculates the logical position from the x,y coords.
1380 RepositionSelectionHandles( xPosition,
1382 mEventData->mDoubleTapAction );
1388 void Controller::Impl::OnPanEvent( const Event& event )
1390 if( NULL == mEventData )
1392 // Nothing to do if there is no text input.
1396 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1397 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1399 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1401 // Nothing to do if scrolling is not enabled.
1405 const int state = event.p1.mInt;
1409 case Gesture::Started:
1411 // Will remove the cursor, handles or text's popup, ...
1412 ChangeState( EventData::TEXT_PANNING );
1415 case Gesture::Continuing:
1417 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1418 const Vector2 currentScroll = mModel->mScrollPosition;
1420 if( isHorizontalScrollEnabled )
1422 const float displacementX = event.p2.mFloat;
1423 mModel->mScrollPosition.x += displacementX;
1425 ClampHorizontalScroll( layoutSize );
1428 if( isVerticalScrollEnabled )
1430 const float displacementY = event.p3.mFloat;
1431 mModel->mScrollPosition.y += displacementY;
1433 ClampVerticalScroll( layoutSize );
1436 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1439 case Gesture::Finished:
1440 case Gesture::Cancelled: // FALLTHROUGH
1442 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1443 ChangeState( mEventData->mPreviousState );
1451 void Controller::Impl::OnLongPressEvent( const Event& event )
1453 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1455 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1457 ChangeState( EventData::EDITING_WITH_POPUP );
1458 mEventData->mDecoratorUpdated = true;
1459 mEventData->mUpdateInputStyle = true;
1463 if( mEventData->mSelectionEnabled )
1465 // Convert from control's coords to text's coords.
1466 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1467 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1469 // Calculates the logical position from the x,y coords.
1470 RepositionSelectionHandles( xPosition,
1472 mEventData->mLongPressAction );
1477 void Controller::Impl::OnHandleEvent( const Event& event )
1479 if( NULL == mEventData )
1481 // Nothing to do if there is no text input.
1485 const unsigned int state = event.p1.mUint;
1486 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1487 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1489 if( HANDLE_PRESSED == state )
1491 // Convert from decorator's coords to text's coords.
1492 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1493 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1495 // Need to calculate the handle's new position.
1496 bool matchedCharacter = false;
1497 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1498 mModel->mLogicalModel,
1502 CharacterHitTest::SCROLL,
1505 if( Event::GRAB_HANDLE_EVENT == event.type )
1507 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1509 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1511 // Updates the cursor position if the handle's new position is different than the current one.
1512 mEventData->mUpdateCursorPosition = true;
1513 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1514 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1515 mEventData->mPrimaryCursorPosition = handleNewPosition;
1518 // 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.
1519 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1521 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1523 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1525 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1526 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1528 // Updates the highlight box if the handle's new position is different than the current one.
1529 mEventData->mUpdateHighlightBox = true;
1530 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1531 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1532 mEventData->mLeftSelectionPosition = handleNewPosition;
1535 // 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.
1536 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1538 // Will define the order to scroll the text to match the handle position.
1539 mEventData->mIsLeftHandleSelected = true;
1540 mEventData->mIsRightHandleSelected = false;
1542 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1544 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1546 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1547 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1549 // Updates the highlight box if the handle's new position is different than the current one.
1550 mEventData->mUpdateHighlightBox = true;
1551 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1552 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1553 mEventData->mRightSelectionPosition = handleNewPosition;
1556 // 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.
1557 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1559 // Will define the order to scroll the text to match the handle position.
1560 mEventData->mIsLeftHandleSelected = false;
1561 mEventData->mIsRightHandleSelected = true;
1563 } // end ( HANDLE_PRESSED == state )
1564 else if( ( HANDLE_RELEASED == state ) ||
1565 handleStopScrolling )
1567 CharacterIndex handlePosition = 0u;
1568 if( handleStopScrolling || isSmoothHandlePanEnabled )
1570 // Convert from decorator's coords to text's coords.
1571 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1572 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1574 bool matchedCharacter = false;
1575 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1576 mModel->mLogicalModel,
1580 CharacterHitTest::SCROLL,
1584 if( Event::GRAB_HANDLE_EVENT == event.type )
1586 mEventData->mUpdateCursorPosition = true;
1587 mEventData->mUpdateGrabHandlePosition = true;
1588 mEventData->mUpdateInputStyle = true;
1590 if( !IsClipboardEmpty() )
1592 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1595 if( handleStopScrolling || isSmoothHandlePanEnabled )
1597 mEventData->mScrollAfterUpdatePosition = true;
1598 mEventData->mPrimaryCursorPosition = handlePosition;
1601 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1603 ChangeState( EventData::SELECTING );
1605 mEventData->mUpdateHighlightBox = true;
1606 mEventData->mUpdateLeftSelectionPosition = true;
1607 mEventData->mUpdateRightSelectionPosition = true;
1609 if( handleStopScrolling || isSmoothHandlePanEnabled )
1611 mEventData->mScrollAfterUpdatePosition = true;
1613 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1614 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1616 mEventData->mLeftSelectionPosition = handlePosition;
1620 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1622 ChangeState( EventData::SELECTING );
1624 mEventData->mUpdateHighlightBox = true;
1625 mEventData->mUpdateRightSelectionPosition = true;
1626 mEventData->mUpdateLeftSelectionPosition = true;
1628 if( handleStopScrolling || isSmoothHandlePanEnabled )
1630 mEventData->mScrollAfterUpdatePosition = true;
1631 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1632 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1634 mEventData->mRightSelectionPosition = handlePosition;
1639 mEventData->mDecoratorUpdated = true;
1640 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1641 else if( HANDLE_SCROLLING == state )
1643 const float xSpeed = event.p2.mFloat;
1644 const float ySpeed = event.p3.mFloat;
1645 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1646 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1648 mModel->mScrollPosition.x += xSpeed;
1649 mModel->mScrollPosition.y += ySpeed;
1651 ClampHorizontalScroll( layoutSize );
1652 ClampVerticalScroll( layoutSize );
1654 bool endOfScroll = false;
1655 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1657 // Notify the decorator there is no more text to scroll.
1658 // The decorator won't send more scroll events.
1659 mEventData->mDecorator->NotifyEndOfScroll();
1660 // Still need to set the position of the handle.
1664 // Set the position of the handle.
1665 const bool scrollRightDirection = xSpeed > 0.f;
1666 const bool scrollBottomDirection = ySpeed > 0.f;
1667 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1668 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1670 if( Event::GRAB_HANDLE_EVENT == event.type )
1672 ChangeState( EventData::GRAB_HANDLE_PANNING );
1674 // Get the grab handle position in decorator coords.
1675 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1677 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1679 // Position the grag handle close to either the left or right edge.
1680 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1683 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1685 position.x = mEventData->mCursorHookPositionX;
1687 // Position the grag handle close to either the top or bottom edge.
1688 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1691 // Get the new handle position.
1692 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1693 bool matchedCharacter = false;
1694 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1695 mModel->mLogicalModel,
1697 position.x - mModel->mScrollPosition.x,
1698 position.y - mModel->mScrollPosition.y,
1699 CharacterHitTest::SCROLL,
1702 if( mEventData->mPrimaryCursorPosition != handlePosition )
1704 mEventData->mUpdateCursorPosition = true;
1705 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1706 mEventData->mScrollAfterUpdatePosition = true;
1707 mEventData->mPrimaryCursorPosition = handlePosition;
1709 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1711 // Updates the decorator if the soft handle panning is enabled.
1712 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1714 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1716 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1718 // Get the selection handle position in decorator coords.
1719 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1721 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1723 // Position the selection 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 selection handle's position is in decorator's coords. Need to transform 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( leftSelectionHandleEvent )
1748 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1750 if( differentHandles || endOfScroll )
1752 mEventData->mUpdateHighlightBox = true;
1753 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1754 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1755 mEventData->mLeftSelectionPosition = handlePosition;
1760 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1761 if( differentHandles || endOfScroll )
1763 mEventData->mUpdateHighlightBox = true;
1764 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1765 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1766 mEventData->mRightSelectionPosition = handlePosition;
1770 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1772 RepositionSelectionHandles();
1774 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1777 mEventData->mDecoratorUpdated = true;
1778 } // end ( HANDLE_SCROLLING == state )
1781 void Controller::Impl::OnSelectEvent( const Event& event )
1783 if( NULL == mEventData )
1785 // Nothing to do if there is no text.
1789 if( mEventData->mSelectionEnabled )
1791 // Convert from control's coords to text's coords.
1792 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1793 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1795 // Calculates the logical position from the x,y coords.
1796 RepositionSelectionHandles( xPosition,
1798 Controller::NoTextTap::HIGHLIGHT );
1802 void Controller::Impl::OnSelectAllEvent()
1804 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1806 if( NULL == mEventData )
1808 // Nothing to do if there is no text.
1812 if( mEventData->mSelectionEnabled )
1814 ChangeState( EventData::SELECTING );
1816 mEventData->mLeftSelectionPosition = 0u;
1817 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1819 mEventData->mScrollAfterUpdatePosition = true;
1820 mEventData->mUpdateLeftSelectionPosition = true;
1821 mEventData->mUpdateRightSelectionPosition = true;
1822 mEventData->mUpdateHighlightBox = true;
1826 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1828 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1830 // Nothing to select if handles are in the same place.
1831 selectedText.clear();
1835 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1837 //Get start and end position of selection
1838 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1839 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1841 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1842 const Length numberOfCharacters = utf32Characters.Count();
1844 // Validate the start and end selection points
1845 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1847 //Get text as a UTF8 string
1848 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1850 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1852 // Keep a copy of the current input style.
1853 InputStyle currentInputStyle;
1854 currentInputStyle.Copy( mEventData->mInputStyle );
1856 // Set as input style the style of the first deleted character.
1857 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1859 // Compare if the input style has changed.
1860 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1862 if( hasInputStyleChanged )
1864 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1865 // Queue the input style changed signal.
1866 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1869 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1871 // Mark the paragraphs to be updated.
1872 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1874 mTextUpdateInfo.mCharacterIndex = 0;
1875 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1876 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1877 mTextUpdateInfo.mClearAll = true;
1881 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1882 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1885 // Delete text between handles
1886 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1887 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1888 utf32Characters.Erase( first, last );
1890 // Will show the cursor at the first character of the selection.
1891 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1895 // Will show the cursor at the last character of the selection.
1896 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1899 mEventData->mDecoratorUpdated = true;
1903 void Controller::Impl::ShowClipboard()
1907 mClipboard.ShowClipboard();
1911 void Controller::Impl::HideClipboard()
1913 if( mClipboard && mClipboardHideEnabled )
1915 mClipboard.HideClipboard();
1919 void Controller::Impl::SetClipboardHideEnable(bool enable)
1921 mClipboardHideEnabled = enable;
1924 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1926 //Send string to clipboard
1927 return ( mClipboard && mClipboard.SetItem( source ) );
1930 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1932 std::string selectedText;
1933 RetrieveSelection( selectedText, deleteAfterSending );
1934 CopyStringToClipboard( selectedText );
1935 ChangeState( EventData::EDITING );
1938 void Controller::Impl::RequestGetTextFromClipboard()
1942 mClipboard.RequestItem();
1946 void Controller::Impl::RepositionSelectionHandles()
1948 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1949 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1951 if( selectionStart == selectionEnd )
1953 // Nothing to select if handles are in the same place.
1957 mEventData->mDecorator->ClearHighlights();
1959 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1960 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1961 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1962 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1963 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1964 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1965 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1967 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1968 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1969 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1971 // Swap the indices if the start is greater than the end.
1972 const bool indicesSwapped = selectionStart > selectionEnd;
1974 // Tell the decorator to flip the selection handles if needed.
1975 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1977 if( indicesSwapped )
1979 std::swap( selectionStart, selectionEnd );
1982 // Get the indices to the first and last selected glyphs.
1983 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1984 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1985 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1986 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1988 // Get the lines where the glyphs are laid-out.
1989 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1991 LineIndex lineIndex = 0u;
1992 Length numberOfLines = 0u;
1993 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1994 1u + glyphEnd - glyphStart,
1997 const LineIndex firstLineIndex = lineIndex;
1999 // Create the structure to store some selection box info.
2000 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2001 selectionBoxLinesInfo.Resize( numberOfLines );
2003 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2004 selectionBoxInfo->minX = MAX_FLOAT;
2005 selectionBoxInfo->maxX = MIN_FLOAT;
2007 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2008 float minHighlightX = std::numeric_limits<float>::max();
2009 float maxHighlightX = std::numeric_limits<float>::min();
2011 Vector2 highLightPosition; // The highlight position in decorator's coords.
2013 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2015 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2016 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2019 // Transform to decorator's (control) coords.
2020 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2022 lineRun += firstLineIndex;
2024 // The line height is the addition of the line ascender and the line descender.
2025 // However, the line descender has a negative value, hence the subtraction.
2026 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2028 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2030 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2031 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2032 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2034 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2035 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2036 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2038 // The number of quads of the selection box.
2039 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2040 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2042 // Count the actual number of quads.
2043 unsigned int actualNumberOfQuads = 0u;
2046 // Traverse the glyphs.
2047 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2049 const GlyphInfo& glyph = *( glyphsBuffer + index );
2050 const Vector2& position = *( positionsBuffer + index );
2052 if( splitStartGlyph )
2054 // 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.
2056 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2057 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2058 // Get the direction of the character.
2059 CharacterDirection isCurrentRightToLeft = false;
2060 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2062 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2065 // The end point could be in the middle of the ligature.
2066 // Calculate the number of characters selected.
2067 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2069 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2070 quad.y = selectionBoxInfo->lineOffset;
2071 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2072 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2074 // Store the min and max 'x' for each line.
2075 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2076 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2078 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2079 ++actualNumberOfQuads;
2081 splitStartGlyph = false;
2085 if( splitEndGlyph && ( index == glyphEnd ) )
2087 // 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.
2089 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2090 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2091 // Get the direction of the character.
2092 CharacterDirection isCurrentRightToLeft = false;
2093 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2095 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2098 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2100 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2101 quad.y = selectionBoxInfo->lineOffset;
2102 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2103 quad.w = quad.y + selectionBoxInfo->lineHeight;
2105 // Store the min and max 'x' for each line.
2106 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2107 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2109 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2111 ++actualNumberOfQuads;
2113 splitEndGlyph = false;
2117 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2118 quad.y = selectionBoxInfo->lineOffset;
2119 quad.z = quad.x + glyph.advance;
2120 quad.w = quad.y + selectionBoxInfo->lineHeight;
2122 // Store the min and max 'x' for each line.
2123 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2124 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2126 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2128 ++actualNumberOfQuads;
2130 // Whether to retrieve the next line.
2131 if( index == lastGlyphOfLine )
2134 if( lineIndex < firstLineIndex + numberOfLines )
2136 // Retrieve the next line.
2139 // Get the last glyph of the new line.
2140 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2142 // Keep the offset and height of the current selection box.
2143 const float currentLineOffset = selectionBoxInfo->lineOffset;
2144 const float currentLineHeight = selectionBoxInfo->lineHeight;
2146 // Get the selection box info for the next line.
2149 selectionBoxInfo->minX = MAX_FLOAT;
2150 selectionBoxInfo->maxX = MIN_FLOAT;
2152 // Update the line's vertical offset.
2153 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2155 // The line height is the addition of the line ascender and the line descender.
2156 // However, the line descender has a negative value, hence the subtraction.
2157 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2162 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2163 // The final width is calculated after 'boxifying' the selection.
2164 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2165 endIt = selectionBoxLinesInfo.End();
2169 const SelectionBoxInfo& info = *it;
2171 // Update the size of the highlighted text.
2172 highLightSize.height += info.lineHeight;
2173 minHighlightX = std::min( minHighlightX, info.minX );
2174 maxHighlightX = std::max( maxHighlightX, info.maxX );
2177 // Add extra geometry to 'boxify' the selection.
2179 if( 1u < numberOfLines )
2181 // Boxify the first line.
2182 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2183 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2185 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2186 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2191 quad.y = firstSelectionBoxLineInfo.lineOffset;
2192 quad.z = firstSelectionBoxLineInfo.minX;
2193 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2195 // Boxify at the beginning of the line.
2196 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2198 ++actualNumberOfQuads;
2200 // Update the size of the highlighted text.
2201 minHighlightX = 0.f;
2206 quad.x = firstSelectionBoxLineInfo.maxX;
2207 quad.y = firstSelectionBoxLineInfo.lineOffset;
2208 quad.z = mModel->mVisualModel->mControlSize.width;
2209 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2211 // Boxify at the end of the line.
2212 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2214 ++actualNumberOfQuads;
2216 // Update the size of the highlighted text.
2217 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2220 // Boxify the central lines.
2221 if( 2u < numberOfLines )
2223 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2224 endIt = selectionBoxLinesInfo.End() - 1u;
2228 const SelectionBoxInfo& info = *it;
2231 quad.y = info.lineOffset;
2233 quad.w = info.lineOffset + info.lineHeight;
2235 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2237 ++actualNumberOfQuads;
2240 quad.y = info.lineOffset;
2241 quad.z = mModel->mVisualModel->mControlSize.width;
2242 quad.w = info.lineOffset + info.lineHeight;
2244 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2246 ++actualNumberOfQuads;
2249 // Update the size of the highlighted text.
2250 minHighlightX = 0.f;
2251 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2254 // Boxify the last line.
2255 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2256 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2258 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2259 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2264 quad.y = lastSelectionBoxLineInfo.lineOffset;
2265 quad.z = lastSelectionBoxLineInfo.minX;
2266 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2268 // Boxify at the beginning of the line.
2269 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2271 ++actualNumberOfQuads;
2273 // Update the size of the highlighted text.
2274 minHighlightX = 0.f;
2279 quad.x = lastSelectionBoxLineInfo.maxX;
2280 quad.y = lastSelectionBoxLineInfo.lineOffset;
2281 quad.z = mModel->mVisualModel->mControlSize.width;
2282 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2284 // Boxify at the end of the line.
2285 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2287 ++actualNumberOfQuads;
2289 // Update the size of the highlighted text.
2290 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2294 // Set the actual number of quads.
2295 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2297 // Sets the highlight's size and position. In decorator's coords.
2298 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2299 highLightSize.width = maxHighlightX - minHighlightX;
2301 highLightPosition.x = minHighlightX;
2302 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2303 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2305 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2307 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2309 CursorInfo primaryCursorInfo;
2310 GetCursorPosition( mEventData->mLeftSelectionPosition,
2311 primaryCursorInfo );
2313 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2315 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2317 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2318 primaryCursorInfo.lineHeight );
2320 CursorInfo secondaryCursorInfo;
2321 GetCursorPosition( mEventData->mRightSelectionPosition,
2322 secondaryCursorInfo );
2324 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2326 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2327 secondaryPosition.x,
2328 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2329 secondaryCursorInfo.lineHeight );
2332 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2333 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2335 // Set the flag to update the decorator.
2336 mEventData->mDecoratorUpdated = true;
2339 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2341 if( NULL == mEventData )
2343 // Nothing to do if there is no text input.
2347 if( IsShowingPlaceholderText() )
2349 // Nothing to do if there is the place-holder text.
2353 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2354 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2355 if( ( 0 == numberOfGlyphs ) ||
2356 ( 0 == numberOfLines ) )
2358 // Nothing to do if there is no text.
2362 // Find which word was selected
2363 CharacterIndex selectionStart( 0 );
2364 CharacterIndex selectionEnd( 0 );
2365 CharacterIndex noTextHitIndex( 0 );
2366 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2367 mModel->mLogicalModel,
2374 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2376 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2378 ChangeState( EventData::SELECTING );
2380 mEventData->mLeftSelectionPosition = selectionStart;
2381 mEventData->mRightSelectionPosition = selectionEnd;
2383 mEventData->mUpdateLeftSelectionPosition = true;
2384 mEventData->mUpdateRightSelectionPosition = true;
2385 mEventData->mUpdateHighlightBox = true;
2387 // It may happen an IMF commit event arrives before the selection event
2388 // if the IMF manager is in pre-edit state. The commit event will set the
2389 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2390 // to false, the highlight box won't be updated.
2391 mEventData->mUpdateCursorPosition = false;
2393 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2395 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2397 // Nothing to select. i.e. a white space, out of bounds
2398 ChangeState( EventData::EDITING_WITH_POPUP );
2400 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2402 mEventData->mUpdateCursorPosition = true;
2403 mEventData->mUpdateGrabHandlePosition = true;
2404 mEventData->mScrollAfterUpdatePosition = true;
2405 mEventData->mUpdateInputStyle = true;
2407 else if( Controller::NoTextTap::NO_ACTION == action )
2409 // Nothing to select. i.e. a white space, out of bounds
2410 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2412 mEventData->mUpdateCursorPosition = true;
2413 mEventData->mUpdateGrabHandlePosition = true;
2414 mEventData->mScrollAfterUpdatePosition = true;
2415 mEventData->mUpdateInputStyle = true;
2419 void Controller::Impl::SetPopupButtons()
2422 * Sets the Popup buttons to be shown depending on State.
2424 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2426 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2429 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2431 if( EventData::SELECTING == mEventData->mState )
2433 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2435 if( !IsClipboardEmpty() )
2437 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2438 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2441 if( !mEventData->mAllTextSelected )
2443 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2446 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2448 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2450 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2453 if( !IsClipboardEmpty() )
2455 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2456 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2459 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2461 if ( !IsClipboardEmpty() )
2463 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2464 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2468 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2471 void Controller::Impl::ChangeState( EventData::State newState )
2473 if( NULL == mEventData )
2475 // Nothing to do if there is no text input.
2479 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2481 if( mEventData->mState != newState )
2483 mEventData->mPreviousState = mEventData->mState;
2484 mEventData->mState = newState;
2486 switch( mEventData->mState )
2488 case EventData::INACTIVE:
2490 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2491 mEventData->mDecorator->StopCursorBlink();
2492 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2493 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2494 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2495 mEventData->mDecorator->SetHighlightActive( false );
2496 mEventData->mDecorator->SetPopupActive( false );
2497 mEventData->mDecoratorUpdated = true;
2500 case EventData::INTERRUPTED:
2502 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2503 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2504 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2505 mEventData->mDecorator->SetHighlightActive( false );
2506 mEventData->mDecorator->SetPopupActive( false );
2507 mEventData->mDecoratorUpdated = true;
2510 case EventData::SELECTING:
2512 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2513 mEventData->mDecorator->StopCursorBlink();
2514 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2515 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2516 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2517 mEventData->mDecorator->SetHighlightActive( true );
2518 if( mEventData->mGrabHandlePopupEnabled )
2521 mEventData->mDecorator->SetPopupActive( true );
2523 mEventData->mDecoratorUpdated = true;
2526 case EventData::EDITING:
2528 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2529 if( mEventData->mCursorBlinkEnabled )
2531 mEventData->mDecorator->StartCursorBlink();
2533 // Grab handle is not shown until a tap is received whilst EDITING
2534 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2535 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2536 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2537 mEventData->mDecorator->SetHighlightActive( false );
2538 if( mEventData->mGrabHandlePopupEnabled )
2540 mEventData->mDecorator->SetPopupActive( false );
2542 mEventData->mDecoratorUpdated = true;
2545 case EventData::EDITING_WITH_POPUP:
2547 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2549 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2550 if( mEventData->mCursorBlinkEnabled )
2552 mEventData->mDecorator->StartCursorBlink();
2554 if( mEventData->mSelectionEnabled )
2556 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2557 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2558 mEventData->mDecorator->SetHighlightActive( false );
2562 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2564 if( mEventData->mGrabHandlePopupEnabled )
2567 mEventData->mDecorator->SetPopupActive( true );
2569 mEventData->mDecoratorUpdated = true;
2572 case EventData::EDITING_WITH_GRAB_HANDLE:
2574 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2576 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2577 if( mEventData->mCursorBlinkEnabled )
2579 mEventData->mDecorator->StartCursorBlink();
2581 // Grab handle is not shown until a tap is received whilst EDITING
2582 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2583 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2584 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2585 mEventData->mDecorator->SetHighlightActive( false );
2586 if( mEventData->mGrabHandlePopupEnabled )
2588 mEventData->mDecorator->SetPopupActive( false );
2590 mEventData->mDecoratorUpdated = true;
2593 case EventData::SELECTION_HANDLE_PANNING:
2595 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2596 mEventData->mDecorator->StopCursorBlink();
2597 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2598 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2599 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2600 mEventData->mDecorator->SetHighlightActive( true );
2601 if( mEventData->mGrabHandlePopupEnabled )
2603 mEventData->mDecorator->SetPopupActive( false );
2605 mEventData->mDecoratorUpdated = true;
2608 case EventData::GRAB_HANDLE_PANNING:
2610 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2612 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2613 if( mEventData->mCursorBlinkEnabled )
2615 mEventData->mDecorator->StartCursorBlink();
2617 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2618 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2619 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2620 mEventData->mDecorator->SetHighlightActive( false );
2621 if( mEventData->mGrabHandlePopupEnabled )
2623 mEventData->mDecorator->SetPopupActive( false );
2625 mEventData->mDecoratorUpdated = true;
2628 case EventData::EDITING_WITH_PASTE_POPUP:
2630 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2632 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2633 if( mEventData->mCursorBlinkEnabled )
2635 mEventData->mDecorator->StartCursorBlink();
2638 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2639 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2640 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2641 mEventData->mDecorator->SetHighlightActive( false );
2643 if( mEventData->mGrabHandlePopupEnabled )
2646 mEventData->mDecorator->SetPopupActive( true );
2648 mEventData->mDecoratorUpdated = true;
2651 case EventData::TEXT_PANNING:
2653 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2654 mEventData->mDecorator->StopCursorBlink();
2655 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2656 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2657 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2659 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2660 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2661 mEventData->mDecorator->SetHighlightActive( true );
2664 if( mEventData->mGrabHandlePopupEnabled )
2666 mEventData->mDecorator->SetPopupActive( false );
2669 mEventData->mDecoratorUpdated = true;
2676 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2677 CursorInfo& cursorInfo )
2679 if( !IsShowingRealText() )
2681 // Do not want to use the place-holder text to set the cursor position.
2683 // Use the line's height of the font's family set to set the cursor's size.
2684 // If there is no font's family set, use the default font.
2685 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2687 cursorInfo.lineOffset = 0.f;
2688 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2689 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2691 switch( mModel->mHorizontalAlignment )
2693 case Text::HorizontalAlignment::BEGIN :
2695 cursorInfo.primaryPosition.x = 0.f;
2698 case Text::HorizontalAlignment::CENTER:
2700 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2703 case Text::HorizontalAlignment::END:
2705 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2710 // Nothing else to do.
2714 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2715 GetCursorPositionParameters parameters;
2716 parameters.visualModel = mModel->mVisualModel;
2717 parameters.logicalModel = mModel->mLogicalModel;
2718 parameters.metrics = mMetrics;
2719 parameters.logical = logical;
2720 parameters.isMultiline = isMultiLine;
2722 Text::GetCursorPosition( parameters,
2727 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2729 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2730 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2732 if( 0.f > cursorInfo.primaryPosition.x )
2734 cursorInfo.primaryPosition.x = 0.f;
2737 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2738 if( cursorInfo.primaryPosition.x > edgeWidth )
2740 cursorInfo.primaryPosition.x = edgeWidth;
2745 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2747 if( NULL == mEventData )
2749 // Nothing to do if there is no text input.
2753 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2755 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2756 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2758 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2759 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2761 if( numberOfCharacters > 1u )
2763 const Script script = mModel->mLogicalModel->GetScript( index );
2764 if( HasLigatureMustBreak( script ) )
2766 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2767 numberOfCharacters = 1u;
2772 while( 0u == numberOfCharacters )
2775 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2779 if( index < mEventData->mPrimaryCursorPosition )
2781 cursorIndex -= numberOfCharacters;
2785 cursorIndex += numberOfCharacters;
2788 // Will update the cursor hook position.
2789 mEventData->mUpdateCursorHookPosition = true;
2794 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2796 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2797 if( NULL == mEventData )
2799 // Nothing to do if there is no text input.
2800 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2804 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2806 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2808 // Sets the cursor position.
2809 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2812 cursorInfo.primaryCursorHeight,
2813 cursorInfo.lineHeight );
2814 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2816 if( mEventData->mUpdateGrabHandlePosition )
2818 // Sets the grab handle position.
2819 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2821 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2822 cursorInfo.lineHeight );
2825 if( cursorInfo.isSecondaryCursor )
2827 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2828 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2829 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2830 cursorInfo.secondaryCursorHeight,
2831 cursorInfo.lineHeight );
2832 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2835 // Set which cursors are active according the state.
2836 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2838 if( cursorInfo.isSecondaryCursor )
2840 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2844 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2849 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2852 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2855 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2856 const CursorInfo& cursorInfo )
2858 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2859 ( RIGHT_SELECTION_HANDLE != handleType ) )
2864 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2866 // Sets the handle's position.
2867 mEventData->mDecorator->SetPosition( handleType,
2869 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2870 cursorInfo.lineHeight );
2872 // If selection handle at start of the text and other at end of the text then all text is selected.
2873 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2874 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2875 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2878 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2880 // Clamp between -space & -alignment offset.
2882 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2884 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2885 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2886 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2888 mEventData->mDecoratorUpdated = true;
2892 mModel->mScrollPosition.x = 0.f;
2896 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2898 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2900 // Nothing to do if the text is single line.
2904 // Clamp between -space & 0.
2905 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2907 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2908 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2909 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2911 mEventData->mDecoratorUpdated = true;
2915 mModel->mScrollPosition.y = 0.f;
2919 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2921 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2923 // position is in actor's coords.
2924 const float positionEndX = position.x + cursorWidth;
2925 const float positionEndY = position.y + lineHeight;
2927 // Transform the position to decorator coords.
2928 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2929 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2931 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2932 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2934 if( decoratorPositionBeginX < 0.f )
2936 mModel->mScrollPosition.x = -position.x;
2938 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2940 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2943 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2945 if( decoratorPositionBeginY < 0.f )
2947 mModel->mScrollPosition.y = -position.y;
2949 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2951 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2956 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2958 // Get the current cursor position in decorator coords.
2959 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2961 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2965 // Calculate the offset to match the cursor position before the character was deleted.
2966 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2968 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2969 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2971 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2972 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2976 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2977 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2979 // Makes the new cursor position visible if needed.
2980 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2983 void Controller::Impl::RequestRelayout()
2985 if( NULL != mControlInterface )
2987 mControlInterface->RequestTextRelayout();
2993 } // namespace Toolkit