2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderFont( NULL ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false ),
109 mIsPlaceholderPixelSize( false ),
110 mIsPlaceholderElideEnabled( false ),
111 mPlaceholderEllipsisFlag( false ),
112 mShiftSelectionFlag( true )
114 mImfManager = ImfManager::Get();
117 EventData::~EventData()
120 bool Controller::Impl::ProcessInputEvents()
122 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
123 if( NULL == mEventData )
125 // Nothing to do if there is no text input.
126 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
130 if( mEventData->mDecorator )
132 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
133 iter != mEventData->mEventQueue.end();
138 case Event::CURSOR_KEY_EVENT:
140 OnCursorKeyEvent( *iter );
143 case Event::TAP_EVENT:
148 case Event::LONG_PRESS_EVENT:
150 OnLongPressEvent( *iter );
153 case Event::PAN_EVENT:
158 case Event::GRAB_HANDLE_EVENT:
159 case Event::LEFT_SELECTION_HANDLE_EVENT:
160 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
162 OnHandleEvent( *iter );
167 OnSelectEvent( *iter );
170 case Event::SELECT_ALL:
179 if( mEventData->mUpdateCursorPosition ||
180 mEventData->mUpdateHighlightBox )
185 // The cursor must also be repositioned after inserts into the model
186 if( mEventData->mUpdateCursorPosition )
188 // Updates the cursor position and scrolls the text to make it visible.
189 CursorInfo cursorInfo;
190 // Calculate the cursor position from the new cursor index.
191 GetCursorPosition( mEventData->mPrimaryCursorPosition,
194 if( mEventData->mUpdateCursorHookPosition )
196 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
197 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
198 mEventData->mUpdateCursorHookPosition = false;
201 // Scroll first the text after delete ...
202 if( mEventData->mScrollAfterDelete )
204 ScrollTextToMatchCursor( cursorInfo );
207 // ... then, text can be scrolled to make the cursor visible.
208 if( mEventData->mScrollAfterUpdatePosition )
210 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
211 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
213 mEventData->mScrollAfterUpdatePosition = false;
214 mEventData->mScrollAfterDelete = false;
216 UpdateCursorPosition( cursorInfo );
218 mEventData->mDecoratorUpdated = true;
219 mEventData->mUpdateCursorPosition = false;
220 mEventData->mUpdateGrabHandlePosition = false;
224 CursorInfo leftHandleInfo;
225 CursorInfo rightHandleInfo;
227 if( mEventData->mUpdateHighlightBox )
229 GetCursorPosition( mEventData->mLeftSelectionPosition,
232 GetCursorPosition( mEventData->mRightSelectionPosition,
235 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
237 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
239 CursorInfo& infoLeft = leftHandleInfo;
241 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
242 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
244 CursorInfo& infoRight = rightHandleInfo;
246 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
247 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
251 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
253 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
254 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
259 if( mEventData->mUpdateLeftSelectionPosition )
261 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
265 mEventData->mDecoratorUpdated = true;
266 mEventData->mUpdateLeftSelectionPosition = false;
269 if( mEventData->mUpdateRightSelectionPosition )
271 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
275 mEventData->mDecoratorUpdated = true;
276 mEventData->mUpdateRightSelectionPosition = false;
279 if( mEventData->mUpdateHighlightBox )
281 RepositionSelectionHandles();
283 mEventData->mUpdateLeftSelectionPosition = false;
284 mEventData->mUpdateRightSelectionPosition = false;
285 mEventData->mUpdateHighlightBox = false;
286 mEventData->mIsLeftHandleSelected = false;
287 mEventData->mIsRightHandleSelected = false;
290 mEventData->mScrollAfterUpdatePosition = false;
293 if( mEventData->mUpdateInputStyle )
295 // Keep a copy of the current input style.
296 InputStyle currentInputStyle;
297 currentInputStyle.Copy( mEventData->mInputStyle );
299 // Set the default style first.
300 RetrieveDefaultInputStyle( mEventData->mInputStyle );
302 // Get the character index from the cursor index.
303 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
305 // Retrieve the style from the style runs stored in the logical model.
306 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
308 // Compare if the input style has changed.
309 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
311 if( hasInputStyleChanged )
313 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
314 // Queue the input style changed signal.
315 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
318 mEventData->mUpdateInputStyle = false;
321 mEventData->mEventQueue.clear();
323 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
325 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
326 mEventData->mDecoratorUpdated = false;
328 return decoratorUpdated;
331 void Controller::Impl::NotifyImfManager()
333 if( mEventData && mEventData->mImfManager )
335 CharacterIndex cursorPosition = GetLogicalCursorPosition();
337 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
339 // Update the cursor position by removing the initial white spaces.
340 if( cursorPosition < numberOfWhiteSpaces )
346 cursorPosition -= numberOfWhiteSpaces;
349 mEventData->mImfManager.SetCursorPosition( cursorPosition );
350 mEventData->mImfManager.NotifyCursorPosition();
354 void Controller::Impl::NotifyImfMultiLineStatus()
358 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
359 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
363 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
365 CharacterIndex cursorPosition = 0u;
369 if( ( EventData::SELECTING == mEventData->mState ) ||
370 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
372 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
376 cursorPosition = mEventData->mPrimaryCursorPosition;
380 return cursorPosition;
383 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
385 Length numberOfWhiteSpaces = 0u;
387 // Get the buffer to the text.
388 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
390 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
391 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
393 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
399 return numberOfWhiteSpaces;
402 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
404 // Get the total number of characters.
405 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
407 // Retrieve the text.
408 if( 0u != numberOfCharacters )
410 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
414 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
416 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
417 mTextUpdateInfo.mStartGlyphIndex = 0u;
418 mTextUpdateInfo.mStartLineIndex = 0u;
419 numberOfCharacters = 0u;
421 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
422 if( 0u == numberOfParagraphs )
424 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
425 numberOfCharacters = 0u;
427 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
429 // Nothing else to do if there are no paragraphs.
433 // Find the paragraphs to be updated.
434 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
435 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
437 // Text is being added at the end of the current text.
438 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
440 // Text is being added in a new paragraph after the last character of the text.
441 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
442 numberOfCharacters = 0u;
443 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
445 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
446 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
448 // Nothing else to do;
452 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
456 Length numberOfCharactersToUpdate = 0u;
457 if( mTextUpdateInfo.mFullRelayoutNeeded )
459 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
463 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
465 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
466 numberOfCharactersToUpdate,
467 paragraphsToBeUpdated );
470 if( 0u != paragraphsToBeUpdated.Count() )
472 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
473 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
474 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
476 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
477 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
479 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
480 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
481 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
482 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
484 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
485 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
487 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
495 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
496 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
499 void Controller::Impl::ClearFullModelData( OperationsMask operations )
501 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
503 mModel->mLogicalModel->mLineBreakInfo.Clear();
504 mModel->mLogicalModel->mParagraphInfo.Clear();
507 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
509 mModel->mLogicalModel->mLineBreakInfo.Clear();
512 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
514 mModel->mLogicalModel->mScriptRuns.Clear();
517 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
519 mModel->mLogicalModel->mFontRuns.Clear();
522 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
524 if( NO_OPERATION != ( BIDI_INFO & operations ) )
526 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
527 mModel->mLogicalModel->mCharacterDirections.Clear();
530 if( NO_OPERATION != ( REORDER & operations ) )
532 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
533 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
534 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
538 BidirectionalLineInfoRun& bidiLineInfo = *it;
540 free( bidiLineInfo.visualToLogicalMap );
541 bidiLineInfo.visualToLogicalMap = NULL;
543 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
547 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
549 mModel->mVisualModel->mGlyphs.Clear();
550 mModel->mVisualModel->mGlyphsToCharacters.Clear();
551 mModel->mVisualModel->mCharactersToGlyph.Clear();
552 mModel->mVisualModel->mCharactersPerGlyph.Clear();
553 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
554 mModel->mVisualModel->mGlyphPositions.Clear();
557 if( NO_OPERATION != ( LAYOUT & operations ) )
559 mModel->mVisualModel->mLines.Clear();
562 if( NO_OPERATION != ( COLOR & operations ) )
564 mModel->mVisualModel->mColorIndices.Clear();
568 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
570 const CharacterIndex endIndexPlusOne = endIndex + 1u;
572 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
574 // Clear the line break info.
575 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
577 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
578 lineBreakInfoBuffer + endIndexPlusOne );
580 // Clear the paragraphs.
581 ClearCharacterRuns( startIndex,
583 mModel->mLogicalModel->mParagraphInfo );
586 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
588 // Clear the word break info.
589 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
591 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
592 wordBreakInfoBuffer + endIndexPlusOne );
595 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
597 // Clear the scripts.
598 ClearCharacterRuns( startIndex,
600 mModel->mLogicalModel->mScriptRuns );
603 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
606 ClearCharacterRuns( startIndex,
608 mModel->mLogicalModel->mFontRuns );
611 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
613 if( NO_OPERATION != ( BIDI_INFO & operations ) )
615 // Clear the bidirectional paragraph info.
616 ClearCharacterRuns( startIndex,
618 mModel->mLogicalModel->mBidirectionalParagraphInfo );
620 // Clear the character's directions.
621 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
623 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
624 characterDirectionsBuffer + endIndexPlusOne );
627 if( NO_OPERATION != ( REORDER & operations ) )
629 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
630 uint32_t endRemoveIndex = startRemoveIndex;
631 ClearCharacterRuns( startIndex,
633 mModel->mLogicalModel->mBidirectionalLineInfo,
637 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
639 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
640 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
641 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
645 BidirectionalLineInfoRun& bidiLineInfo = *it;
647 free( bidiLineInfo.visualToLogicalMap );
648 bidiLineInfo.visualToLogicalMap = NULL;
651 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
652 bidirectionalLineInfoBuffer + endRemoveIndex );
657 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
659 const CharacterIndex endIndexPlusOne = endIndex + 1u;
660 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
662 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
663 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
664 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
666 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
667 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
669 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
671 // Update the character to glyph indices.
672 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
673 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
677 CharacterIndex& index = *it;
678 index -= numberOfGlyphsRemoved;
681 // Clear the character to glyph conversion table.
682 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
683 charactersToGlyphBuffer + endIndexPlusOne );
685 // Clear the glyphs per character table.
686 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
687 glyphsPerCharacterBuffer + endIndexPlusOne );
689 // Clear the glyphs buffer.
690 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
691 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
692 glyphsBuffer + endGlyphIndexPlusOne );
694 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
696 // Update the glyph to character indices.
697 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
698 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
702 CharacterIndex& index = *it;
703 index -= numberOfCharactersRemoved;
706 // Clear the glyphs to characters buffer.
707 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
708 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
710 // Clear the characters per glyph buffer.
711 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
712 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
713 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
715 // Clear the positions buffer.
716 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
717 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
718 positionsBuffer + endGlyphIndexPlusOne );
721 if( NO_OPERATION != ( LAYOUT & operations ) )
724 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
725 uint32_t endRemoveIndex = startRemoveIndex;
726 ClearCharacterRuns( startIndex,
728 mModel->mVisualModel->mLines,
732 // Will update the glyph runs.
733 startRemoveIndex = mModel->mVisualModel->mLines.Count();
734 endRemoveIndex = startRemoveIndex;
735 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
736 endGlyphIndexPlusOne - 1u,
737 mModel->mVisualModel->mLines,
741 // Set the line index from where to insert the new laid-out lines.
742 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
744 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
745 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
746 linesBuffer + endRemoveIndex );
749 if( NO_OPERATION != ( COLOR & operations ) )
751 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
753 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
754 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
755 colorIndexBuffer + endGlyphIndexPlusOne );
760 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
762 if( mTextUpdateInfo.mClearAll ||
763 ( ( 0u == startIndex ) &&
764 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
766 ClearFullModelData( operations );
770 // Clear the model data related with characters.
771 ClearCharacterModelData( startIndex, endIndex, operations );
773 // Clear the model data related with glyphs.
774 ClearGlyphModelData( startIndex, endIndex, operations );
777 // The estimated number of lines. Used to avoid reallocations when layouting.
778 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
780 mModel->mVisualModel->ClearCaches();
783 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
785 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
787 // Calculate the operations to be done.
788 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
790 if( NO_OPERATION == operations )
792 // Nothing to do if no operations are pending and required.
796 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
797 Vector<Character> displayCharacters;
798 bool useHiddenText = false;
799 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
801 mHiddenInput->Substitute( srcCharacters,displayCharacters );
802 useHiddenText = true;
805 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
806 const Length numberOfCharacters = utf32Characters.Count();
808 // Index to the first character of the first paragraph to be updated.
809 CharacterIndex startIndex = 0u;
810 // Number of characters of the paragraphs to be removed.
811 Length paragraphCharacters = 0u;
813 CalculateTextUpdateIndices( paragraphCharacters );
814 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
816 if( mTextUpdateInfo.mClearAll ||
817 ( 0u != paragraphCharacters ) )
819 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
822 mTextUpdateInfo.mClearAll = false;
824 // Whether the model is updated.
825 bool updated = false;
827 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
828 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
830 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
832 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
833 // calculate the bidirectional info for each 'paragraph'.
834 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
835 // is not shaped together).
836 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
838 SetLineBreakInfo( utf32Characters,
840 requestedNumberOfCharacters,
843 // Create the paragraph info.
844 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
845 requestedNumberOfCharacters );
849 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
850 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
852 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
853 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
855 SetWordBreakInfo( utf32Characters,
857 requestedNumberOfCharacters,
862 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
863 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
865 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
866 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
868 if( getScripts || validateFonts )
870 // Validates the fonts assigned by the application or assigns default ones.
871 // It makes sure all the characters are going to be rendered by the correct font.
872 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
876 // Retrieves the scripts used in the text.
877 multilanguageSupport.SetScripts( utf32Characters,
879 requestedNumberOfCharacters,
885 // Validate the fonts set through the mark-up string.
886 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
888 // Get the default font's description.
889 TextAbstraction::FontDescription defaultFontDescription;
890 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
892 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
894 // If the placeholder font is set specifically, only placeholder font is changed.
895 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
896 if( mEventData->mPlaceholderFont->sizeDefined )
898 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
901 else if( NULL != mFontDefaults )
903 // Set the normal font and the placeholder font.
904 defaultFontDescription = mFontDefaults->mFontDescription;
905 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
908 // Validates the fonts. If there is a character with no assigned font it sets a default one.
909 // After this call, fonts are validated.
910 multilanguageSupport.ValidateFonts( utf32Characters,
913 defaultFontDescription,
916 requestedNumberOfCharacters,
922 Vector<Character> mirroredUtf32Characters;
923 bool textMirrored = false;
924 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
925 if( NO_OPERATION != ( BIDI_INFO & operations ) )
927 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
928 bidirectionalInfo.Reserve( numberOfParagraphs );
930 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
931 SetBidirectionalInfo( utf32Characters,
935 requestedNumberOfCharacters,
938 if( 0u != bidirectionalInfo.Count() )
940 // Only set the character directions if there is right to left characters.
941 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
942 GetCharactersDirection( bidirectionalInfo,
945 requestedNumberOfCharacters,
948 // This paragraph has right to left text. Some characters may need to be mirrored.
949 // TODO: consider if the mirrored string can be stored as well.
951 textMirrored = GetMirroredText( utf32Characters,
955 requestedNumberOfCharacters,
956 mirroredUtf32Characters );
960 // There is no right to left characters. Clear the directions vector.
961 mModel->mLogicalModel->mCharacterDirections.Clear();
966 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
967 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
968 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
969 Vector<GlyphIndex> newParagraphGlyphs;
970 newParagraphGlyphs.Reserve( numberOfParagraphs );
972 const Length currentNumberOfGlyphs = glyphs.Count();
973 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
975 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
977 ShapeText( textToShape,
982 mTextUpdateInfo.mStartGlyphIndex,
983 requestedNumberOfCharacters,
985 glyphsToCharactersMap,
987 newParagraphGlyphs );
989 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
990 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
991 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
995 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
997 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
999 GlyphInfo* glyphsBuffer = glyphs.Begin();
1000 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1002 // Update the width and advance of all new paragraph characters.
1003 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1005 const GlyphIndex index = *it;
1006 GlyphInfo& glyph = *( glyphsBuffer + index );
1008 glyph.xBearing = 0.f;
1010 glyph.advance = 0.f;
1015 if( NO_OPERATION != ( COLOR & operations ) )
1017 // Set the color runs in glyphs.
1018 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1019 mModel->mVisualModel->mCharactersToGlyph,
1020 mModel->mVisualModel->mGlyphsPerCharacter,
1022 mTextUpdateInfo.mStartGlyphIndex,
1023 requestedNumberOfCharacters,
1024 mModel->mVisualModel->mColors,
1025 mModel->mVisualModel->mColorIndices );
1030 if( ( NULL != mEventData ) &&
1031 mEventData->mPreEditFlag &&
1032 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1034 // Add the underline for the pre-edit text.
1035 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1036 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1038 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1039 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1040 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1041 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1043 GlyphRun underlineRun;
1044 underlineRun.glyphIndex = glyphStart;
1045 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1047 // TODO: At the moment the underline runs are only for pre-edit.
1048 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1051 // The estimated number of lines. Used to avoid reallocations when layouting.
1052 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1054 // Set the previous number of characters for the next time the text is updated.
1055 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1060 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1062 // Sets the default text's color.
1063 inputStyle.textColor = mTextColor;
1064 inputStyle.isDefaultColor = true;
1066 inputStyle.familyName.clear();
1067 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1068 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1069 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1070 inputStyle.size = 0.f;
1072 inputStyle.lineSpacing = 0.f;
1074 inputStyle.underlineProperties.clear();
1075 inputStyle.shadowProperties.clear();
1076 inputStyle.embossProperties.clear();
1077 inputStyle.outlineProperties.clear();
1079 inputStyle.isFamilyDefined = false;
1080 inputStyle.isWeightDefined = false;
1081 inputStyle.isWidthDefined = false;
1082 inputStyle.isSlantDefined = false;
1083 inputStyle.isSizeDefined = false;
1085 inputStyle.isLineSpacingDefined = false;
1087 inputStyle.isUnderlineDefined = false;
1088 inputStyle.isShadowDefined = false;
1089 inputStyle.isEmbossDefined = false;
1090 inputStyle.isOutlineDefined = false;
1092 // Sets the default font's family name, weight, width, slant and size.
1095 if( mFontDefaults->familyDefined )
1097 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1098 inputStyle.isFamilyDefined = true;
1101 if( mFontDefaults->weightDefined )
1103 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1104 inputStyle.isWeightDefined = true;
1107 if( mFontDefaults->widthDefined )
1109 inputStyle.width = mFontDefaults->mFontDescription.width;
1110 inputStyle.isWidthDefined = true;
1113 if( mFontDefaults->slantDefined )
1115 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1116 inputStyle.isSlantDefined = true;
1119 if( mFontDefaults->sizeDefined )
1121 inputStyle.size = mFontDefaults->mDefaultPointSize;
1122 inputStyle.isSizeDefined = true;
1127 float Controller::Impl::GetDefaultFontLineHeight()
1129 FontId defaultFontId = 0u;
1130 if( NULL == mFontDefaults )
1132 TextAbstraction::FontDescription fontDescription;
1133 defaultFontId = mFontClient.GetFontId( fontDescription );
1137 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1140 Text::FontMetrics fontMetrics;
1141 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1143 return( fontMetrics.ascender - fontMetrics.descender );
1146 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1148 if( NULL == mEventData || !IsShowingRealText() )
1150 // Nothing to do if there is no text input.
1154 int keyCode = event.p1.mInt;
1155 bool isShiftModifier = event.p2.mBool;
1157 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1159 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1161 if( mEventData->mPrimaryCursorPosition > 0u )
1163 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1165 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1169 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1173 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1175 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1177 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1179 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1183 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1187 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1189 // Ignore Shift-Up for text selection for now.
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 );
1200 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1202 // Retrieve the cursor position info.
1203 CursorInfo cursorInfo;
1204 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1207 // Get the line above.
1208 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1210 // Get the next hit 'y' point.
1211 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1213 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1214 bool matchedCharacter = false;
1215 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1216 mModel->mLogicalModel,
1218 mEventData->mCursorHookPositionX,
1220 CharacterHitTest::TAP,
1223 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1225 // Ignore Shift-Down for text selection for now.
1227 // Get first the line index of the current cursor position index.
1228 CharacterIndex characterIndex = 0u;
1230 if( mEventData->mPrimaryCursorPosition > 0u )
1232 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1235 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1237 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1239 // Retrieve the cursor position info.
1240 CursorInfo cursorInfo;
1241 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1244 // Get the line below.
1245 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1247 // Get the next hit 'y' point.
1248 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1250 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1251 bool matchedCharacter = false;
1252 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1253 mModel->mLogicalModel,
1255 mEventData->mCursorHookPositionX,
1257 CharacterHitTest::TAP,
1262 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1264 // Update selection position after moving the cursor
1265 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1266 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1269 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1271 // Handle text selection
1272 bool selecting = false;
1274 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1276 // Shift-Left/Right to select the text
1277 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1278 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1280 mEventData->mRightSelectionPosition += cursorPositionDelta;
1284 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1286 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1292 // Notify the cursor position to the imf manager.
1293 if( mEventData->mImfManager )
1295 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1296 mEventData->mImfManager.NotifyCursorPosition();
1299 ChangeState( EventData::SELECTING );
1301 mEventData->mUpdateLeftSelectionPosition = true;
1302 mEventData->mUpdateRightSelectionPosition = true;
1303 mEventData->mUpdateGrabHandlePosition = true;
1304 mEventData->mUpdateHighlightBox = true;
1306 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1307 if( mEventData->mGrabHandlePopupEnabled )
1309 mEventData->mDecorator->SetPopupActive( false );
1315 // Handle normal cursor move
1316 ChangeState( EventData::EDITING );
1317 mEventData->mUpdateCursorPosition = true;
1320 mEventData->mUpdateInputStyle = true;
1321 mEventData->mScrollAfterUpdatePosition = true;
1324 void Controller::Impl::OnTapEvent( const Event& event )
1326 if( NULL != mEventData )
1328 const unsigned int tapCount = event.p1.mUint;
1330 if( 1u == tapCount )
1332 if( IsShowingRealText() )
1334 // Convert from control's coords to text's coords.
1335 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1336 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1338 // Keep the tap 'x' position. Used to move the cursor.
1339 mEventData->mCursorHookPositionX = xPosition;
1341 // Whether to touch point hits on a glyph.
1342 bool matchedCharacter = false;
1343 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1344 mModel->mLogicalModel,
1348 CharacterHitTest::TAP,
1351 // When the cursor position is changing, delay cursor blinking
1352 mEventData->mDecorator->DelayCursorBlink();
1356 mEventData->mPrimaryCursorPosition = 0u;
1359 // Update selection position after tapping
1360 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1361 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1363 mEventData->mUpdateCursorPosition = true;
1364 mEventData->mUpdateGrabHandlePosition = true;
1365 mEventData->mScrollAfterUpdatePosition = true;
1366 mEventData->mUpdateInputStyle = true;
1368 // Notify the cursor position to the imf manager.
1369 if( mEventData->mImfManager )
1371 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1372 mEventData->mImfManager.NotifyCursorPosition();
1375 else if( 2u == tapCount )
1377 if( mEventData->mSelectionEnabled )
1379 // Convert from control's coords to text's coords.
1380 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1381 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1383 // Calculates the logical position from the x,y coords.
1384 RepositionSelectionHandles( xPosition,
1386 mEventData->mDoubleTapAction );
1392 void Controller::Impl::OnPanEvent( const Event& event )
1394 if( NULL == mEventData )
1396 // Nothing to do if there is no text input.
1400 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1401 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1403 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1405 // Nothing to do if scrolling is not enabled.
1409 const int state = event.p1.mInt;
1413 case Gesture::Started:
1415 // Will remove the cursor, handles or text's popup, ...
1416 ChangeState( EventData::TEXT_PANNING );
1419 case Gesture::Continuing:
1421 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1422 const Vector2 currentScroll = mModel->mScrollPosition;
1424 if( isHorizontalScrollEnabled )
1426 const float displacementX = event.p2.mFloat;
1427 mModel->mScrollPosition.x += displacementX;
1429 ClampHorizontalScroll( layoutSize );
1432 if( isVerticalScrollEnabled )
1434 const float displacementY = event.p3.mFloat;
1435 mModel->mScrollPosition.y += displacementY;
1437 ClampVerticalScroll( layoutSize );
1440 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1443 case Gesture::Finished:
1444 case Gesture::Cancelled: // FALLTHROUGH
1446 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1447 ChangeState( mEventData->mPreviousState );
1455 void Controller::Impl::OnLongPressEvent( const Event& event )
1457 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1459 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1461 ChangeState( EventData::EDITING_WITH_POPUP );
1462 mEventData->mDecoratorUpdated = true;
1463 mEventData->mUpdateInputStyle = true;
1467 if( mEventData->mSelectionEnabled )
1469 // Convert from control's coords to text's coords.
1470 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1471 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1473 // Calculates the logical position from the x,y coords.
1474 RepositionSelectionHandles( xPosition,
1476 mEventData->mLongPressAction );
1481 void Controller::Impl::OnHandleEvent( const Event& event )
1483 if( NULL == mEventData )
1485 // Nothing to do if there is no text input.
1489 const unsigned int state = event.p1.mUint;
1490 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1491 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1493 if( HANDLE_PRESSED == state )
1495 // Convert from decorator's coords to text's coords.
1496 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1497 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1499 // Need to calculate the handle's new position.
1500 bool matchedCharacter = false;
1501 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1502 mModel->mLogicalModel,
1506 CharacterHitTest::SCROLL,
1509 if( Event::GRAB_HANDLE_EVENT == event.type )
1511 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1513 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1515 // Updates the cursor position if the handle's new position is different than the current one.
1516 mEventData->mUpdateCursorPosition = true;
1517 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1518 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1519 mEventData->mPrimaryCursorPosition = handleNewPosition;
1522 // 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.
1523 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1525 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1527 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1529 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1530 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1532 // Updates the highlight box if the handle's new position is different than the current one.
1533 mEventData->mUpdateHighlightBox = true;
1534 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1535 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1536 mEventData->mLeftSelectionPosition = handleNewPosition;
1539 // 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.
1540 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1542 // Will define the order to scroll the text to match the handle position.
1543 mEventData->mIsLeftHandleSelected = true;
1544 mEventData->mIsRightHandleSelected = false;
1546 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1548 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1550 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1551 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1553 // Updates the highlight box if the handle's new position is different than the current one.
1554 mEventData->mUpdateHighlightBox = true;
1555 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1556 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1557 mEventData->mRightSelectionPosition = handleNewPosition;
1560 // 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.
1561 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1563 // Will define the order to scroll the text to match the handle position.
1564 mEventData->mIsLeftHandleSelected = false;
1565 mEventData->mIsRightHandleSelected = true;
1567 } // end ( HANDLE_PRESSED == state )
1568 else if( ( HANDLE_RELEASED == state ) ||
1569 handleStopScrolling )
1571 CharacterIndex handlePosition = 0u;
1572 if( handleStopScrolling || isSmoothHandlePanEnabled )
1574 // Convert from decorator's coords to text's coords.
1575 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1576 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1578 bool matchedCharacter = false;
1579 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1580 mModel->mLogicalModel,
1584 CharacterHitTest::SCROLL,
1588 if( Event::GRAB_HANDLE_EVENT == event.type )
1590 mEventData->mUpdateCursorPosition = true;
1591 mEventData->mUpdateGrabHandlePosition = true;
1592 mEventData->mUpdateInputStyle = true;
1594 if( !IsClipboardEmpty() )
1596 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1599 if( handleStopScrolling || isSmoothHandlePanEnabled )
1601 mEventData->mScrollAfterUpdatePosition = true;
1602 mEventData->mPrimaryCursorPosition = handlePosition;
1605 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1607 ChangeState( EventData::SELECTING );
1609 mEventData->mUpdateHighlightBox = true;
1610 mEventData->mUpdateLeftSelectionPosition = true;
1611 mEventData->mUpdateRightSelectionPosition = true;
1613 if( handleStopScrolling || isSmoothHandlePanEnabled )
1615 mEventData->mScrollAfterUpdatePosition = true;
1617 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1618 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1620 mEventData->mLeftSelectionPosition = handlePosition;
1624 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1626 ChangeState( EventData::SELECTING );
1628 mEventData->mUpdateHighlightBox = true;
1629 mEventData->mUpdateRightSelectionPosition = true;
1630 mEventData->mUpdateLeftSelectionPosition = true;
1632 if( handleStopScrolling || isSmoothHandlePanEnabled )
1634 mEventData->mScrollAfterUpdatePosition = true;
1635 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1636 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1638 mEventData->mRightSelectionPosition = handlePosition;
1643 mEventData->mDecoratorUpdated = true;
1644 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1645 else if( HANDLE_SCROLLING == state )
1647 const float xSpeed = event.p2.mFloat;
1648 const float ySpeed = event.p3.mFloat;
1649 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1650 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1652 mModel->mScrollPosition.x += xSpeed;
1653 mModel->mScrollPosition.y += ySpeed;
1655 ClampHorizontalScroll( layoutSize );
1656 ClampVerticalScroll( layoutSize );
1658 bool endOfScroll = false;
1659 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1661 // Notify the decorator there is no more text to scroll.
1662 // The decorator won't send more scroll events.
1663 mEventData->mDecorator->NotifyEndOfScroll();
1664 // Still need to set the position of the handle.
1668 // Set the position of the handle.
1669 const bool scrollRightDirection = xSpeed > 0.f;
1670 const bool scrollBottomDirection = ySpeed > 0.f;
1671 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1672 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1674 if( Event::GRAB_HANDLE_EVENT == event.type )
1676 ChangeState( EventData::GRAB_HANDLE_PANNING );
1678 // Get the grab handle position in decorator coords.
1679 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1681 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1683 // Position the grag handle close to either the left or right edge.
1684 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1687 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1689 position.x = mEventData->mCursorHookPositionX;
1691 // Position the grag handle close to either the top or bottom edge.
1692 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1695 // Get the new handle position.
1696 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1697 bool matchedCharacter = false;
1698 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1699 mModel->mLogicalModel,
1701 position.x - mModel->mScrollPosition.x,
1702 position.y - mModel->mScrollPosition.y,
1703 CharacterHitTest::SCROLL,
1706 if( mEventData->mPrimaryCursorPosition != handlePosition )
1708 mEventData->mUpdateCursorPosition = true;
1709 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1710 mEventData->mScrollAfterUpdatePosition = true;
1711 mEventData->mPrimaryCursorPosition = handlePosition;
1713 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1715 // Updates the decorator if the soft handle panning is enabled.
1716 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1718 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1720 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1722 // Get the selection handle position in decorator coords.
1723 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1725 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1727 // Position the selection handle close to either the left or right edge.
1728 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1731 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1733 position.x = mEventData->mCursorHookPositionX;
1735 // Position the grag handle close to either the top or bottom edge.
1736 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1739 // Get the new handle position.
1740 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1741 bool matchedCharacter = false;
1742 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1743 mModel->mLogicalModel,
1745 position.x - mModel->mScrollPosition.x,
1746 position.y - mModel->mScrollPosition.y,
1747 CharacterHitTest::SCROLL,
1750 if( leftSelectionHandleEvent )
1752 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1754 if( differentHandles || endOfScroll )
1756 mEventData->mUpdateHighlightBox = true;
1757 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1758 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1759 mEventData->mLeftSelectionPosition = handlePosition;
1764 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1765 if( differentHandles || endOfScroll )
1767 mEventData->mUpdateHighlightBox = true;
1768 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1769 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1770 mEventData->mRightSelectionPosition = handlePosition;
1774 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1776 RepositionSelectionHandles();
1778 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1781 mEventData->mDecoratorUpdated = true;
1782 } // end ( HANDLE_SCROLLING == state )
1785 void Controller::Impl::OnSelectEvent( const Event& event )
1787 if( NULL == mEventData )
1789 // Nothing to do if there is no text.
1793 if( mEventData->mSelectionEnabled )
1795 // Convert from control's coords to text's coords.
1796 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1797 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1799 // Calculates the logical position from the x,y coords.
1800 RepositionSelectionHandles( xPosition,
1802 Controller::NoTextTap::HIGHLIGHT );
1806 void Controller::Impl::OnSelectAllEvent()
1808 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1810 if( NULL == mEventData )
1812 // Nothing to do if there is no text.
1816 if( mEventData->mSelectionEnabled )
1818 ChangeState( EventData::SELECTING );
1820 mEventData->mLeftSelectionPosition = 0u;
1821 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1823 mEventData->mScrollAfterUpdatePosition = true;
1824 mEventData->mUpdateLeftSelectionPosition = true;
1825 mEventData->mUpdateRightSelectionPosition = true;
1826 mEventData->mUpdateHighlightBox = true;
1830 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1832 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1834 // Nothing to select if handles are in the same place.
1835 selectedText.clear();
1839 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1841 //Get start and end position of selection
1842 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1843 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1845 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1846 const Length numberOfCharacters = utf32Characters.Count();
1848 // Validate the start and end selection points
1849 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1851 //Get text as a UTF8 string
1852 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1854 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1856 // Keep a copy of the current input style.
1857 InputStyle currentInputStyle;
1858 currentInputStyle.Copy( mEventData->mInputStyle );
1860 // Set as input style the style of the first deleted character.
1861 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1863 // Compare if the input style has changed.
1864 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1866 if( hasInputStyleChanged )
1868 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1869 // Queue the input style changed signal.
1870 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1873 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1875 // Mark the paragraphs to be updated.
1876 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1878 mTextUpdateInfo.mCharacterIndex = 0;
1879 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1880 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1881 mTextUpdateInfo.mClearAll = true;
1885 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1886 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1889 // Delete text between handles
1890 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1891 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1892 utf32Characters.Erase( first, last );
1894 // Will show the cursor at the first character of the selection.
1895 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1899 // Will show the cursor at the last character of the selection.
1900 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1903 mEventData->mDecoratorUpdated = true;
1907 void Controller::Impl::ShowClipboard()
1911 mClipboard.ShowClipboard();
1915 void Controller::Impl::HideClipboard()
1917 if( mClipboard && mClipboardHideEnabled )
1919 mClipboard.HideClipboard();
1923 void Controller::Impl::SetClipboardHideEnable(bool enable)
1925 mClipboardHideEnabled = enable;
1928 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1930 //Send string to clipboard
1931 return ( mClipboard && mClipboard.SetItem( source ) );
1934 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1936 std::string selectedText;
1937 RetrieveSelection( selectedText, deleteAfterSending );
1938 CopyStringToClipboard( selectedText );
1939 ChangeState( EventData::EDITING );
1942 void Controller::Impl::RequestGetTextFromClipboard()
1946 mClipboard.RequestItem();
1950 void Controller::Impl::RepositionSelectionHandles()
1952 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1953 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1955 if( selectionStart == selectionEnd )
1957 // Nothing to select if handles are in the same place.
1958 // So, deactive Highlight box.
1959 mEventData->mDecorator->SetHighlightActive( false );
1963 mEventData->mDecorator->ClearHighlights();
1965 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1966 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1967 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1968 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1969 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1970 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1971 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1973 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1974 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1975 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1977 // Swap the indices if the start is greater than the end.
1978 const bool indicesSwapped = selectionStart > selectionEnd;
1980 // Tell the decorator to flip the selection handles if needed.
1981 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1983 if( indicesSwapped )
1985 std::swap( selectionStart, selectionEnd );
1988 // Get the indices to the first and last selected glyphs.
1989 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1990 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1991 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1992 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1994 // Get the lines where the glyphs are laid-out.
1995 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1997 LineIndex lineIndex = 0u;
1998 Length numberOfLines = 0u;
1999 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2000 1u + glyphEnd - glyphStart,
2003 const LineIndex firstLineIndex = lineIndex;
2005 // Create the structure to store some selection box info.
2006 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2007 selectionBoxLinesInfo.Resize( numberOfLines );
2009 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2010 selectionBoxInfo->minX = MAX_FLOAT;
2011 selectionBoxInfo->maxX = MIN_FLOAT;
2013 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2014 float minHighlightX = std::numeric_limits<float>::max();
2015 float maxHighlightX = std::numeric_limits<float>::min();
2017 Vector2 highLightPosition; // The highlight position in decorator's coords.
2019 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2021 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2022 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2025 // Transform to decorator's (control) coords.
2026 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2028 lineRun += firstLineIndex;
2030 // The line height is the addition of the line ascender and the line descender.
2031 // However, the line descender has a negative value, hence the subtraction.
2032 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2034 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2036 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2037 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2038 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2040 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2041 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2042 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2044 // The number of quads of the selection box.
2045 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2046 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2048 // Count the actual number of quads.
2049 unsigned int actualNumberOfQuads = 0u;
2052 // Traverse the glyphs.
2053 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2055 const GlyphInfo& glyph = *( glyphsBuffer + index );
2056 const Vector2& position = *( positionsBuffer + index );
2058 if( splitStartGlyph )
2060 // 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.
2062 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2063 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2064 // Get the direction of the character.
2065 CharacterDirection isCurrentRightToLeft = false;
2066 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2068 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2071 // The end point could be in the middle of the ligature.
2072 // Calculate the number of characters selected.
2073 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2075 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2076 quad.y = selectionBoxInfo->lineOffset;
2077 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2078 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2080 // Store the min and max 'x' for each line.
2081 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2082 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2084 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2085 ++actualNumberOfQuads;
2087 splitStartGlyph = false;
2091 if( splitEndGlyph && ( index == glyphEnd ) )
2093 // 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.
2095 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2096 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2097 // Get the direction of the character.
2098 CharacterDirection isCurrentRightToLeft = false;
2099 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2101 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2104 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2106 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2107 quad.y = selectionBoxInfo->lineOffset;
2108 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2109 quad.w = quad.y + selectionBoxInfo->lineHeight;
2111 // Store the min and max 'x' for each line.
2112 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2113 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2115 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2117 ++actualNumberOfQuads;
2119 splitEndGlyph = false;
2123 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2124 quad.y = selectionBoxInfo->lineOffset;
2125 quad.z = quad.x + glyph.advance;
2126 quad.w = quad.y + selectionBoxInfo->lineHeight;
2128 // Store the min and max 'x' for each line.
2129 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2130 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2132 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2134 ++actualNumberOfQuads;
2136 // Whether to retrieve the next line.
2137 if( index == lastGlyphOfLine )
2140 if( lineIndex < firstLineIndex + numberOfLines )
2142 // Retrieve the next line.
2145 // Get the last glyph of the new line.
2146 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2148 // Keep the offset and height of the current selection box.
2149 const float currentLineOffset = selectionBoxInfo->lineOffset;
2150 const float currentLineHeight = selectionBoxInfo->lineHeight;
2152 // Get the selection box info for the next line.
2155 selectionBoxInfo->minX = MAX_FLOAT;
2156 selectionBoxInfo->maxX = MIN_FLOAT;
2158 // Update the line's vertical offset.
2159 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2161 // The line height is the addition of the line ascender and the line descender.
2162 // However, the line descender has a negative value, hence the subtraction.
2163 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2168 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2169 // The final width is calculated after 'boxifying' the selection.
2170 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2171 endIt = selectionBoxLinesInfo.End();
2175 const SelectionBoxInfo& info = *it;
2177 // Update the size of the highlighted text.
2178 highLightSize.height += info.lineHeight;
2179 minHighlightX = std::min( minHighlightX, info.minX );
2180 maxHighlightX = std::max( maxHighlightX, info.maxX );
2183 // Add extra geometry to 'boxify' the selection.
2185 if( 1u < numberOfLines )
2187 // Boxify the first line.
2188 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2189 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2191 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2192 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2197 quad.y = firstSelectionBoxLineInfo.lineOffset;
2198 quad.z = firstSelectionBoxLineInfo.minX;
2199 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2201 // Boxify at the beginning of the line.
2202 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2204 ++actualNumberOfQuads;
2206 // Update the size of the highlighted text.
2207 minHighlightX = 0.f;
2212 quad.x = firstSelectionBoxLineInfo.maxX;
2213 quad.y = firstSelectionBoxLineInfo.lineOffset;
2214 quad.z = mModel->mVisualModel->mControlSize.width;
2215 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2217 // Boxify at the end of the line.
2218 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2220 ++actualNumberOfQuads;
2222 // Update the size of the highlighted text.
2223 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2226 // Boxify the central lines.
2227 if( 2u < numberOfLines )
2229 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2230 endIt = selectionBoxLinesInfo.End() - 1u;
2234 const SelectionBoxInfo& info = *it;
2237 quad.y = info.lineOffset;
2239 quad.w = info.lineOffset + info.lineHeight;
2241 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2243 ++actualNumberOfQuads;
2246 quad.y = info.lineOffset;
2247 quad.z = mModel->mVisualModel->mControlSize.width;
2248 quad.w = info.lineOffset + info.lineHeight;
2250 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2252 ++actualNumberOfQuads;
2255 // Update the size of the highlighted text.
2256 minHighlightX = 0.f;
2257 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2260 // Boxify the last line.
2261 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2262 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2264 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2265 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2270 quad.y = lastSelectionBoxLineInfo.lineOffset;
2271 quad.z = lastSelectionBoxLineInfo.minX;
2272 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2274 // Boxify at the beginning of the line.
2275 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2277 ++actualNumberOfQuads;
2279 // Update the size of the highlighted text.
2280 minHighlightX = 0.f;
2285 quad.x = lastSelectionBoxLineInfo.maxX;
2286 quad.y = lastSelectionBoxLineInfo.lineOffset;
2287 quad.z = mModel->mVisualModel->mControlSize.width;
2288 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2290 // Boxify at the end of the line.
2291 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2293 ++actualNumberOfQuads;
2295 // Update the size of the highlighted text.
2296 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2300 // Set the actual number of quads.
2301 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2303 // Sets the highlight's size and position. In decorator's coords.
2304 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2305 highLightSize.width = maxHighlightX - minHighlightX;
2307 highLightPosition.x = minHighlightX;
2308 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2309 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2311 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2313 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2315 CursorInfo primaryCursorInfo;
2316 GetCursorPosition( mEventData->mLeftSelectionPosition,
2317 primaryCursorInfo );
2319 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2321 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2323 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2324 primaryCursorInfo.lineHeight );
2326 CursorInfo secondaryCursorInfo;
2327 GetCursorPosition( mEventData->mRightSelectionPosition,
2328 secondaryCursorInfo );
2330 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2332 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2333 secondaryPosition.x,
2334 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2335 secondaryCursorInfo.lineHeight );
2338 // Set the flag to update the decorator.
2339 mEventData->mDecoratorUpdated = true;
2342 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2344 if( NULL == mEventData )
2346 // Nothing to do if there is no text input.
2350 if( IsShowingPlaceholderText() )
2352 // Nothing to do if there is the place-holder text.
2356 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2357 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2358 if( ( 0 == numberOfGlyphs ) ||
2359 ( 0 == numberOfLines ) )
2361 // Nothing to do if there is no text.
2365 // Find which word was selected
2366 CharacterIndex selectionStart( 0 );
2367 CharacterIndex selectionEnd( 0 );
2368 CharacterIndex noTextHitIndex( 0 );
2369 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2370 mModel->mLogicalModel,
2377 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2379 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2381 ChangeState( EventData::SELECTING );
2383 mEventData->mLeftSelectionPosition = selectionStart;
2384 mEventData->mRightSelectionPosition = selectionEnd;
2386 mEventData->mUpdateLeftSelectionPosition = true;
2387 mEventData->mUpdateRightSelectionPosition = true;
2388 mEventData->mUpdateHighlightBox = true;
2390 // It may happen an IMF commit event arrives before the selection event
2391 // if the IMF manager is in pre-edit state. The commit event will set the
2392 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2393 // to false, the highlight box won't be updated.
2394 mEventData->mUpdateCursorPosition = false;
2396 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2398 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2399 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2401 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2403 // Nothing to select. i.e. a white space, out of bounds
2404 ChangeState( EventData::EDITING_WITH_POPUP );
2406 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2408 mEventData->mUpdateCursorPosition = true;
2409 mEventData->mUpdateGrabHandlePosition = true;
2410 mEventData->mScrollAfterUpdatePosition = true;
2411 mEventData->mUpdateInputStyle = true;
2413 else if( Controller::NoTextTap::NO_ACTION == action )
2415 // Nothing to select. i.e. a white space, out of bounds
2416 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2418 mEventData->mUpdateCursorPosition = true;
2419 mEventData->mUpdateGrabHandlePosition = true;
2420 mEventData->mScrollAfterUpdatePosition = true;
2421 mEventData->mUpdateInputStyle = true;
2425 void Controller::Impl::SetPopupButtons()
2428 * Sets the Popup buttons to be shown depending on State.
2430 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2432 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2435 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2437 if( EventData::SELECTING == mEventData->mState )
2439 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2441 if( !IsClipboardEmpty() )
2443 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2444 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2447 if( !mEventData->mAllTextSelected )
2449 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2452 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2454 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2456 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2459 if( !IsClipboardEmpty() )
2461 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2462 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2465 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2467 if ( !IsClipboardEmpty() )
2469 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2470 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2474 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2477 void Controller::Impl::ChangeState( EventData::State newState )
2479 if( NULL == mEventData )
2481 // Nothing to do if there is no text input.
2485 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2487 if( mEventData->mState != newState )
2489 mEventData->mPreviousState = mEventData->mState;
2490 mEventData->mState = newState;
2492 switch( mEventData->mState )
2494 case EventData::INACTIVE:
2496 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2497 mEventData->mDecorator->StopCursorBlink();
2498 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2499 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2500 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2501 mEventData->mDecorator->SetHighlightActive( false );
2502 mEventData->mDecorator->SetPopupActive( false );
2503 mEventData->mDecoratorUpdated = true;
2506 case EventData::INTERRUPTED:
2508 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2509 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2510 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2511 mEventData->mDecorator->SetHighlightActive( false );
2512 mEventData->mDecorator->SetPopupActive( false );
2513 mEventData->mDecoratorUpdated = true;
2516 case EventData::SELECTING:
2518 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2519 mEventData->mDecorator->StopCursorBlink();
2520 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2521 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2522 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2523 mEventData->mDecorator->SetHighlightActive( true );
2524 if( mEventData->mGrabHandlePopupEnabled )
2527 mEventData->mDecorator->SetPopupActive( true );
2529 mEventData->mDecoratorUpdated = true;
2532 case EventData::EDITING:
2534 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2535 if( mEventData->mCursorBlinkEnabled )
2537 mEventData->mDecorator->StartCursorBlink();
2539 // Grab handle is not shown until a tap is received whilst EDITING
2540 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2541 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2542 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2543 mEventData->mDecorator->SetHighlightActive( false );
2544 if( mEventData->mGrabHandlePopupEnabled )
2546 mEventData->mDecorator->SetPopupActive( false );
2548 mEventData->mDecoratorUpdated = true;
2551 case EventData::EDITING_WITH_POPUP:
2553 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2555 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2556 if( mEventData->mCursorBlinkEnabled )
2558 mEventData->mDecorator->StartCursorBlink();
2560 if( mEventData->mSelectionEnabled )
2562 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2563 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2564 mEventData->mDecorator->SetHighlightActive( false );
2568 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2570 if( mEventData->mGrabHandlePopupEnabled )
2573 mEventData->mDecorator->SetPopupActive( true );
2575 mEventData->mDecoratorUpdated = true;
2578 case EventData::EDITING_WITH_GRAB_HANDLE:
2580 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2582 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2583 if( mEventData->mCursorBlinkEnabled )
2585 mEventData->mDecorator->StartCursorBlink();
2587 // Grab handle is not shown until a tap is received whilst EDITING
2588 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2589 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2590 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2591 mEventData->mDecorator->SetHighlightActive( false );
2592 if( mEventData->mGrabHandlePopupEnabled )
2594 mEventData->mDecorator->SetPopupActive( false );
2596 mEventData->mDecoratorUpdated = true;
2599 case EventData::SELECTION_HANDLE_PANNING:
2601 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2602 mEventData->mDecorator->StopCursorBlink();
2603 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2604 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2605 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2606 mEventData->mDecorator->SetHighlightActive( true );
2607 if( mEventData->mGrabHandlePopupEnabled )
2609 mEventData->mDecorator->SetPopupActive( false );
2611 mEventData->mDecoratorUpdated = true;
2614 case EventData::GRAB_HANDLE_PANNING:
2616 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2618 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2619 if( mEventData->mCursorBlinkEnabled )
2621 mEventData->mDecorator->StartCursorBlink();
2623 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2624 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2625 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2626 mEventData->mDecorator->SetHighlightActive( false );
2627 if( mEventData->mGrabHandlePopupEnabled )
2629 mEventData->mDecorator->SetPopupActive( false );
2631 mEventData->mDecoratorUpdated = true;
2634 case EventData::EDITING_WITH_PASTE_POPUP:
2636 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2638 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2639 if( mEventData->mCursorBlinkEnabled )
2641 mEventData->mDecorator->StartCursorBlink();
2644 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2645 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2646 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2647 mEventData->mDecorator->SetHighlightActive( false );
2649 if( mEventData->mGrabHandlePopupEnabled )
2652 mEventData->mDecorator->SetPopupActive( true );
2654 mEventData->mDecoratorUpdated = true;
2657 case EventData::TEXT_PANNING:
2659 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2660 mEventData->mDecorator->StopCursorBlink();
2661 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2662 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2663 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2665 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2666 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2667 mEventData->mDecorator->SetHighlightActive( true );
2670 if( mEventData->mGrabHandlePopupEnabled )
2672 mEventData->mDecorator->SetPopupActive( false );
2675 mEventData->mDecoratorUpdated = true;
2682 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2683 CursorInfo& cursorInfo )
2685 if( !IsShowingRealText() )
2687 // Do not want to use the place-holder text to set the cursor position.
2689 // Use the line's height of the font's family set to set the cursor's size.
2690 // If there is no font's family set, use the default font.
2691 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2693 cursorInfo.lineOffset = 0.f;
2694 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2695 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2697 switch( mModel->mHorizontalAlignment )
2699 case Text::HorizontalAlignment::BEGIN :
2701 cursorInfo.primaryPosition.x = 0.f;
2704 case Text::HorizontalAlignment::CENTER:
2706 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2709 case Text::HorizontalAlignment::END:
2711 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2716 // Nothing else to do.
2720 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2721 GetCursorPositionParameters parameters;
2722 parameters.visualModel = mModel->mVisualModel;
2723 parameters.logicalModel = mModel->mLogicalModel;
2724 parameters.metrics = mMetrics;
2725 parameters.logical = logical;
2726 parameters.isMultiline = isMultiLine;
2728 Text::GetCursorPosition( parameters,
2731 // Adds Outline offset.
2732 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2733 cursorInfo.primaryPosition.x += outlineWidth;
2734 cursorInfo.primaryPosition.y += outlineWidth;
2735 cursorInfo.secondaryPosition.x += outlineWidth;
2736 cursorInfo.secondaryPosition.y += outlineWidth;
2740 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2742 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2743 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2745 if( 0.f > cursorInfo.primaryPosition.x )
2747 cursorInfo.primaryPosition.x = 0.f;
2750 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2751 if( cursorInfo.primaryPosition.x > edgeWidth )
2753 cursorInfo.primaryPosition.x = edgeWidth;
2758 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2760 if( NULL == mEventData )
2762 // Nothing to do if there is no text input.
2766 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2768 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2769 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2771 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2772 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2774 if( numberOfCharacters > 1u )
2776 const Script script = mModel->mLogicalModel->GetScript( index );
2777 if( HasLigatureMustBreak( script ) )
2779 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2780 numberOfCharacters = 1u;
2785 while( 0u == numberOfCharacters )
2788 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2792 if( index < mEventData->mPrimaryCursorPosition )
2794 cursorIndex -= numberOfCharacters;
2798 cursorIndex += numberOfCharacters;
2801 // Will update the cursor hook position.
2802 mEventData->mUpdateCursorHookPosition = true;
2807 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2809 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2810 if( NULL == mEventData )
2812 // Nothing to do if there is no text input.
2813 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2817 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2819 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2821 // Sets the cursor position.
2822 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2825 cursorInfo.primaryCursorHeight,
2826 cursorInfo.lineHeight );
2827 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2829 if( mEventData->mUpdateGrabHandlePosition )
2831 // Sets the grab handle position.
2832 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2834 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2835 cursorInfo.lineHeight );
2838 if( cursorInfo.isSecondaryCursor )
2840 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2841 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2842 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2843 cursorInfo.secondaryCursorHeight,
2844 cursorInfo.lineHeight );
2845 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2848 // Set which cursors are active according the state.
2849 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2851 if( cursorInfo.isSecondaryCursor )
2853 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2857 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2862 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2865 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2868 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2869 const CursorInfo& cursorInfo )
2871 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2872 ( RIGHT_SELECTION_HANDLE != handleType ) )
2877 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2879 // Sets the handle's position.
2880 mEventData->mDecorator->SetPosition( handleType,
2882 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2883 cursorInfo.lineHeight );
2885 // If selection handle at start of the text and other at end of the text then all text is selected.
2886 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2887 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2888 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2891 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2893 // Clamp between -space & -alignment offset.
2895 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2897 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2898 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2899 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2901 mEventData->mDecoratorUpdated = true;
2905 mModel->mScrollPosition.x = 0.f;
2909 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2911 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2913 // Nothing to do if the text is single line.
2917 // Clamp between -space & 0.
2918 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2920 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2921 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2922 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2924 mEventData->mDecoratorUpdated = true;
2928 mModel->mScrollPosition.y = 0.f;
2932 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2934 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2936 // position is in actor's coords.
2937 const float positionEndX = position.x + cursorWidth;
2938 const float positionEndY = position.y + lineHeight;
2940 // Transform the position to decorator coords.
2941 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2942 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2944 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2945 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2947 if( decoratorPositionBeginX < 0.f )
2949 mModel->mScrollPosition.x = -position.x;
2951 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2953 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2956 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2958 if( decoratorPositionBeginY < 0.f )
2960 mModel->mScrollPosition.y = -position.y;
2962 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2964 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2969 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2971 // Get the current cursor position in decorator coords.
2972 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2974 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2978 // Calculate the offset to match the cursor position before the character was deleted.
2979 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2981 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2982 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2984 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2985 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2989 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2990 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2992 // Makes the new cursor position visible if needed.
2993 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2996 void Controller::Impl::RequestRelayout()
2998 if( NULL != mControlInterface )
3000 mControlInterface->RequestTextRelayout();
3006 } // namespace Toolkit