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() && ( 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;
1166 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1168 if( mEventData->mPrimaryCursorPosition > 0u )
1170 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1173 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1175 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1177 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1180 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1182 // Get first the line index of the current cursor position index.
1183 CharacterIndex characterIndex = 0u;
1185 if( mEventData->mPrimaryCursorPosition > 0u )
1187 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1190 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1191 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1193 // Retrieve the cursor position info.
1194 CursorInfo cursorInfo;
1195 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1198 // Get the line above.
1199 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1201 // Get the next hit 'y' point.
1202 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1204 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1205 bool matchedCharacter = false;
1206 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1207 mModel->mLogicalModel,
1209 mEventData->mCursorHookPositionX,
1211 CharacterHitTest::TAP,
1214 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1216 // Get first the line index of the current cursor position index.
1217 CharacterIndex characterIndex = 0u;
1219 if( mEventData->mPrimaryCursorPosition > 0u )
1221 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1224 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1226 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1228 // Retrieve the cursor position info.
1229 CursorInfo cursorInfo;
1230 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1233 // Get the line below.
1234 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1236 // Get the next hit 'y' point.
1237 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1239 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1240 bool matchedCharacter = false;
1241 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1242 mModel->mLogicalModel,
1244 mEventData->mCursorHookPositionX,
1246 CharacterHitTest::TAP,
1251 mEventData->mUpdateCursorPosition = true;
1252 mEventData->mUpdateInputStyle = true;
1253 mEventData->mScrollAfterUpdatePosition = true;
1256 void Controller::Impl::OnTapEvent( const Event& event )
1258 if( NULL != mEventData )
1260 const unsigned int tapCount = event.p1.mUint;
1262 if( 1u == tapCount )
1264 if( IsShowingRealText() )
1266 // Convert from control's coords to text's coords.
1267 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1268 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1270 // Keep the tap 'x' position. Used to move the cursor.
1271 mEventData->mCursorHookPositionX = xPosition;
1273 // Whether to touch point hits on a glyph.
1274 bool matchedCharacter = false;
1275 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1276 mModel->mLogicalModel,
1280 CharacterHitTest::TAP,
1283 // When the cursor position is changing, delay cursor blinking
1284 mEventData->mDecorator->DelayCursorBlink();
1288 mEventData->mPrimaryCursorPosition = 0u;
1291 mEventData->mUpdateCursorPosition = true;
1292 mEventData->mUpdateGrabHandlePosition = true;
1293 mEventData->mScrollAfterUpdatePosition = true;
1294 mEventData->mUpdateInputStyle = true;
1296 // Notify the cursor position to the imf manager.
1297 if( mEventData->mImfManager )
1299 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1300 mEventData->mImfManager.NotifyCursorPosition();
1303 else if( 2u == tapCount )
1305 if( mEventData->mSelectionEnabled )
1307 // Convert from control's coords to text's coords.
1308 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1309 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1311 // Calculates the logical position from the x,y coords.
1312 RepositionSelectionHandles( xPosition,
1314 mEventData->mDoubleTapAction );
1320 void Controller::Impl::OnPanEvent( const Event& event )
1322 if( NULL == mEventData )
1324 // Nothing to do if there is no text input.
1328 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1329 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1331 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1333 // Nothing to do if scrolling is not enabled.
1337 const int state = event.p1.mInt;
1341 case Gesture::Started:
1343 // Will remove the cursor, handles or text's popup, ...
1344 ChangeState( EventData::TEXT_PANNING );
1347 case Gesture::Continuing:
1349 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1350 const Vector2 currentScroll = mModel->mScrollPosition;
1352 if( isHorizontalScrollEnabled )
1354 const float displacementX = event.p2.mFloat;
1355 mModel->mScrollPosition.x += displacementX;
1357 ClampHorizontalScroll( layoutSize );
1360 if( isVerticalScrollEnabled )
1362 const float displacementY = event.p3.mFloat;
1363 mModel->mScrollPosition.y += displacementY;
1365 ClampVerticalScroll( layoutSize );
1368 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1371 case Gesture::Finished:
1372 case Gesture::Cancelled: // FALLTHROUGH
1374 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1375 ChangeState( mEventData->mPreviousState );
1383 void Controller::Impl::OnLongPressEvent( const Event& event )
1385 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1387 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1389 ChangeState( EventData::EDITING_WITH_POPUP );
1390 mEventData->mDecoratorUpdated = true;
1391 mEventData->mUpdateInputStyle = true;
1395 if( mEventData->mSelectionEnabled )
1397 // Convert from control's coords to text's coords.
1398 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1399 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1401 // Calculates the logical position from the x,y coords.
1402 RepositionSelectionHandles( xPosition,
1404 mEventData->mLongPressAction );
1409 void Controller::Impl::OnHandleEvent( const Event& event )
1411 if( NULL == mEventData )
1413 // Nothing to do if there is no text input.
1417 const unsigned int state = event.p1.mUint;
1418 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1419 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1421 if( HANDLE_PRESSED == state )
1423 // Convert from decorator's coords to text's coords.
1424 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1425 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1427 // Need to calculate the handle's new position.
1428 bool matchedCharacter = false;
1429 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1430 mModel->mLogicalModel,
1434 CharacterHitTest::SCROLL,
1437 if( Event::GRAB_HANDLE_EVENT == event.type )
1439 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1441 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1443 // Updates the cursor position if the handle's new position is different than the current one.
1444 mEventData->mUpdateCursorPosition = true;
1445 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1446 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1447 mEventData->mPrimaryCursorPosition = handleNewPosition;
1450 // 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.
1451 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1453 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1455 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1457 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1458 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1460 // Updates the highlight box if the handle's new position is different than the current one.
1461 mEventData->mUpdateHighlightBox = true;
1462 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1463 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1464 mEventData->mLeftSelectionPosition = handleNewPosition;
1467 // 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.
1468 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1470 // Will define the order to scroll the text to match the handle position.
1471 mEventData->mIsLeftHandleSelected = true;
1472 mEventData->mIsRightHandleSelected = false;
1474 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1476 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1478 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1479 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1481 // Updates the highlight box if the handle's new position is different than the current one.
1482 mEventData->mUpdateHighlightBox = true;
1483 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1484 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1485 mEventData->mRightSelectionPosition = handleNewPosition;
1488 // 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.
1489 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1491 // Will define the order to scroll the text to match the handle position.
1492 mEventData->mIsLeftHandleSelected = false;
1493 mEventData->mIsRightHandleSelected = true;
1495 } // end ( HANDLE_PRESSED == state )
1496 else if( ( HANDLE_RELEASED == state ) ||
1497 handleStopScrolling )
1499 CharacterIndex handlePosition = 0u;
1500 if( handleStopScrolling || isSmoothHandlePanEnabled )
1502 // Convert from decorator's coords to text's coords.
1503 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1504 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1506 bool matchedCharacter = false;
1507 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1508 mModel->mLogicalModel,
1512 CharacterHitTest::SCROLL,
1516 if( Event::GRAB_HANDLE_EVENT == event.type )
1518 mEventData->mUpdateCursorPosition = true;
1519 mEventData->mUpdateGrabHandlePosition = true;
1520 mEventData->mUpdateInputStyle = true;
1522 if( !IsClipboardEmpty() )
1524 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1527 if( handleStopScrolling || isSmoothHandlePanEnabled )
1529 mEventData->mScrollAfterUpdatePosition = true;
1530 mEventData->mPrimaryCursorPosition = handlePosition;
1533 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1535 ChangeState( EventData::SELECTING );
1537 mEventData->mUpdateHighlightBox = true;
1538 mEventData->mUpdateLeftSelectionPosition = true;
1539 mEventData->mUpdateRightSelectionPosition = true;
1541 if( handleStopScrolling || isSmoothHandlePanEnabled )
1543 mEventData->mScrollAfterUpdatePosition = true;
1545 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1546 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1548 mEventData->mLeftSelectionPosition = handlePosition;
1552 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1554 ChangeState( EventData::SELECTING );
1556 mEventData->mUpdateHighlightBox = true;
1557 mEventData->mUpdateRightSelectionPosition = true;
1558 mEventData->mUpdateLeftSelectionPosition = true;
1560 if( handleStopScrolling || isSmoothHandlePanEnabled )
1562 mEventData->mScrollAfterUpdatePosition = true;
1563 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1564 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1566 mEventData->mRightSelectionPosition = handlePosition;
1571 mEventData->mDecoratorUpdated = true;
1572 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1573 else if( HANDLE_SCROLLING == state )
1575 const float xSpeed = event.p2.mFloat;
1576 const float ySpeed = event.p3.mFloat;
1577 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1578 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1580 mModel->mScrollPosition.x += xSpeed;
1581 mModel->mScrollPosition.y += ySpeed;
1583 ClampHorizontalScroll( layoutSize );
1584 ClampVerticalScroll( layoutSize );
1586 bool endOfScroll = false;
1587 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1589 // Notify the decorator there is no more text to scroll.
1590 // The decorator won't send more scroll events.
1591 mEventData->mDecorator->NotifyEndOfScroll();
1592 // Still need to set the position of the handle.
1596 // Set the position of the handle.
1597 const bool scrollRightDirection = xSpeed > 0.f;
1598 const bool scrollBottomDirection = ySpeed > 0.f;
1599 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1600 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1602 if( Event::GRAB_HANDLE_EVENT == event.type )
1604 ChangeState( EventData::GRAB_HANDLE_PANNING );
1606 // Get the grab handle position in decorator coords.
1607 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1609 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1611 // Position the grag handle close to either the left or right edge.
1612 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1615 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1617 position.x = mEventData->mCursorHookPositionX;
1619 // Position the grag handle close to either the top or bottom edge.
1620 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1623 // Get the new handle position.
1624 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1625 bool matchedCharacter = false;
1626 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1627 mModel->mLogicalModel,
1629 position.x - mModel->mScrollPosition.x,
1630 position.y - mModel->mScrollPosition.y,
1631 CharacterHitTest::SCROLL,
1634 if( mEventData->mPrimaryCursorPosition != handlePosition )
1636 mEventData->mUpdateCursorPosition = true;
1637 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1638 mEventData->mScrollAfterUpdatePosition = true;
1639 mEventData->mPrimaryCursorPosition = handlePosition;
1641 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1643 // Updates the decorator if the soft handle panning is enabled.
1644 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1646 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1648 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1650 // Get the selection handle position in decorator coords.
1651 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1653 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1655 // Position the selection handle close to either the left or right edge.
1656 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1659 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1661 position.x = mEventData->mCursorHookPositionX;
1663 // Position the grag handle close to either the top or bottom edge.
1664 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1667 // Get the new handle position.
1668 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1669 bool matchedCharacter = false;
1670 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1671 mModel->mLogicalModel,
1673 position.x - mModel->mScrollPosition.x,
1674 position.y - mModel->mScrollPosition.y,
1675 CharacterHitTest::SCROLL,
1678 if( leftSelectionHandleEvent )
1680 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1682 if( differentHandles || endOfScroll )
1684 mEventData->mUpdateHighlightBox = true;
1685 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1686 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1687 mEventData->mLeftSelectionPosition = handlePosition;
1692 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1693 if( differentHandles || endOfScroll )
1695 mEventData->mUpdateHighlightBox = true;
1696 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1697 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1698 mEventData->mRightSelectionPosition = handlePosition;
1702 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1704 RepositionSelectionHandles();
1706 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1709 mEventData->mDecoratorUpdated = true;
1710 } // end ( HANDLE_SCROLLING == state )
1713 void Controller::Impl::OnSelectEvent( const Event& event )
1715 if( NULL == mEventData )
1717 // Nothing to do if there is no text.
1721 if( mEventData->mSelectionEnabled )
1723 // Convert from control's coords to text's coords.
1724 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1725 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1727 // Calculates the logical position from the x,y coords.
1728 RepositionSelectionHandles( xPosition,
1730 Controller::NoTextTap::HIGHLIGHT );
1734 void Controller::Impl::OnSelectAllEvent()
1736 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1738 if( NULL == mEventData )
1740 // Nothing to do if there is no text.
1744 if( mEventData->mSelectionEnabled )
1746 ChangeState( EventData::SELECTING );
1748 mEventData->mLeftSelectionPosition = 0u;
1749 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1751 mEventData->mScrollAfterUpdatePosition = true;
1752 mEventData->mUpdateLeftSelectionPosition = true;
1753 mEventData->mUpdateRightSelectionPosition = true;
1754 mEventData->mUpdateHighlightBox = true;
1758 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1760 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1762 // Nothing to select if handles are in the same place.
1763 selectedText.clear();
1767 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1769 //Get start and end position of selection
1770 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1771 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1773 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1774 const Length numberOfCharacters = utf32Characters.Count();
1776 // Validate the start and end selection points
1777 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1779 //Get text as a UTF8 string
1780 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1782 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1784 // Keep a copy of the current input style.
1785 InputStyle currentInputStyle;
1786 currentInputStyle.Copy( mEventData->mInputStyle );
1788 // Set as input style the style of the first deleted character.
1789 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1791 // Compare if the input style has changed.
1792 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1794 if( hasInputStyleChanged )
1796 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1797 // Queue the input style changed signal.
1798 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1801 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1803 // Mark the paragraphs to be updated.
1804 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1806 mTextUpdateInfo.mCharacterIndex = 0;
1807 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1808 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1809 mTextUpdateInfo.mClearAll = true;
1813 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1814 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1817 // Delete text between handles
1818 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1819 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1820 utf32Characters.Erase( first, last );
1822 // Will show the cursor at the first character of the selection.
1823 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1827 // Will show the cursor at the last character of the selection.
1828 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1831 mEventData->mDecoratorUpdated = true;
1835 void Controller::Impl::ShowClipboard()
1839 mClipboard.ShowClipboard();
1843 void Controller::Impl::HideClipboard()
1845 if( mClipboard && mClipboardHideEnabled )
1847 mClipboard.HideClipboard();
1851 void Controller::Impl::SetClipboardHideEnable(bool enable)
1853 mClipboardHideEnabled = enable;
1856 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1858 //Send string to clipboard
1859 return ( mClipboard && mClipboard.SetItem( source ) );
1862 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1864 std::string selectedText;
1865 RetrieveSelection( selectedText, deleteAfterSending );
1866 CopyStringToClipboard( selectedText );
1867 ChangeState( EventData::EDITING );
1870 void Controller::Impl::RequestGetTextFromClipboard()
1874 mClipboard.RequestItem();
1878 void Controller::Impl::RepositionSelectionHandles()
1880 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1881 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1883 if( selectionStart == selectionEnd )
1885 // Nothing to select if handles are in the same place.
1889 mEventData->mDecorator->ClearHighlights();
1891 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1892 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1893 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1894 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1895 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1896 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1897 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1899 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1900 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1901 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1903 // Swap the indices if the start is greater than the end.
1904 const bool indicesSwapped = selectionStart > selectionEnd;
1906 // Tell the decorator to flip the selection handles if needed.
1907 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1909 if( indicesSwapped )
1911 std::swap( selectionStart, selectionEnd );
1914 // Get the indices to the first and last selected glyphs.
1915 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1916 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1917 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1918 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1920 // Get the lines where the glyphs are laid-out.
1921 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1923 LineIndex lineIndex = 0u;
1924 Length numberOfLines = 0u;
1925 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1926 1u + glyphEnd - glyphStart,
1929 const LineIndex firstLineIndex = lineIndex;
1931 // Create the structure to store some selection box info.
1932 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1933 selectionBoxLinesInfo.Resize( numberOfLines );
1935 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1936 selectionBoxInfo->minX = MAX_FLOAT;
1937 selectionBoxInfo->maxX = MIN_FLOAT;
1939 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1940 float minHighlightX = std::numeric_limits<float>::max();
1941 float maxHighlightX = std::numeric_limits<float>::min();
1943 Vector2 highLightPosition; // The highlight position in decorator's coords.
1945 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1947 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1948 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1951 // Transform to decorator's (control) coords.
1952 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1954 lineRun += firstLineIndex;
1956 // The line height is the addition of the line ascender and the line descender.
1957 // However, the line descender has a negative value, hence the subtraction.
1958 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1960 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1962 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1963 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1964 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1966 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1967 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1968 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1970 // The number of quads of the selection box.
1971 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1972 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1974 // Count the actual number of quads.
1975 unsigned int actualNumberOfQuads = 0u;
1978 // Traverse the glyphs.
1979 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1981 const GlyphInfo& glyph = *( glyphsBuffer + index );
1982 const Vector2& position = *( positionsBuffer + index );
1984 if( splitStartGlyph )
1986 // 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.
1988 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1989 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1990 // Get the direction of the character.
1991 CharacterDirection isCurrentRightToLeft = false;
1992 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1994 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1997 // The end point could be in the middle of the ligature.
1998 // Calculate the number of characters selected.
1999 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2001 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2002 quad.y = selectionBoxInfo->lineOffset;
2003 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2004 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2006 // Store the min and max 'x' for each line.
2007 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2008 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2010 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2011 ++actualNumberOfQuads;
2013 splitStartGlyph = false;
2017 if( splitEndGlyph && ( index == glyphEnd ) )
2019 // 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.
2021 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2022 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2023 // Get the direction of the character.
2024 CharacterDirection isCurrentRightToLeft = false;
2025 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2027 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2030 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2032 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2033 quad.y = selectionBoxInfo->lineOffset;
2034 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2035 quad.w = quad.y + selectionBoxInfo->lineHeight;
2037 // Store the min and max 'x' for each line.
2038 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2039 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2041 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2043 ++actualNumberOfQuads;
2045 splitEndGlyph = false;
2049 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2050 quad.y = selectionBoxInfo->lineOffset;
2051 quad.z = quad.x + glyph.advance;
2052 quad.w = quad.y + selectionBoxInfo->lineHeight;
2054 // Store the min and max 'x' for each line.
2055 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2056 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2058 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2060 ++actualNumberOfQuads;
2062 // Whether to retrieve the next line.
2063 if( index == lastGlyphOfLine )
2066 if( lineIndex < firstLineIndex + numberOfLines )
2068 // Retrieve the next line.
2071 // Get the last glyph of the new line.
2072 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2074 // Keep the offset and height of the current selection box.
2075 const float currentLineOffset = selectionBoxInfo->lineOffset;
2076 const float currentLineHeight = selectionBoxInfo->lineHeight;
2078 // Get the selection box info for the next line.
2081 selectionBoxInfo->minX = MAX_FLOAT;
2082 selectionBoxInfo->maxX = MIN_FLOAT;
2084 // Update the line's vertical offset.
2085 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2087 // The line height is the addition of the line ascender and the line descender.
2088 // However, the line descender has a negative value, hence the subtraction.
2089 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2094 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2095 // The final width is calculated after 'boxifying' the selection.
2096 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2097 endIt = selectionBoxLinesInfo.End();
2101 const SelectionBoxInfo& info = *it;
2103 // Update the size of the highlighted text.
2104 highLightSize.height += info.lineHeight;
2105 minHighlightX = std::min( minHighlightX, info.minX );
2106 maxHighlightX = std::max( maxHighlightX, info.maxX );
2109 // Add extra geometry to 'boxify' the selection.
2111 if( 1u < numberOfLines )
2113 // Boxify the first line.
2114 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2115 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2117 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2118 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2123 quad.y = firstSelectionBoxLineInfo.lineOffset;
2124 quad.z = firstSelectionBoxLineInfo.minX;
2125 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2127 // Boxify at the beginning of the line.
2128 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2130 ++actualNumberOfQuads;
2132 // Update the size of the highlighted text.
2133 minHighlightX = 0.f;
2138 quad.x = firstSelectionBoxLineInfo.maxX;
2139 quad.y = firstSelectionBoxLineInfo.lineOffset;
2140 quad.z = mModel->mVisualModel->mControlSize.width;
2141 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2143 // Boxify at the end of the line.
2144 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2146 ++actualNumberOfQuads;
2148 // Update the size of the highlighted text.
2149 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2152 // Boxify the central lines.
2153 if( 2u < numberOfLines )
2155 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2156 endIt = selectionBoxLinesInfo.End() - 1u;
2160 const SelectionBoxInfo& info = *it;
2163 quad.y = info.lineOffset;
2165 quad.w = info.lineOffset + info.lineHeight;
2167 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2169 ++actualNumberOfQuads;
2172 quad.y = info.lineOffset;
2173 quad.z = mModel->mVisualModel->mControlSize.width;
2174 quad.w = info.lineOffset + info.lineHeight;
2176 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2178 ++actualNumberOfQuads;
2181 // Update the size of the highlighted text.
2182 minHighlightX = 0.f;
2183 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2186 // Boxify the last line.
2187 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2188 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2190 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2191 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2196 quad.y = lastSelectionBoxLineInfo.lineOffset;
2197 quad.z = lastSelectionBoxLineInfo.minX;
2198 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2200 // Boxify at the beginning of the line.
2201 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2203 ++actualNumberOfQuads;
2205 // Update the size of the highlighted text.
2206 minHighlightX = 0.f;
2211 quad.x = lastSelectionBoxLineInfo.maxX;
2212 quad.y = lastSelectionBoxLineInfo.lineOffset;
2213 quad.z = mModel->mVisualModel->mControlSize.width;
2214 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2216 // Boxify at the end of the line.
2217 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2219 ++actualNumberOfQuads;
2221 // Update the size of the highlighted text.
2222 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2226 // Set the actual number of quads.
2227 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2229 // Sets the highlight's size and position. In decorator's coords.
2230 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2231 highLightSize.width = maxHighlightX - minHighlightX;
2233 highLightPosition.x = minHighlightX;
2234 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2235 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2237 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2239 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2241 CursorInfo primaryCursorInfo;
2242 GetCursorPosition( mEventData->mLeftSelectionPosition,
2243 primaryCursorInfo );
2245 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2247 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2249 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2250 primaryCursorInfo.lineHeight );
2252 CursorInfo secondaryCursorInfo;
2253 GetCursorPosition( mEventData->mRightSelectionPosition,
2254 secondaryCursorInfo );
2256 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2258 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2259 secondaryPosition.x,
2260 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2261 secondaryCursorInfo.lineHeight );
2264 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2265 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2267 // Set the flag to update the decorator.
2268 mEventData->mDecoratorUpdated = true;
2271 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2273 if( NULL == mEventData )
2275 // Nothing to do if there is no text input.
2279 if( IsShowingPlaceholderText() )
2281 // Nothing to do if there is the place-holder text.
2285 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2286 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2287 if( ( 0 == numberOfGlyphs ) ||
2288 ( 0 == numberOfLines ) )
2290 // Nothing to do if there is no text.
2294 // Find which word was selected
2295 CharacterIndex selectionStart( 0 );
2296 CharacterIndex selectionEnd( 0 );
2297 CharacterIndex noTextHitIndex( 0 );
2298 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2299 mModel->mLogicalModel,
2306 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2308 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2310 ChangeState( EventData::SELECTING );
2312 mEventData->mLeftSelectionPosition = selectionStart;
2313 mEventData->mRightSelectionPosition = selectionEnd;
2315 mEventData->mUpdateLeftSelectionPosition = true;
2316 mEventData->mUpdateRightSelectionPosition = true;
2317 mEventData->mUpdateHighlightBox = true;
2319 // It may happen an IMF commit event arrives before the selection event
2320 // if the IMF manager is in pre-edit state. The commit event will set the
2321 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2322 // to false, the highlight box won't be updated.
2323 mEventData->mUpdateCursorPosition = false;
2325 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2327 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2329 // Nothing to select. i.e. a white space, out of bounds
2330 ChangeState( EventData::EDITING_WITH_POPUP );
2332 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2334 mEventData->mUpdateCursorPosition = true;
2335 mEventData->mUpdateGrabHandlePosition = true;
2336 mEventData->mScrollAfterUpdatePosition = true;
2337 mEventData->mUpdateInputStyle = true;
2339 else if( Controller::NoTextTap::NO_ACTION == action )
2341 // Nothing to select. i.e. a white space, out of bounds
2342 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2344 mEventData->mUpdateCursorPosition = true;
2345 mEventData->mUpdateGrabHandlePosition = true;
2346 mEventData->mScrollAfterUpdatePosition = true;
2347 mEventData->mUpdateInputStyle = true;
2351 void Controller::Impl::SetPopupButtons()
2354 * Sets the Popup buttons to be shown depending on State.
2356 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2358 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2361 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2363 if( EventData::SELECTING == mEventData->mState )
2365 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2367 if( !IsClipboardEmpty() )
2369 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2370 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2373 if( !mEventData->mAllTextSelected )
2375 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2378 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2380 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2382 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2385 if( !IsClipboardEmpty() )
2387 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2388 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2391 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2393 if ( !IsClipboardEmpty() )
2395 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2396 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2400 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2403 void Controller::Impl::ChangeState( EventData::State newState )
2405 if( NULL == mEventData )
2407 // Nothing to do if there is no text input.
2411 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2413 if( mEventData->mState != newState )
2415 mEventData->mPreviousState = mEventData->mState;
2416 mEventData->mState = newState;
2418 switch( mEventData->mState )
2420 case EventData::INACTIVE:
2422 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2423 mEventData->mDecorator->StopCursorBlink();
2424 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2425 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2426 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2427 mEventData->mDecorator->SetHighlightActive( false );
2428 mEventData->mDecorator->SetPopupActive( false );
2429 mEventData->mDecoratorUpdated = true;
2432 case EventData::INTERRUPTED:
2434 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2435 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2436 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2437 mEventData->mDecorator->SetHighlightActive( false );
2438 mEventData->mDecorator->SetPopupActive( false );
2439 mEventData->mDecoratorUpdated = true;
2442 case EventData::SELECTING:
2444 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2445 mEventData->mDecorator->StopCursorBlink();
2446 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2447 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2448 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2449 mEventData->mDecorator->SetHighlightActive( true );
2450 if( mEventData->mGrabHandlePopupEnabled )
2453 mEventData->mDecorator->SetPopupActive( true );
2455 mEventData->mDecoratorUpdated = true;
2458 case EventData::EDITING:
2460 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2461 if( mEventData->mCursorBlinkEnabled )
2463 mEventData->mDecorator->StartCursorBlink();
2465 // Grab handle is not shown until a tap is received whilst EDITING
2466 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2467 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2468 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2469 mEventData->mDecorator->SetHighlightActive( false );
2470 if( mEventData->mGrabHandlePopupEnabled )
2472 mEventData->mDecorator->SetPopupActive( false );
2474 mEventData->mDecoratorUpdated = true;
2477 case EventData::EDITING_WITH_POPUP:
2479 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2481 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2482 if( mEventData->mCursorBlinkEnabled )
2484 mEventData->mDecorator->StartCursorBlink();
2486 if( mEventData->mSelectionEnabled )
2488 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2489 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2490 mEventData->mDecorator->SetHighlightActive( false );
2494 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2496 if( mEventData->mGrabHandlePopupEnabled )
2499 mEventData->mDecorator->SetPopupActive( true );
2501 mEventData->mDecoratorUpdated = true;
2504 case EventData::EDITING_WITH_GRAB_HANDLE:
2506 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2508 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2509 if( mEventData->mCursorBlinkEnabled )
2511 mEventData->mDecorator->StartCursorBlink();
2513 // Grab handle is not shown until a tap is received whilst EDITING
2514 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2515 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2516 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2517 mEventData->mDecorator->SetHighlightActive( false );
2518 if( mEventData->mGrabHandlePopupEnabled )
2520 mEventData->mDecorator->SetPopupActive( false );
2522 mEventData->mDecoratorUpdated = true;
2525 case EventData::SELECTION_HANDLE_PANNING:
2527 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2528 mEventData->mDecorator->StopCursorBlink();
2529 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2530 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2531 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2532 mEventData->mDecorator->SetHighlightActive( true );
2533 if( mEventData->mGrabHandlePopupEnabled )
2535 mEventData->mDecorator->SetPopupActive( false );
2537 mEventData->mDecoratorUpdated = true;
2540 case EventData::GRAB_HANDLE_PANNING:
2542 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2544 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2545 if( mEventData->mCursorBlinkEnabled )
2547 mEventData->mDecorator->StartCursorBlink();
2549 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2550 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2551 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2552 mEventData->mDecorator->SetHighlightActive( false );
2553 if( mEventData->mGrabHandlePopupEnabled )
2555 mEventData->mDecorator->SetPopupActive( false );
2557 mEventData->mDecoratorUpdated = true;
2560 case EventData::EDITING_WITH_PASTE_POPUP:
2562 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2564 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2565 if( mEventData->mCursorBlinkEnabled )
2567 mEventData->mDecorator->StartCursorBlink();
2570 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2571 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2572 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2573 mEventData->mDecorator->SetHighlightActive( false );
2575 if( mEventData->mGrabHandlePopupEnabled )
2578 mEventData->mDecorator->SetPopupActive( true );
2580 mEventData->mDecoratorUpdated = true;
2583 case EventData::TEXT_PANNING:
2585 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2586 mEventData->mDecorator->StopCursorBlink();
2587 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2588 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2589 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2591 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2592 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2593 mEventData->mDecorator->SetHighlightActive( true );
2596 if( mEventData->mGrabHandlePopupEnabled )
2598 mEventData->mDecorator->SetPopupActive( false );
2601 mEventData->mDecoratorUpdated = true;
2608 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2609 CursorInfo& cursorInfo )
2611 if( !IsShowingRealText() )
2613 // Do not want to use the place-holder text to set the cursor position.
2615 // Use the line's height of the font's family set to set the cursor's size.
2616 // If there is no font's family set, use the default font.
2617 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2619 cursorInfo.lineOffset = 0.f;
2620 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2621 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2623 switch( mModel->mHorizontalAlignment )
2625 case Text::HorizontalAlignment::BEGIN :
2627 cursorInfo.primaryPosition.x = 0.f;
2630 case Text::HorizontalAlignment::CENTER:
2632 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2635 case Text::HorizontalAlignment::END:
2637 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2642 // Nothing else to do.
2646 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2647 GetCursorPositionParameters parameters;
2648 parameters.visualModel = mModel->mVisualModel;
2649 parameters.logicalModel = mModel->mLogicalModel;
2650 parameters.metrics = mMetrics;
2651 parameters.logical = logical;
2652 parameters.isMultiline = isMultiLine;
2654 Text::GetCursorPosition( parameters,
2659 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2661 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2662 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2664 if( 0.f > cursorInfo.primaryPosition.x )
2666 cursorInfo.primaryPosition.x = 0.f;
2669 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2670 if( cursorInfo.primaryPosition.x > edgeWidth )
2672 cursorInfo.primaryPosition.x = edgeWidth;
2677 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2679 if( NULL == mEventData )
2681 // Nothing to do if there is no text input.
2685 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2687 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2688 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2690 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2691 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2693 if( numberOfCharacters > 1u )
2695 const Script script = mModel->mLogicalModel->GetScript( index );
2696 if( HasLigatureMustBreak( script ) )
2698 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2699 numberOfCharacters = 1u;
2704 while( 0u == numberOfCharacters )
2707 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2711 if( index < mEventData->mPrimaryCursorPosition )
2713 cursorIndex -= numberOfCharacters;
2717 cursorIndex += numberOfCharacters;
2720 // Will update the cursor hook position.
2721 mEventData->mUpdateCursorHookPosition = true;
2726 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2728 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2729 if( NULL == mEventData )
2731 // Nothing to do if there is no text input.
2732 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2736 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2738 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2740 // Sets the cursor position.
2741 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2744 cursorInfo.primaryCursorHeight,
2745 cursorInfo.lineHeight );
2746 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2748 if( mEventData->mUpdateGrabHandlePosition )
2750 // Sets the grab handle position.
2751 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2753 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2754 cursorInfo.lineHeight );
2757 if( cursorInfo.isSecondaryCursor )
2759 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2760 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2761 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2762 cursorInfo.secondaryCursorHeight,
2763 cursorInfo.lineHeight );
2764 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2767 // Set which cursors are active according the state.
2768 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2770 if( cursorInfo.isSecondaryCursor )
2772 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2776 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2781 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2784 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2787 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2788 const CursorInfo& cursorInfo )
2790 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2791 ( RIGHT_SELECTION_HANDLE != handleType ) )
2796 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2798 // Sets the handle's position.
2799 mEventData->mDecorator->SetPosition( handleType,
2801 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2802 cursorInfo.lineHeight );
2804 // If selection handle at start of the text and other at end of the text then all text is selected.
2805 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2806 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2807 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2810 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2812 // Clamp between -space & -alignment offset.
2814 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2816 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2817 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2818 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2820 mEventData->mDecoratorUpdated = true;
2824 mModel->mScrollPosition.x = 0.f;
2828 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2830 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2832 // Nothing to do if the text is single line.
2836 // Clamp between -space & 0.
2837 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2839 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2840 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2841 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2843 mEventData->mDecoratorUpdated = true;
2847 mModel->mScrollPosition.y = 0.f;
2851 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2853 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2855 // position is in actor's coords.
2856 const float positionEndX = position.x + cursorWidth;
2857 const float positionEndY = position.y + lineHeight;
2859 // Transform the position to decorator coords.
2860 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2861 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2863 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2864 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2866 if( decoratorPositionBeginX < 0.f )
2868 mModel->mScrollPosition.x = -position.x;
2870 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2872 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2875 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2877 if( decoratorPositionBeginY < 0.f )
2879 mModel->mScrollPosition.y = -position.y;
2881 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2883 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2888 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2890 // Get the current cursor position in decorator coords.
2891 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2893 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2897 // Calculate the offset to match the cursor position before the character was deleted.
2898 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2900 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2901 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2903 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2904 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2908 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2909 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2911 // Makes the new cursor position visible if needed.
2912 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2915 void Controller::Impl::RequestRelayout()
2917 if( NULL != mControlInterface )
2919 mControlInterface->RequestTextRelayout();
2925 } // namespace Toolkit