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 ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
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 )
110 mImfManager = ImfManager::Get();
113 EventData::~EventData()
116 bool Controller::Impl::ProcessInputEvents()
118 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
119 if( NULL == mEventData )
121 // Nothing to do if there is no text input.
122 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
126 if( mEventData->mDecorator )
128 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
129 iter != mEventData->mEventQueue.end();
134 case Event::CURSOR_KEY_EVENT:
136 OnCursorKeyEvent( *iter );
139 case Event::TAP_EVENT:
144 case Event::LONG_PRESS_EVENT:
146 OnLongPressEvent( *iter );
149 case Event::PAN_EVENT:
154 case Event::GRAB_HANDLE_EVENT:
155 case Event::LEFT_SELECTION_HANDLE_EVENT:
156 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
158 OnHandleEvent( *iter );
163 OnSelectEvent( *iter );
166 case Event::SELECT_ALL:
175 if( mEventData->mUpdateCursorPosition ||
176 mEventData->mUpdateHighlightBox )
181 // The cursor must also be repositioned after inserts into the model
182 if( mEventData->mUpdateCursorPosition )
184 // Updates the cursor position and scrolls the text to make it visible.
185 CursorInfo cursorInfo;
186 // Calculate the cursor position from the new cursor index.
187 GetCursorPosition( mEventData->mPrimaryCursorPosition,
190 if( mEventData->mUpdateCursorHookPosition )
192 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
193 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
194 mEventData->mUpdateCursorHookPosition = false;
197 // Scroll first the text after delete ...
198 if( mEventData->mScrollAfterDelete )
200 ScrollTextToMatchCursor( cursorInfo );
203 // ... then, text can be scrolled to make the cursor visible.
204 if( mEventData->mScrollAfterUpdatePosition )
206 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
207 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
209 mEventData->mScrollAfterUpdatePosition = false;
210 mEventData->mScrollAfterDelete = false;
212 UpdateCursorPosition( cursorInfo );
214 mEventData->mDecoratorUpdated = true;
215 mEventData->mUpdateCursorPosition = false;
216 mEventData->mUpdateGrabHandlePosition = false;
220 CursorInfo leftHandleInfo;
221 CursorInfo rightHandleInfo;
223 if( mEventData->mUpdateHighlightBox )
225 GetCursorPosition( mEventData->mLeftSelectionPosition,
228 GetCursorPosition( mEventData->mRightSelectionPosition,
231 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
233 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
235 CursorInfo& infoLeft = leftHandleInfo;
237 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
238 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
240 CursorInfo& infoRight = rightHandleInfo;
242 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
243 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
247 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
249 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
250 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
255 if( mEventData->mUpdateLeftSelectionPosition )
257 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
261 mEventData->mDecoratorUpdated = true;
262 mEventData->mUpdateLeftSelectionPosition = false;
265 if( mEventData->mUpdateRightSelectionPosition )
267 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
271 mEventData->mDecoratorUpdated = true;
272 mEventData->mUpdateRightSelectionPosition = false;
275 if( mEventData->mUpdateHighlightBox )
277 RepositionSelectionHandles();
279 mEventData->mUpdateLeftSelectionPosition = false;
280 mEventData->mUpdateRightSelectionPosition = false;
281 mEventData->mUpdateHighlightBox = false;
282 mEventData->mIsLeftHandleSelected = false;
283 mEventData->mIsRightHandleSelected = false;
286 mEventData->mScrollAfterUpdatePosition = false;
289 if( mEventData->mUpdateInputStyle )
291 // Keep a copy of the current input style.
292 InputStyle currentInputStyle;
293 currentInputStyle.Copy( mEventData->mInputStyle );
295 // Set the default style first.
296 RetrieveDefaultInputStyle( mEventData->mInputStyle );
298 // Get the character index from the cursor index.
299 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
301 // Retrieve the style from the style runs stored in the logical model.
302 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
304 // Compare if the input style has changed.
305 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
307 if( hasInputStyleChanged )
309 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
310 // Queue the input style changed signal.
311 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
314 mEventData->mUpdateInputStyle = false;
317 mEventData->mEventQueue.clear();
319 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
321 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
322 mEventData->mDecoratorUpdated = false;
324 return decoratorUpdated;
327 void Controller::Impl::NotifyImfManager()
329 if( mEventData && mEventData->mImfManager )
331 CharacterIndex cursorPosition = GetLogicalCursorPosition();
333 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
335 // Update the cursor position by removing the initial white spaces.
336 if( cursorPosition < numberOfWhiteSpaces )
342 cursorPosition -= numberOfWhiteSpaces;
345 mEventData->mImfManager.SetCursorPosition( cursorPosition );
346 mEventData->mImfManager.NotifyCursorPosition();
350 void Controller::Impl::NotifyImfMultiLineStatus()
354 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
355 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
359 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
361 CharacterIndex cursorPosition = 0u;
365 if( ( EventData::SELECTING == mEventData->mState ) ||
366 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
368 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
372 cursorPosition = mEventData->mPrimaryCursorPosition;
376 return cursorPosition;
379 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
381 Length numberOfWhiteSpaces = 0u;
383 // Get the buffer to the text.
384 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
386 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
387 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
389 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
395 return numberOfWhiteSpaces;
398 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
400 // Get the total number of characters.
401 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
403 // Retrieve the text.
404 if( 0u != numberOfCharacters )
406 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
410 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
412 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
413 mTextUpdateInfo.mStartGlyphIndex = 0u;
414 mTextUpdateInfo.mStartLineIndex = 0u;
415 numberOfCharacters = 0u;
417 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
418 if( 0u == numberOfParagraphs )
420 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
421 numberOfCharacters = 0u;
423 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
425 // Nothing else to do if there are no paragraphs.
429 // Find the paragraphs to be updated.
430 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
431 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
433 // Text is being added at the end of the current text.
434 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
436 // Text is being added in a new paragraph after the last character of the text.
437 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
438 numberOfCharacters = 0u;
439 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
441 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
442 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
444 // Nothing else to do;
448 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
452 Length numberOfCharactersToUpdate = 0u;
453 if( mTextUpdateInfo.mFullRelayoutNeeded )
455 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
459 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
461 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
462 numberOfCharactersToUpdate,
463 paragraphsToBeUpdated );
466 if( 0u != paragraphsToBeUpdated.Count() )
468 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
469 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
470 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
472 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
473 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
475 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
476 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
477 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
478 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
480 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
481 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
483 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
487 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
492 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
495 void Controller::Impl::ClearFullModelData( OperationsMask operations )
497 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
499 mModel->mLogicalModel->mLineBreakInfo.Clear();
500 mModel->mLogicalModel->mParagraphInfo.Clear();
503 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
505 mModel->mLogicalModel->mLineBreakInfo.Clear();
508 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
510 mModel->mLogicalModel->mScriptRuns.Clear();
513 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
515 mModel->mLogicalModel->mFontRuns.Clear();
518 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
520 if( NO_OPERATION != ( BIDI_INFO & operations ) )
522 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
523 mModel->mLogicalModel->mCharacterDirections.Clear();
526 if( NO_OPERATION != ( REORDER & operations ) )
528 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
529 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
530 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
534 BidirectionalLineInfoRun& bidiLineInfo = *it;
536 free( bidiLineInfo.visualToLogicalMap );
537 bidiLineInfo.visualToLogicalMap = NULL;
539 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
543 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
545 mModel->mVisualModel->mGlyphs.Clear();
546 mModel->mVisualModel->mGlyphsToCharacters.Clear();
547 mModel->mVisualModel->mCharactersToGlyph.Clear();
548 mModel->mVisualModel->mCharactersPerGlyph.Clear();
549 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
550 mModel->mVisualModel->mGlyphPositions.Clear();
553 if( NO_OPERATION != ( LAYOUT & operations ) )
555 mModel->mVisualModel->mLines.Clear();
558 if( NO_OPERATION != ( COLOR & operations ) )
560 mModel->mVisualModel->mColorIndices.Clear();
564 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
566 const CharacterIndex endIndexPlusOne = endIndex + 1u;
568 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
570 // Clear the line break info.
571 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
573 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
574 lineBreakInfoBuffer + endIndexPlusOne );
576 // Clear the paragraphs.
577 ClearCharacterRuns( startIndex,
579 mModel->mLogicalModel->mParagraphInfo );
582 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
584 // Clear the word break info.
585 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
587 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
588 wordBreakInfoBuffer + endIndexPlusOne );
591 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
593 // Clear the scripts.
594 ClearCharacterRuns( startIndex,
596 mModel->mLogicalModel->mScriptRuns );
599 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
602 ClearCharacterRuns( startIndex,
604 mModel->mLogicalModel->mFontRuns );
607 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
609 if( NO_OPERATION != ( BIDI_INFO & operations ) )
611 // Clear the bidirectional paragraph info.
612 ClearCharacterRuns( startIndex,
614 mModel->mLogicalModel->mBidirectionalParagraphInfo );
616 // Clear the character's directions.
617 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
619 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
620 characterDirectionsBuffer + endIndexPlusOne );
623 if( NO_OPERATION != ( REORDER & operations ) )
625 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
626 uint32_t endRemoveIndex = startRemoveIndex;
627 ClearCharacterRuns( startIndex,
629 mModel->mLogicalModel->mBidirectionalLineInfo,
633 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
635 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
636 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
637 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
641 BidirectionalLineInfoRun& bidiLineInfo = *it;
643 free( bidiLineInfo.visualToLogicalMap );
644 bidiLineInfo.visualToLogicalMap = NULL;
647 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
648 bidirectionalLineInfoBuffer + endRemoveIndex );
653 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
655 const CharacterIndex endIndexPlusOne = endIndex + 1u;
656 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
658 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
659 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
660 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
662 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
663 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
665 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
667 // Update the character to glyph indices.
668 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
669 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
673 CharacterIndex& index = *it;
674 index -= numberOfGlyphsRemoved;
677 // Clear the character to glyph conversion table.
678 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
679 charactersToGlyphBuffer + endIndexPlusOne );
681 // Clear the glyphs per character table.
682 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
683 glyphsPerCharacterBuffer + endIndexPlusOne );
685 // Clear the glyphs buffer.
686 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
687 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
688 glyphsBuffer + endGlyphIndexPlusOne );
690 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
692 // Update the glyph to character indices.
693 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
694 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
698 CharacterIndex& index = *it;
699 index -= numberOfCharactersRemoved;
702 // Clear the glyphs to characters buffer.
703 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
704 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
706 // Clear the characters per glyph buffer.
707 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
708 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
709 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
711 // Clear the positions buffer.
712 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
713 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
714 positionsBuffer + endGlyphIndexPlusOne );
717 if( NO_OPERATION != ( LAYOUT & operations ) )
720 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
721 uint32_t endRemoveIndex = startRemoveIndex;
722 ClearCharacterRuns( startIndex,
724 mModel->mVisualModel->mLines,
728 // Will update the glyph runs.
729 startRemoveIndex = mModel->mVisualModel->mLines.Count();
730 endRemoveIndex = startRemoveIndex;
731 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
732 endGlyphIndexPlusOne - 1u,
733 mModel->mVisualModel->mLines,
737 // Set the line index from where to insert the new laid-out lines.
738 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
740 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
741 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
742 linesBuffer + endRemoveIndex );
745 if( NO_OPERATION != ( COLOR & operations ) )
747 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
749 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
750 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
751 colorIndexBuffer + endGlyphIndexPlusOne );
756 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
758 if( mTextUpdateInfo.mClearAll ||
759 ( ( 0u == startIndex ) &&
760 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
762 ClearFullModelData( operations );
766 // Clear the model data related with characters.
767 ClearCharacterModelData( startIndex, endIndex, operations );
769 // Clear the model data related with glyphs.
770 ClearGlyphModelData( startIndex, endIndex, operations );
773 // The estimated number of lines. Used to avoid reallocations when layouting.
774 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
776 mModel->mVisualModel->ClearCaches();
779 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
781 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
783 // Calculate the operations to be done.
784 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
786 if( NO_OPERATION == operations )
788 // Nothing to do if no operations are pending and required.
792 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
793 Vector<Character> displayCharacters;
794 bool useHiddenText = false;
795 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
797 mHiddenInput->Substitute( srcCharacters,displayCharacters );
798 useHiddenText = true;
801 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
802 const Length numberOfCharacters = utf32Characters.Count();
804 // Index to the first character of the first paragraph to be updated.
805 CharacterIndex startIndex = 0u;
806 // Number of characters of the paragraphs to be removed.
807 Length paragraphCharacters = 0u;
809 CalculateTextUpdateIndices( paragraphCharacters );
810 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
812 if( mTextUpdateInfo.mClearAll ||
813 ( 0u != paragraphCharacters ) )
815 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
818 mTextUpdateInfo.mClearAll = false;
820 // Whether the model is updated.
821 bool updated = false;
823 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
824 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
826 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
828 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
829 // calculate the bidirectional info for each 'paragraph'.
830 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
831 // is not shaped together).
832 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
834 SetLineBreakInfo( utf32Characters,
836 requestedNumberOfCharacters,
839 // Create the paragraph info.
840 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
841 requestedNumberOfCharacters );
845 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
846 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
848 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
849 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
851 SetWordBreakInfo( utf32Characters,
853 requestedNumberOfCharacters,
858 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
859 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
861 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
862 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
864 if( getScripts || validateFonts )
866 // Validates the fonts assigned by the application or assigns default ones.
867 // It makes sure all the characters are going to be rendered by the correct font.
868 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
872 // Retrieves the scripts used in the text.
873 multilanguageSupport.SetScripts( utf32Characters,
875 requestedNumberOfCharacters,
881 // Validate the fonts set through the mark-up string.
882 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
884 // Get the default font's description.
885 TextAbstraction::FontDescription defaultFontDescription;
886 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
887 if( NULL != mFontDefaults )
889 defaultFontDescription = mFontDefaults->mFontDescription;
890 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
893 // Validates the fonts. If there is a character with no assigned font it sets a default one.
894 // After this call, fonts are validated.
895 multilanguageSupport.ValidateFonts( utf32Characters,
898 defaultFontDescription,
901 requestedNumberOfCharacters,
907 Vector<Character> mirroredUtf32Characters;
908 bool textMirrored = false;
909 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
910 if( NO_OPERATION != ( BIDI_INFO & operations ) )
912 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
913 bidirectionalInfo.Reserve( numberOfParagraphs );
915 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
916 SetBidirectionalInfo( utf32Characters,
920 requestedNumberOfCharacters,
923 if( 0u != bidirectionalInfo.Count() )
925 // Only set the character directions if there is right to left characters.
926 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
927 GetCharactersDirection( bidirectionalInfo,
930 requestedNumberOfCharacters,
933 // This paragraph has right to left text. Some characters may need to be mirrored.
934 // TODO: consider if the mirrored string can be stored as well.
936 textMirrored = GetMirroredText( utf32Characters,
940 requestedNumberOfCharacters,
941 mirroredUtf32Characters );
945 // There is no right to left characters. Clear the directions vector.
946 mModel->mLogicalModel->mCharacterDirections.Clear();
951 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
952 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
953 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
954 Vector<GlyphIndex> newParagraphGlyphs;
955 newParagraphGlyphs.Reserve( numberOfParagraphs );
957 const Length currentNumberOfGlyphs = glyphs.Count();
958 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
960 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
962 ShapeText( textToShape,
967 mTextUpdateInfo.mStartGlyphIndex,
968 requestedNumberOfCharacters,
970 glyphsToCharactersMap,
972 newParagraphGlyphs );
974 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
975 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
976 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
980 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
982 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
984 GlyphInfo* glyphsBuffer = glyphs.Begin();
985 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
987 // Update the width and advance of all new paragraph characters.
988 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
990 const GlyphIndex index = *it;
991 GlyphInfo& glyph = *( glyphsBuffer + index );
993 glyph.xBearing = 0.f;
1000 if( NO_OPERATION != ( COLOR & operations ) )
1002 // Set the color runs in glyphs.
1003 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1004 mModel->mVisualModel->mCharactersToGlyph,
1005 mModel->mVisualModel->mGlyphsPerCharacter,
1007 mTextUpdateInfo.mStartGlyphIndex,
1008 requestedNumberOfCharacters,
1009 mModel->mVisualModel->mColors,
1010 mModel->mVisualModel->mColorIndices );
1015 if( ( NULL != mEventData ) &&
1016 mEventData->mPreEditFlag &&
1017 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1019 // Add the underline for the pre-edit text.
1020 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1021 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1023 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1024 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1025 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1026 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1028 GlyphRun underlineRun;
1029 underlineRun.glyphIndex = glyphStart;
1030 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1032 // TODO: At the moment the underline runs are only for pre-edit.
1033 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1036 // The estimated number of lines. Used to avoid reallocations when layouting.
1037 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1039 // Set the previous number of characters for the next time the text is updated.
1040 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1045 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1047 // Sets the default text's color.
1048 inputStyle.textColor = mTextColor;
1049 inputStyle.isDefaultColor = true;
1051 inputStyle.familyName.clear();
1052 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1053 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1054 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1055 inputStyle.size = 0.f;
1057 inputStyle.lineSpacing = 0.f;
1059 inputStyle.underlineProperties.clear();
1060 inputStyle.shadowProperties.clear();
1061 inputStyle.embossProperties.clear();
1062 inputStyle.outlineProperties.clear();
1064 inputStyle.isFamilyDefined = false;
1065 inputStyle.isWeightDefined = false;
1066 inputStyle.isWidthDefined = false;
1067 inputStyle.isSlantDefined = false;
1068 inputStyle.isSizeDefined = false;
1070 inputStyle.isLineSpacingDefined = false;
1072 inputStyle.isUnderlineDefined = false;
1073 inputStyle.isShadowDefined = false;
1074 inputStyle.isEmbossDefined = false;
1075 inputStyle.isOutlineDefined = false;
1077 // Sets the default font's family name, weight, width, slant and size.
1080 if( mFontDefaults->familyDefined )
1082 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1083 inputStyle.isFamilyDefined = true;
1086 if( mFontDefaults->weightDefined )
1088 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1089 inputStyle.isWeightDefined = true;
1092 if( mFontDefaults->widthDefined )
1094 inputStyle.width = mFontDefaults->mFontDescription.width;
1095 inputStyle.isWidthDefined = true;
1098 if( mFontDefaults->slantDefined )
1100 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1101 inputStyle.isSlantDefined = true;
1104 if( mFontDefaults->sizeDefined )
1106 inputStyle.size = mFontDefaults->mDefaultPointSize;
1107 inputStyle.isSizeDefined = true;
1112 float Controller::Impl::GetDefaultFontLineHeight()
1114 FontId defaultFontId = 0u;
1115 if( NULL == mFontDefaults )
1117 TextAbstraction::FontDescription fontDescription;
1118 defaultFontId = mFontClient.GetFontId( fontDescription );
1122 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1125 Text::FontMetrics fontMetrics;
1126 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1128 return( fontMetrics.ascender - fontMetrics.descender );
1131 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1133 if( NULL == mEventData )
1135 // Nothing to do if there is no text input.
1139 int keyCode = event.p1.mInt;
1141 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1143 if( mEventData->mPrimaryCursorPosition > 0u )
1145 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1148 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1150 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1152 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1155 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1157 // Get first the line index of the current cursor position index.
1158 CharacterIndex characterIndex = 0u;
1160 if( mEventData->mPrimaryCursorPosition > 0u )
1162 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1165 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1166 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1168 // Retrieve the cursor position info.
1169 CursorInfo cursorInfo;
1170 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1173 // Get the line above.
1174 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1176 // Get the next hit 'y' point.
1177 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1179 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1180 bool matchedCharacter = false;
1181 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1182 mModel->mLogicalModel,
1184 mEventData->mCursorHookPositionX,
1186 CharacterHitTest::TAP,
1189 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1191 // Get first the line index of the current cursor position index.
1192 CharacterIndex characterIndex = 0u;
1194 if( mEventData->mPrimaryCursorPosition > 0u )
1196 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1199 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1201 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1203 // Retrieve the cursor position info.
1204 CursorInfo cursorInfo;
1205 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1208 // Get the line below.
1209 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1211 // Get the next hit 'y' point.
1212 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1214 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1215 bool matchedCharacter = false;
1216 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1217 mModel->mLogicalModel,
1219 mEventData->mCursorHookPositionX,
1221 CharacterHitTest::TAP,
1226 mEventData->mUpdateCursorPosition = true;
1227 mEventData->mUpdateInputStyle = true;
1228 mEventData->mScrollAfterUpdatePosition = true;
1231 void Controller::Impl::OnTapEvent( const Event& event )
1233 if( NULL != mEventData )
1235 const unsigned int tapCount = event.p1.mUint;
1237 if( 1u == tapCount )
1239 if( IsShowingRealText() )
1241 // Convert from control's coords to text's coords.
1242 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1243 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1245 // Keep the tap 'x' position. Used to move the cursor.
1246 mEventData->mCursorHookPositionX = xPosition;
1248 // Whether to touch point hits on a glyph.
1249 bool matchedCharacter = false;
1250 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1251 mModel->mLogicalModel,
1255 CharacterHitTest::TAP,
1258 // When the cursor position is changing, delay cursor blinking
1259 mEventData->mDecorator->DelayCursorBlink();
1263 mEventData->mPrimaryCursorPosition = 0u;
1266 mEventData->mUpdateCursorPosition = true;
1267 mEventData->mUpdateGrabHandlePosition = true;
1268 mEventData->mScrollAfterUpdatePosition = true;
1269 mEventData->mUpdateInputStyle = true;
1271 // Notify the cursor position to the imf manager.
1272 if( mEventData->mImfManager )
1274 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1275 mEventData->mImfManager.NotifyCursorPosition();
1278 else if( 2u == tapCount )
1280 if( mEventData->mSelectionEnabled )
1282 // Convert from control's coords to text's coords.
1283 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1284 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1286 // Calculates the logical position from the x,y coords.
1287 RepositionSelectionHandles( xPosition,
1289 mEventData->mDoubleTapAction );
1295 void Controller::Impl::OnPanEvent( const Event& event )
1297 if( NULL == mEventData )
1299 // Nothing to do if there is no text input.
1303 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1304 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1306 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1308 // Nothing to do if scrolling is not enabled.
1312 const int state = event.p1.mInt;
1316 case Gesture::Started:
1318 // Will remove the cursor, handles or text's popup, ...
1319 ChangeState( EventData::TEXT_PANNING );
1322 case Gesture::Continuing:
1324 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1325 const Vector2 currentScroll = mModel->mScrollPosition;
1327 if( isHorizontalScrollEnabled )
1329 const float displacementX = event.p2.mFloat;
1330 mModel->mScrollPosition.x += displacementX;
1332 ClampHorizontalScroll( layoutSize );
1335 if( isVerticalScrollEnabled )
1337 const float displacementY = event.p3.mFloat;
1338 mModel->mScrollPosition.y += displacementY;
1340 ClampVerticalScroll( layoutSize );
1343 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1346 case Gesture::Finished:
1347 case Gesture::Cancelled: // FALLTHROUGH
1349 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1350 ChangeState( mEventData->mPreviousState );
1358 void Controller::Impl::OnLongPressEvent( const Event& event )
1360 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1362 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1364 ChangeState( EventData::EDITING_WITH_POPUP );
1365 mEventData->mDecoratorUpdated = true;
1366 mEventData->mUpdateInputStyle = true;
1370 if( mEventData->mSelectionEnabled )
1372 // Convert from control's coords to text's coords.
1373 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1374 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1376 // Calculates the logical position from the x,y coords.
1377 RepositionSelectionHandles( xPosition,
1379 mEventData->mLongPressAction );
1384 void Controller::Impl::OnHandleEvent( const Event& event )
1386 if( NULL == mEventData )
1388 // Nothing to do if there is no text input.
1392 const unsigned int state = event.p1.mUint;
1393 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1394 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1396 if( HANDLE_PRESSED == state )
1398 // Convert from decorator's coords to text's coords.
1399 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1400 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1402 // Need to calculate the handle's new position.
1403 bool matchedCharacter = false;
1404 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1405 mModel->mLogicalModel,
1409 CharacterHitTest::SCROLL,
1412 if( Event::GRAB_HANDLE_EVENT == event.type )
1414 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1416 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1418 // Updates the cursor position if the handle's new position is different than the current one.
1419 mEventData->mUpdateCursorPosition = true;
1420 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1421 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1422 mEventData->mPrimaryCursorPosition = handleNewPosition;
1425 // 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.
1426 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1428 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1430 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1432 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1433 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1435 // Updates the highlight box if the handle's new position is different than the current one.
1436 mEventData->mUpdateHighlightBox = true;
1437 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1438 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1439 mEventData->mLeftSelectionPosition = handleNewPosition;
1442 // 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.
1443 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1445 // Will define the order to scroll the text to match the handle position.
1446 mEventData->mIsLeftHandleSelected = true;
1447 mEventData->mIsRightHandleSelected = false;
1449 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1451 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1453 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1454 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1456 // Updates the highlight box if the handle's new position is different than the current one.
1457 mEventData->mUpdateHighlightBox = true;
1458 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1459 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1460 mEventData->mRightSelectionPosition = handleNewPosition;
1463 // 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.
1464 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1466 // Will define the order to scroll the text to match the handle position.
1467 mEventData->mIsLeftHandleSelected = false;
1468 mEventData->mIsRightHandleSelected = true;
1470 } // end ( HANDLE_PRESSED == state )
1471 else if( ( HANDLE_RELEASED == state ) ||
1472 handleStopScrolling )
1474 CharacterIndex handlePosition = 0u;
1475 if( handleStopScrolling || isSmoothHandlePanEnabled )
1477 // Convert from decorator's coords to text's coords.
1478 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1479 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1481 bool matchedCharacter = false;
1482 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1483 mModel->mLogicalModel,
1487 CharacterHitTest::SCROLL,
1491 if( Event::GRAB_HANDLE_EVENT == event.type )
1493 mEventData->mUpdateCursorPosition = true;
1494 mEventData->mUpdateGrabHandlePosition = true;
1495 mEventData->mUpdateInputStyle = true;
1497 if( !IsClipboardEmpty() )
1499 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1502 if( handleStopScrolling || isSmoothHandlePanEnabled )
1504 mEventData->mScrollAfterUpdatePosition = true;
1505 mEventData->mPrimaryCursorPosition = handlePosition;
1508 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1510 ChangeState( EventData::SELECTING );
1512 mEventData->mUpdateHighlightBox = true;
1513 mEventData->mUpdateLeftSelectionPosition = true;
1514 mEventData->mUpdateRightSelectionPosition = true;
1516 if( handleStopScrolling || isSmoothHandlePanEnabled )
1518 mEventData->mScrollAfterUpdatePosition = true;
1520 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1521 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1523 mEventData->mLeftSelectionPosition = handlePosition;
1527 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1529 ChangeState( EventData::SELECTING );
1531 mEventData->mUpdateHighlightBox = true;
1532 mEventData->mUpdateRightSelectionPosition = true;
1533 mEventData->mUpdateLeftSelectionPosition = true;
1535 if( handleStopScrolling || isSmoothHandlePanEnabled )
1537 mEventData->mScrollAfterUpdatePosition = true;
1538 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1539 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1541 mEventData->mRightSelectionPosition = handlePosition;
1546 mEventData->mDecoratorUpdated = true;
1547 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1548 else if( HANDLE_SCROLLING == state )
1550 const float xSpeed = event.p2.mFloat;
1551 const float ySpeed = event.p3.mFloat;
1552 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1553 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1555 mModel->mScrollPosition.x += xSpeed;
1556 mModel->mScrollPosition.y += ySpeed;
1558 ClampHorizontalScroll( layoutSize );
1559 ClampVerticalScroll( layoutSize );
1561 bool endOfScroll = false;
1562 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1564 // Notify the decorator there is no more text to scroll.
1565 // The decorator won't send more scroll events.
1566 mEventData->mDecorator->NotifyEndOfScroll();
1567 // Still need to set the position of the handle.
1571 // Set the position of the handle.
1572 const bool scrollRightDirection = xSpeed > 0.f;
1573 const bool scrollBottomDirection = ySpeed > 0.f;
1574 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1575 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1577 if( Event::GRAB_HANDLE_EVENT == event.type )
1579 ChangeState( EventData::GRAB_HANDLE_PANNING );
1581 // Get the grab handle position in decorator coords.
1582 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1584 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1586 // Position the grag handle close to either the left or right edge.
1587 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1590 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1592 position.x = mEventData->mCursorHookPositionX;
1594 // Position the grag handle close to either the top or bottom edge.
1595 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1598 // Get the new handle position.
1599 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1600 bool matchedCharacter = false;
1601 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1602 mModel->mLogicalModel,
1604 position.x - mModel->mScrollPosition.x,
1605 position.y - mModel->mScrollPosition.y,
1606 CharacterHitTest::SCROLL,
1609 if( mEventData->mPrimaryCursorPosition != handlePosition )
1611 mEventData->mUpdateCursorPosition = true;
1612 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1613 mEventData->mScrollAfterUpdatePosition = true;
1614 mEventData->mPrimaryCursorPosition = handlePosition;
1616 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1618 // Updates the decorator if the soft handle panning is enabled.
1619 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1621 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1623 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1625 // Get the selection handle position in decorator coords.
1626 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1628 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1630 // Position the selection handle close to either the left or right edge.
1631 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1634 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1636 position.x = mEventData->mCursorHookPositionX;
1638 // Position the grag handle close to either the top or bottom edge.
1639 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1642 // Get the new handle position.
1643 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1644 bool matchedCharacter = false;
1645 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1646 mModel->mLogicalModel,
1648 position.x - mModel->mScrollPosition.x,
1649 position.y - mModel->mScrollPosition.y,
1650 CharacterHitTest::SCROLL,
1653 if( leftSelectionHandleEvent )
1655 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1657 if( differentHandles || endOfScroll )
1659 mEventData->mUpdateHighlightBox = true;
1660 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1661 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1662 mEventData->mLeftSelectionPosition = handlePosition;
1667 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1668 if( differentHandles || endOfScroll )
1670 mEventData->mUpdateHighlightBox = true;
1671 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1672 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1673 mEventData->mRightSelectionPosition = handlePosition;
1677 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1679 RepositionSelectionHandles();
1681 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1684 mEventData->mDecoratorUpdated = true;
1685 } // end ( HANDLE_SCROLLING == state )
1688 void Controller::Impl::OnSelectEvent( const Event& event )
1690 if( NULL == mEventData )
1692 // Nothing to do if there is no text.
1696 if( mEventData->mSelectionEnabled )
1698 // Convert from control's coords to text's coords.
1699 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1700 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1702 // Calculates the logical position from the x,y coords.
1703 RepositionSelectionHandles( xPosition,
1705 Controller::NoTextTap::HIGHLIGHT );
1709 void Controller::Impl::OnSelectAllEvent()
1711 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1713 if( NULL == mEventData )
1715 // Nothing to do if there is no text.
1719 if( mEventData->mSelectionEnabled )
1721 ChangeState( EventData::SELECTING );
1723 mEventData->mLeftSelectionPosition = 0u;
1724 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1726 mEventData->mScrollAfterUpdatePosition = true;
1727 mEventData->mUpdateLeftSelectionPosition = true;
1728 mEventData->mUpdateRightSelectionPosition = true;
1729 mEventData->mUpdateHighlightBox = true;
1733 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1735 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1737 // Nothing to select if handles are in the same place.
1738 selectedText.clear();
1742 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1744 //Get start and end position of selection
1745 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1746 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1748 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1749 const Length numberOfCharacters = utf32Characters.Count();
1751 // Validate the start and end selection points
1752 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1754 //Get text as a UTF8 string
1755 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1757 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1759 // Keep a copy of the current input style.
1760 InputStyle currentInputStyle;
1761 currentInputStyle.Copy( mEventData->mInputStyle );
1763 // Set as input style the style of the first deleted character.
1764 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1766 // Compare if the input style has changed.
1767 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1769 if( hasInputStyleChanged )
1771 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1772 // Queue the input style changed signal.
1773 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1776 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1778 // Mark the paragraphs to be updated.
1779 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1780 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1782 // Delete text between handles
1783 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1784 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1785 utf32Characters.Erase( first, last );
1787 // Will show the cursor at the first character of the selection.
1788 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1792 // Will show the cursor at the last character of the selection.
1793 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1796 mEventData->mDecoratorUpdated = true;
1800 void Controller::Impl::ShowClipboard()
1804 mClipboard.ShowClipboard();
1808 void Controller::Impl::HideClipboard()
1810 if( mClipboard && mClipboardHideEnabled )
1812 mClipboard.HideClipboard();
1816 void Controller::Impl::SetClipboardHideEnable(bool enable)
1818 mClipboardHideEnabled = enable;
1821 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1823 //Send string to clipboard
1824 return ( mClipboard && mClipboard.SetItem( source ) );
1827 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1829 std::string selectedText;
1830 RetrieveSelection( selectedText, deleteAfterSending );
1831 CopyStringToClipboard( selectedText );
1832 ChangeState( EventData::EDITING );
1835 void Controller::Impl::RequestGetTextFromClipboard()
1839 mClipboard.RequestItem();
1843 void Controller::Impl::RepositionSelectionHandles()
1845 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1846 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1848 if( selectionStart == selectionEnd )
1850 // Nothing to select if handles are in the same place.
1854 mEventData->mDecorator->ClearHighlights();
1856 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1857 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1858 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1859 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1860 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1861 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1862 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1864 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1865 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1866 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1868 // Swap the indices if the start is greater than the end.
1869 const bool indicesSwapped = selectionStart > selectionEnd;
1871 // Tell the decorator to flip the selection handles if needed.
1872 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1874 if( indicesSwapped )
1876 std::swap( selectionStart, selectionEnd );
1879 // Get the indices to the first and last selected glyphs.
1880 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1881 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1882 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1883 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1885 // Get the lines where the glyphs are laid-out.
1886 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1888 LineIndex lineIndex = 0u;
1889 Length numberOfLines = 0u;
1890 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1891 1u + glyphEnd - glyphStart,
1894 const LineIndex firstLineIndex = lineIndex;
1896 // Create the structure to store some selection box info.
1897 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1898 selectionBoxLinesInfo.Resize( numberOfLines );
1900 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1901 selectionBoxInfo->minX = MAX_FLOAT;
1902 selectionBoxInfo->maxX = MIN_FLOAT;
1904 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1905 float minHighlightX = std::numeric_limits<float>::max();
1906 float maxHighlightX = std::numeric_limits<float>::min();
1908 Vector2 highLightPosition; // The highlight position in decorator's coords.
1910 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1912 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1913 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1916 // Transform to decorator's (control) coords.
1917 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1919 lineRun += firstLineIndex;
1921 // The line height is the addition of the line ascender and the line descender.
1922 // However, the line descender has a negative value, hence the subtraction.
1923 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1925 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1927 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1928 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1929 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1931 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1932 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1933 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1935 // The number of quads of the selection box.
1936 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1937 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1939 // Count the actual number of quads.
1940 unsigned int actualNumberOfQuads = 0u;
1943 // Traverse the glyphs.
1944 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1946 const GlyphInfo& glyph = *( glyphsBuffer + index );
1947 const Vector2& position = *( positionsBuffer + index );
1949 if( splitStartGlyph )
1951 // 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.
1953 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1954 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1955 // Get the direction of the character.
1956 CharacterDirection isCurrentRightToLeft = false;
1957 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1959 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1962 // The end point could be in the middle of the ligature.
1963 // Calculate the number of characters selected.
1964 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1966 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1967 quad.y = selectionBoxInfo->lineOffset;
1968 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1969 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1971 // Store the min and max 'x' for each line.
1972 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1973 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1975 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1976 ++actualNumberOfQuads;
1978 splitStartGlyph = false;
1982 if( splitEndGlyph && ( index == glyphEnd ) )
1984 // 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.
1986 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1987 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1988 // Get the direction of the character.
1989 CharacterDirection isCurrentRightToLeft = false;
1990 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1992 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1995 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1997 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1998 quad.y = selectionBoxInfo->lineOffset;
1999 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2000 quad.w = quad.y + selectionBoxInfo->lineHeight;
2002 // Store the min and max 'x' for each line.
2003 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2004 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2006 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2008 ++actualNumberOfQuads;
2010 splitEndGlyph = false;
2014 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2015 quad.y = selectionBoxInfo->lineOffset;
2016 quad.z = quad.x + glyph.advance;
2017 quad.w = quad.y + selectionBoxInfo->lineHeight;
2019 // Store the min and max 'x' for each line.
2020 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2021 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2023 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2025 ++actualNumberOfQuads;
2027 // Whether to retrieve the next line.
2028 if( index == lastGlyphOfLine )
2031 if( lineIndex < firstLineIndex + numberOfLines )
2033 // Retrieve the next line.
2036 // Get the last glyph of the new line.
2037 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2039 // Keep the offset and height of the current selection box.
2040 const float currentLineOffset = selectionBoxInfo->lineOffset;
2041 const float currentLineHeight = selectionBoxInfo->lineHeight;
2043 // Get the selection box info for the next line.
2046 selectionBoxInfo->minX = MAX_FLOAT;
2047 selectionBoxInfo->maxX = MIN_FLOAT;
2049 // Update the line's vertical offset.
2050 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2052 // The line height is the addition of the line ascender and the line descender.
2053 // However, the line descender has a negative value, hence the subtraction.
2054 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2059 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2060 // The final width is calculated after 'boxifying' the selection.
2061 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2062 endIt = selectionBoxLinesInfo.End();
2066 const SelectionBoxInfo& info = *it;
2068 // Update the size of the highlighted text.
2069 highLightSize.height += info.lineHeight;
2070 minHighlightX = std::min( minHighlightX, info.minX );
2071 maxHighlightX = std::max( maxHighlightX, info.maxX );
2074 // Add extra geometry to 'boxify' the selection.
2076 if( 1u < numberOfLines )
2078 // Boxify the first line.
2079 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2080 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2082 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2083 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2088 quad.y = firstSelectionBoxLineInfo.lineOffset;
2089 quad.z = firstSelectionBoxLineInfo.minX;
2090 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2092 // Boxify at the beginning of the line.
2093 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2095 ++actualNumberOfQuads;
2097 // Update the size of the highlighted text.
2098 minHighlightX = 0.f;
2103 quad.x = firstSelectionBoxLineInfo.maxX;
2104 quad.y = firstSelectionBoxLineInfo.lineOffset;
2105 quad.z = mModel->mVisualModel->mControlSize.width;
2106 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2108 // Boxify at the end of the line.
2109 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2111 ++actualNumberOfQuads;
2113 // Update the size of the highlighted text.
2114 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2117 // Boxify the central lines.
2118 if( 2u < numberOfLines )
2120 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2121 endIt = selectionBoxLinesInfo.End() - 1u;
2125 const SelectionBoxInfo& info = *it;
2128 quad.y = info.lineOffset;
2130 quad.w = info.lineOffset + info.lineHeight;
2132 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2134 ++actualNumberOfQuads;
2137 quad.y = info.lineOffset;
2138 quad.z = mModel->mVisualModel->mControlSize.width;
2139 quad.w = info.lineOffset + info.lineHeight;
2141 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2143 ++actualNumberOfQuads;
2146 // Update the size of the highlighted text.
2147 minHighlightX = 0.f;
2148 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2151 // Boxify the last line.
2152 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2153 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2155 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2156 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2161 quad.y = lastSelectionBoxLineInfo.lineOffset;
2162 quad.z = lastSelectionBoxLineInfo.minX;
2163 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2165 // Boxify at the beginning of the line.
2166 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2168 ++actualNumberOfQuads;
2170 // Update the size of the highlighted text.
2171 minHighlightX = 0.f;
2176 quad.x = lastSelectionBoxLineInfo.maxX;
2177 quad.y = lastSelectionBoxLineInfo.lineOffset;
2178 quad.z = mModel->mVisualModel->mControlSize.width;
2179 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2181 // Boxify at the end of the line.
2182 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2184 ++actualNumberOfQuads;
2186 // Update the size of the highlighted text.
2187 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2191 // Set the actual number of quads.
2192 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2194 // Sets the highlight's size and position. In decorator's coords.
2195 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2196 highLightSize.width = maxHighlightX - minHighlightX;
2198 highLightPosition.x = minHighlightX;
2199 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2200 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2202 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2204 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2206 CursorInfo primaryCursorInfo;
2207 GetCursorPosition( mEventData->mLeftSelectionPosition,
2208 primaryCursorInfo );
2210 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2212 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2214 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2215 primaryCursorInfo.lineHeight );
2217 CursorInfo secondaryCursorInfo;
2218 GetCursorPosition( mEventData->mRightSelectionPosition,
2219 secondaryCursorInfo );
2221 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2223 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2224 secondaryPosition.x,
2225 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2226 secondaryCursorInfo.lineHeight );
2229 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2230 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2232 // Set the flag to update the decorator.
2233 mEventData->mDecoratorUpdated = true;
2236 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2238 if( NULL == mEventData )
2240 // Nothing to do if there is no text input.
2244 if( IsShowingPlaceholderText() )
2246 // Nothing to do if there is the place-holder text.
2250 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2251 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2252 if( ( 0 == numberOfGlyphs ) ||
2253 ( 0 == numberOfLines ) )
2255 // Nothing to do if there is no text.
2259 // Find which word was selected
2260 CharacterIndex selectionStart( 0 );
2261 CharacterIndex selectionEnd( 0 );
2262 CharacterIndex noTextHitIndex( 0 );
2263 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2264 mModel->mLogicalModel,
2271 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2273 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2275 ChangeState( EventData::SELECTING );
2277 mEventData->mLeftSelectionPosition = selectionStart;
2278 mEventData->mRightSelectionPosition = selectionEnd;
2280 mEventData->mUpdateLeftSelectionPosition = true;
2281 mEventData->mUpdateRightSelectionPosition = true;
2282 mEventData->mUpdateHighlightBox = true;
2284 // It may happen an IMF commit event arrives before the selection event
2285 // if the IMF manager is in pre-edit state. The commit event will set the
2286 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2287 // to false, the highlight box won't be updated.
2288 mEventData->mUpdateCursorPosition = false;
2290 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2292 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2294 // Nothing to select. i.e. a white space, out of bounds
2295 ChangeState( EventData::EDITING_WITH_POPUP );
2297 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2299 mEventData->mUpdateCursorPosition = true;
2300 mEventData->mUpdateGrabHandlePosition = true;
2301 mEventData->mScrollAfterUpdatePosition = true;
2302 mEventData->mUpdateInputStyle = true;
2304 else if( Controller::NoTextTap::NO_ACTION == action )
2306 // Nothing to select. i.e. a white space, out of bounds
2307 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2309 mEventData->mUpdateCursorPosition = true;
2310 mEventData->mUpdateGrabHandlePosition = true;
2311 mEventData->mScrollAfterUpdatePosition = true;
2312 mEventData->mUpdateInputStyle = true;
2316 void Controller::Impl::SetPopupButtons()
2319 * Sets the Popup buttons to be shown depending on State.
2321 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2323 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2326 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2328 if( EventData::SELECTING == mEventData->mState )
2330 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2332 if( !IsClipboardEmpty() )
2334 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2335 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2338 if( !mEventData->mAllTextSelected )
2340 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2343 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2345 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2347 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2350 if( !IsClipboardEmpty() )
2352 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2353 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2356 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2358 if ( !IsClipboardEmpty() )
2360 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2361 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2365 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2368 void Controller::Impl::ChangeState( EventData::State newState )
2370 if( NULL == mEventData )
2372 // Nothing to do if there is no text input.
2376 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2378 if( mEventData->mState != newState )
2380 mEventData->mPreviousState = mEventData->mState;
2381 mEventData->mState = newState;
2383 switch( mEventData->mState )
2385 case EventData::INACTIVE:
2387 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2388 mEventData->mDecorator->StopCursorBlink();
2389 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2390 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2391 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2392 mEventData->mDecorator->SetHighlightActive( false );
2393 mEventData->mDecorator->SetPopupActive( false );
2394 mEventData->mDecoratorUpdated = true;
2397 case EventData::INTERRUPTED:
2399 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2400 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2401 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2402 mEventData->mDecorator->SetHighlightActive( false );
2403 mEventData->mDecorator->SetPopupActive( false );
2404 mEventData->mDecoratorUpdated = true;
2407 case EventData::SELECTING:
2409 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2410 mEventData->mDecorator->StopCursorBlink();
2411 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2412 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2413 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2414 mEventData->mDecorator->SetHighlightActive( true );
2415 if( mEventData->mGrabHandlePopupEnabled )
2418 mEventData->mDecorator->SetPopupActive( true );
2420 mEventData->mDecoratorUpdated = true;
2423 case EventData::EDITING:
2425 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2426 if( mEventData->mCursorBlinkEnabled )
2428 mEventData->mDecorator->StartCursorBlink();
2430 // Grab handle is not shown until a tap is received whilst EDITING
2431 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2432 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2433 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2434 mEventData->mDecorator->SetHighlightActive( false );
2435 if( mEventData->mGrabHandlePopupEnabled )
2437 mEventData->mDecorator->SetPopupActive( false );
2439 mEventData->mDecoratorUpdated = true;
2442 case EventData::EDITING_WITH_POPUP:
2444 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2446 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2447 if( mEventData->mCursorBlinkEnabled )
2449 mEventData->mDecorator->StartCursorBlink();
2451 if( mEventData->mSelectionEnabled )
2453 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2454 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2455 mEventData->mDecorator->SetHighlightActive( false );
2459 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2461 if( mEventData->mGrabHandlePopupEnabled )
2464 mEventData->mDecorator->SetPopupActive( true );
2466 mEventData->mDecoratorUpdated = true;
2469 case EventData::EDITING_WITH_GRAB_HANDLE:
2471 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2473 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2474 if( mEventData->mCursorBlinkEnabled )
2476 mEventData->mDecorator->StartCursorBlink();
2478 // Grab handle is not shown until a tap is received whilst EDITING
2479 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2480 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2481 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2482 mEventData->mDecorator->SetHighlightActive( false );
2483 if( mEventData->mGrabHandlePopupEnabled )
2485 mEventData->mDecorator->SetPopupActive( false );
2487 mEventData->mDecoratorUpdated = true;
2490 case EventData::SELECTION_HANDLE_PANNING:
2492 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2493 mEventData->mDecorator->StopCursorBlink();
2494 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2495 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2496 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2497 mEventData->mDecorator->SetHighlightActive( true );
2498 if( mEventData->mGrabHandlePopupEnabled )
2500 mEventData->mDecorator->SetPopupActive( false );
2502 mEventData->mDecoratorUpdated = true;
2505 case EventData::GRAB_HANDLE_PANNING:
2507 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2509 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2510 if( mEventData->mCursorBlinkEnabled )
2512 mEventData->mDecorator->StartCursorBlink();
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::EDITING_WITH_PASTE_POPUP:
2527 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2529 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2530 if( mEventData->mCursorBlinkEnabled )
2532 mEventData->mDecorator->StartCursorBlink();
2535 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2536 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2537 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2538 mEventData->mDecorator->SetHighlightActive( false );
2540 if( mEventData->mGrabHandlePopupEnabled )
2543 mEventData->mDecorator->SetPopupActive( true );
2545 mEventData->mDecoratorUpdated = true;
2548 case EventData::TEXT_PANNING:
2550 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2551 mEventData->mDecorator->StopCursorBlink();
2552 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2553 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2554 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2556 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2557 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2558 mEventData->mDecorator->SetHighlightActive( true );
2561 if( mEventData->mGrabHandlePopupEnabled )
2563 mEventData->mDecorator->SetPopupActive( false );
2566 mEventData->mDecoratorUpdated = true;
2573 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2574 CursorInfo& cursorInfo )
2576 if( !IsShowingRealText() )
2578 // Do not want to use the place-holder text to set the cursor position.
2580 // Use the line's height of the font's family set to set the cursor's size.
2581 // If there is no font's family set, use the default font.
2582 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2584 cursorInfo.lineOffset = 0.f;
2585 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2586 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2588 switch( mModel->mHorizontalAlignment )
2590 case Layout::HORIZONTAL_ALIGN_BEGIN:
2592 cursorInfo.primaryPosition.x = 0.f;
2595 case Layout::HORIZONTAL_ALIGN_CENTER:
2597 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2600 case Layout::HORIZONTAL_ALIGN_END:
2602 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2607 // Nothing else to do.
2611 Text::GetCursorPosition( mModel->mVisualModel,
2612 mModel->mLogicalModel,
2617 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2619 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2621 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2622 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2624 if( 0.f > cursorInfo.primaryPosition.x )
2626 cursorInfo.primaryPosition.x = 0.f;
2629 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2630 if( cursorInfo.primaryPosition.x > edgeWidth )
2632 cursorInfo.primaryPosition.x = edgeWidth;
2637 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2639 if( NULL == mEventData )
2641 // Nothing to do if there is no text input.
2645 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2647 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2648 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2650 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2651 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2653 if( numberOfCharacters > 1u )
2655 const Script script = mModel->mLogicalModel->GetScript( index );
2656 if( HasLigatureMustBreak( script ) )
2658 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2659 numberOfCharacters = 1u;
2664 while( 0u == numberOfCharacters )
2667 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2671 if( index < mEventData->mPrimaryCursorPosition )
2673 cursorIndex -= numberOfCharacters;
2677 cursorIndex += numberOfCharacters;
2680 // Will update the cursor hook position.
2681 mEventData->mUpdateCursorHookPosition = true;
2686 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2688 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2689 if( NULL == mEventData )
2691 // Nothing to do if there is no text input.
2692 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2696 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2698 // Sets the cursor position.
2699 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2702 cursorInfo.primaryCursorHeight,
2703 cursorInfo.lineHeight );
2704 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2706 if( mEventData->mUpdateGrabHandlePosition )
2708 // Sets the grab handle position.
2709 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2711 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2712 cursorInfo.lineHeight );
2715 if( cursorInfo.isSecondaryCursor )
2717 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2718 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2719 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2720 cursorInfo.secondaryCursorHeight,
2721 cursorInfo.lineHeight );
2722 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2725 // Set which cursors are active according the state.
2726 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2728 if( cursorInfo.isSecondaryCursor )
2730 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2734 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2739 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2742 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2745 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2746 const CursorInfo& cursorInfo )
2748 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2749 ( RIGHT_SELECTION_HANDLE != handleType ) )
2754 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2756 // Sets the handle's position.
2757 mEventData->mDecorator->SetPosition( handleType,
2759 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2760 cursorInfo.lineHeight );
2762 // If selection handle at start of the text and other at end of the text then all text is selected.
2763 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2764 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2765 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2768 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2770 // Clamp between -space & -alignment offset.
2772 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2774 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2775 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2776 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2778 mEventData->mDecoratorUpdated = true;
2782 mModel->mScrollPosition.x = 0.f;
2786 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2788 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2790 // Nothing to do if the text is single line.
2794 // Clamp between -space & 0.
2795 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2797 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2798 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2799 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2801 mEventData->mDecoratorUpdated = true;
2805 mModel->mScrollPosition.y = 0.f;
2809 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2811 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2813 // position is in actor's coords.
2814 const float positionEndX = position.x + cursorWidth;
2815 const float positionEndY = position.y + lineHeight;
2817 // Transform the position to decorator coords.
2818 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2819 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2821 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2822 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2824 if( decoratorPositionBeginX < 0.f )
2826 mModel->mScrollPosition.x = -position.x;
2828 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2830 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2833 if( decoratorPositionBeginY < 0.f )
2835 mModel->mScrollPosition.y = -position.y;
2837 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2839 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2843 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2845 // Get the current cursor position in decorator coords.
2846 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2848 // Calculate the offset to match the cursor position before the character was deleted.
2849 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2850 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2852 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2853 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2855 // Makes the new cursor position visible if needed.
2856 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2859 void Controller::Impl::RequestRelayout()
2861 if( NULL != mControlInterface )
2863 mControlInterface->RequestTextRelayout();
2869 } // namespace Toolkit