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 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
428 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
430 // Nothing else to do if there are no paragraphs.
434 // Find the paragraphs to be updated.
435 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
436 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
438 // Text is being added at the end of the current text.
439 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
441 // Text is being added in a new paragraph after the last character of the text.
442 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
443 numberOfCharacters = 0u;
445 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
446 mTextUpdateInfo.mRequestedNumberOfCharacters = ( mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove ? 0u : mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove );
448 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
449 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
451 // Nothing else to do;
455 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
459 Length numberOfCharactersToUpdate = 0u;
460 if( mTextUpdateInfo.mFullRelayoutNeeded )
462 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
466 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
468 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
469 numberOfCharactersToUpdate,
470 paragraphsToBeUpdated );
473 if( 0u != paragraphsToBeUpdated.Count() )
475 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
476 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
477 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
479 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
480 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
482 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
483 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
484 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
485 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
487 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
488 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
490 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
494 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
498 // prevent mTextUpdateInfo.mRequestedNumberOfCharacters value underflow
499 if( numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd < mTextUpdateInfo.mNumberOfCharactersToRemove )
501 mTextUpdateInfo.mRequestedNumberOfCharacters = 0u;
505 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
507 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
510 void Controller::Impl::ClearFullModelData( OperationsMask operations )
512 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
514 mModel->mLogicalModel->mLineBreakInfo.Clear();
515 mModel->mLogicalModel->mParagraphInfo.Clear();
518 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
520 mModel->mLogicalModel->mLineBreakInfo.Clear();
523 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
525 mModel->mLogicalModel->mScriptRuns.Clear();
528 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
530 mModel->mLogicalModel->mFontRuns.Clear();
533 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
535 if( NO_OPERATION != ( BIDI_INFO & operations ) )
537 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
538 mModel->mLogicalModel->mCharacterDirections.Clear();
541 if( NO_OPERATION != ( REORDER & operations ) )
543 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
544 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
545 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
549 BidirectionalLineInfoRun& bidiLineInfo = *it;
551 free( bidiLineInfo.visualToLogicalMap );
552 bidiLineInfo.visualToLogicalMap = NULL;
554 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
558 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
560 mModel->mVisualModel->mGlyphs.Clear();
561 mModel->mVisualModel->mGlyphsToCharacters.Clear();
562 mModel->mVisualModel->mCharactersToGlyph.Clear();
563 mModel->mVisualModel->mCharactersPerGlyph.Clear();
564 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
565 mModel->mVisualModel->mGlyphPositions.Clear();
568 if( NO_OPERATION != ( LAYOUT & operations ) )
570 mModel->mVisualModel->mLines.Clear();
573 if( NO_OPERATION != ( COLOR & operations ) )
575 mModel->mVisualModel->mColorIndices.Clear();
579 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
581 const CharacterIndex endIndexPlusOne = endIndex + 1u;
583 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
585 // Clear the line break info.
586 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
588 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
589 lineBreakInfoBuffer + endIndexPlusOne );
591 // Clear the paragraphs.
592 ClearCharacterRuns( startIndex,
594 mModel->mLogicalModel->mParagraphInfo );
597 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
599 // Clear the word break info.
600 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
602 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
603 wordBreakInfoBuffer + endIndexPlusOne );
606 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
608 // Clear the scripts.
609 ClearCharacterRuns( startIndex,
611 mModel->mLogicalModel->mScriptRuns );
614 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
617 ClearCharacterRuns( startIndex,
619 mModel->mLogicalModel->mFontRuns );
622 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
624 if( NO_OPERATION != ( BIDI_INFO & operations ) )
626 // Clear the bidirectional paragraph info.
627 ClearCharacterRuns( startIndex,
629 mModel->mLogicalModel->mBidirectionalParagraphInfo );
631 // Clear the character's directions.
632 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
634 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
635 characterDirectionsBuffer + endIndexPlusOne );
638 if( NO_OPERATION != ( REORDER & operations ) )
640 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
641 uint32_t endRemoveIndex = startRemoveIndex;
642 ClearCharacterRuns( startIndex,
644 mModel->mLogicalModel->mBidirectionalLineInfo,
648 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
650 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
651 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
652 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
656 BidirectionalLineInfoRun& bidiLineInfo = *it;
658 free( bidiLineInfo.visualToLogicalMap );
659 bidiLineInfo.visualToLogicalMap = NULL;
662 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
663 bidirectionalLineInfoBuffer + endRemoveIndex );
668 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
670 const CharacterIndex endIndexPlusOne = endIndex + 1u;
671 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
673 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
674 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
675 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
677 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
678 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
680 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
682 // Update the character to glyph indices.
683 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
684 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
688 CharacterIndex& index = *it;
689 index -= numberOfGlyphsRemoved;
692 // Clear the character to glyph conversion table.
693 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
694 charactersToGlyphBuffer + endIndexPlusOne );
696 // Clear the glyphs per character table.
697 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
698 glyphsPerCharacterBuffer + endIndexPlusOne );
700 // Clear the glyphs buffer.
701 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
702 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
703 glyphsBuffer + endGlyphIndexPlusOne );
705 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
707 // Update the glyph to character indices.
708 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
709 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
713 CharacterIndex& index = *it;
714 index -= numberOfCharactersRemoved;
717 // Clear the glyphs to characters buffer.
718 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
719 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
721 // Clear the characters per glyph buffer.
722 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
723 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
724 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
726 // Clear the positions buffer.
727 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
728 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
729 positionsBuffer + endGlyphIndexPlusOne );
732 if( NO_OPERATION != ( LAYOUT & operations ) )
735 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
736 uint32_t endRemoveIndex = startRemoveIndex;
737 ClearCharacterRuns( startIndex,
739 mModel->mVisualModel->mLines,
743 // Will update the glyph runs.
744 startRemoveIndex = mModel->mVisualModel->mLines.Count();
745 endRemoveIndex = startRemoveIndex;
746 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
747 endGlyphIndexPlusOne - 1u,
748 mModel->mVisualModel->mLines,
752 // Set the line index from where to insert the new laid-out lines.
753 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
755 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
756 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
757 linesBuffer + endRemoveIndex );
760 if( NO_OPERATION != ( COLOR & operations ) )
762 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
764 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
765 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
766 colorIndexBuffer + endGlyphIndexPlusOne );
771 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
773 if( mTextUpdateInfo.mClearAll ||
774 ( ( 0u == startIndex ) &&
775 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
777 ClearFullModelData( operations );
781 // Clear the model data related with characters.
782 ClearCharacterModelData( startIndex, endIndex, operations );
784 // Clear the model data related with glyphs.
785 ClearGlyphModelData( startIndex, endIndex, operations );
788 // The estimated number of lines. Used to avoid reallocations when layouting.
789 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
791 mModel->mVisualModel->ClearCaches();
794 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
796 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
798 // Calculate the operations to be done.
799 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
801 if( NO_OPERATION == operations )
803 // Nothing to do if no operations are pending and required.
807 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
808 Vector<Character> displayCharacters;
809 bool useHiddenText = false;
810 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
812 mHiddenInput->Substitute( srcCharacters,displayCharacters );
813 useHiddenText = true;
816 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
817 const Length numberOfCharacters = utf32Characters.Count();
819 // Index to the first character of the first paragraph to be updated.
820 CharacterIndex startIndex = 0u;
821 // Number of characters of the paragraphs to be removed.
822 Length paragraphCharacters = 0u;
824 CalculateTextUpdateIndices( paragraphCharacters );
825 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
827 if( mTextUpdateInfo.mClearAll ||
828 ( 0u != paragraphCharacters ) )
830 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
833 mTextUpdateInfo.mClearAll = false;
835 // Whether the model is updated.
836 bool updated = false;
838 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
839 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
841 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
843 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
844 // calculate the bidirectional info for each 'paragraph'.
845 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
846 // is not shaped together).
847 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
849 SetLineBreakInfo( utf32Characters,
851 requestedNumberOfCharacters,
854 // Create the paragraph info.
855 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
856 requestedNumberOfCharacters );
860 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
861 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
863 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
864 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
866 SetWordBreakInfo( utf32Characters,
868 requestedNumberOfCharacters,
873 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
874 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
876 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
877 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
879 if( getScripts || validateFonts )
881 // Validates the fonts assigned by the application or assigns default ones.
882 // It makes sure all the characters are going to be rendered by the correct font.
883 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
887 // Retrieves the scripts used in the text.
888 multilanguageSupport.SetScripts( utf32Characters,
890 requestedNumberOfCharacters,
896 // Validate the fonts set through the mark-up string.
897 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
899 // Get the default font's description.
900 TextAbstraction::FontDescription defaultFontDescription;
901 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
903 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
905 // If the placeholder font is set specifically, only placeholder font is changed.
906 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
907 if( mEventData->mPlaceholderFont->sizeDefined )
909 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
912 else if( NULL != mFontDefaults )
914 // Set the normal font and the placeholder font.
915 defaultFontDescription = mFontDefaults->mFontDescription;
916 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
919 // Validates the fonts. If there is a character with no assigned font it sets a default one.
920 // After this call, fonts are validated.
921 multilanguageSupport.ValidateFonts( utf32Characters,
924 defaultFontDescription,
927 requestedNumberOfCharacters,
933 Vector<Character> mirroredUtf32Characters;
934 bool textMirrored = false;
935 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
936 if( NO_OPERATION != ( BIDI_INFO & operations ) )
938 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
939 bidirectionalInfo.Reserve( numberOfParagraphs );
941 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
942 SetBidirectionalInfo( utf32Characters,
946 requestedNumberOfCharacters,
949 if( 0u != bidirectionalInfo.Count() )
951 // Only set the character directions if there is right to left characters.
952 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
953 GetCharactersDirection( bidirectionalInfo,
956 requestedNumberOfCharacters,
959 // This paragraph has right to left text. Some characters may need to be mirrored.
960 // TODO: consider if the mirrored string can be stored as well.
962 textMirrored = GetMirroredText( utf32Characters,
966 requestedNumberOfCharacters,
967 mirroredUtf32Characters );
971 // There is no right to left characters. Clear the directions vector.
972 mModel->mLogicalModel->mCharacterDirections.Clear();
977 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
978 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
979 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
980 Vector<GlyphIndex> newParagraphGlyphs;
981 newParagraphGlyphs.Reserve( numberOfParagraphs );
983 const Length currentNumberOfGlyphs = glyphs.Count();
984 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
986 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
988 ShapeText( textToShape,
993 mTextUpdateInfo.mStartGlyphIndex,
994 requestedNumberOfCharacters,
996 glyphsToCharactersMap,
998 newParagraphGlyphs );
1000 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1001 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1002 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1006 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1008 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1010 GlyphInfo* glyphsBuffer = glyphs.Begin();
1011 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1013 // Update the width and advance of all new paragraph characters.
1014 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1016 const GlyphIndex index = *it;
1017 GlyphInfo& glyph = *( glyphsBuffer + index );
1019 glyph.xBearing = 0.f;
1021 glyph.advance = 0.f;
1026 if( NO_OPERATION != ( COLOR & operations ) )
1028 // Set the color runs in glyphs.
1029 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1030 mModel->mVisualModel->mCharactersToGlyph,
1031 mModel->mVisualModel->mGlyphsPerCharacter,
1033 mTextUpdateInfo.mStartGlyphIndex,
1034 requestedNumberOfCharacters,
1035 mModel->mVisualModel->mColors,
1036 mModel->mVisualModel->mColorIndices );
1041 if( ( NULL != mEventData ) &&
1042 mEventData->mPreEditFlag &&
1043 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1045 // Add the underline for the pre-edit text.
1046 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1047 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1049 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1050 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1051 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1052 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1054 GlyphRun underlineRun;
1055 underlineRun.glyphIndex = glyphStart;
1056 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1058 // TODO: At the moment the underline runs are only for pre-edit.
1059 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1062 // The estimated number of lines. Used to avoid reallocations when layouting.
1063 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1065 // Set the previous number of characters for the next time the text is updated.
1066 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1071 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1073 // Sets the default text's color.
1074 inputStyle.textColor = mTextColor;
1075 inputStyle.isDefaultColor = true;
1077 inputStyle.familyName.clear();
1078 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1079 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1080 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1081 inputStyle.size = 0.f;
1083 inputStyle.lineSpacing = 0.f;
1085 inputStyle.underlineProperties.clear();
1086 inputStyle.shadowProperties.clear();
1087 inputStyle.embossProperties.clear();
1088 inputStyle.outlineProperties.clear();
1090 inputStyle.isFamilyDefined = false;
1091 inputStyle.isWeightDefined = false;
1092 inputStyle.isWidthDefined = false;
1093 inputStyle.isSlantDefined = false;
1094 inputStyle.isSizeDefined = false;
1096 inputStyle.isLineSpacingDefined = false;
1098 inputStyle.isUnderlineDefined = false;
1099 inputStyle.isShadowDefined = false;
1100 inputStyle.isEmbossDefined = false;
1101 inputStyle.isOutlineDefined = false;
1103 // Sets the default font's family name, weight, width, slant and size.
1106 if( mFontDefaults->familyDefined )
1108 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1109 inputStyle.isFamilyDefined = true;
1112 if( mFontDefaults->weightDefined )
1114 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1115 inputStyle.isWeightDefined = true;
1118 if( mFontDefaults->widthDefined )
1120 inputStyle.width = mFontDefaults->mFontDescription.width;
1121 inputStyle.isWidthDefined = true;
1124 if( mFontDefaults->slantDefined )
1126 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1127 inputStyle.isSlantDefined = true;
1130 if( mFontDefaults->sizeDefined )
1132 inputStyle.size = mFontDefaults->mDefaultPointSize;
1133 inputStyle.isSizeDefined = true;
1138 float Controller::Impl::GetDefaultFontLineHeight()
1140 FontId defaultFontId = 0u;
1141 if( NULL == mFontDefaults )
1143 TextAbstraction::FontDescription fontDescription;
1144 defaultFontId = mFontClient.GetFontId( fontDescription );
1148 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1151 Text::FontMetrics fontMetrics;
1152 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1154 return( fontMetrics.ascender - fontMetrics.descender );
1157 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1159 if( NULL == mEventData )
1161 // Nothing to do if there is no text input.
1165 int keyCode = event.p1.mInt;
1166 bool isShiftModifier = event.p2.mBool;
1168 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1170 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1172 if( mEventData->mPrimaryCursorPosition > 0u )
1174 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1177 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1179 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1181 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1184 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1186 // Ignore Shift-Up for text selection for now.
1188 // Get first the line index of the current cursor position index.
1189 CharacterIndex characterIndex = 0u;
1191 if( mEventData->mPrimaryCursorPosition > 0u )
1193 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1196 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1197 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1199 // Retrieve the cursor position info.
1200 CursorInfo cursorInfo;
1201 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1204 // Get the line above.
1205 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1207 // Get the next hit 'y' point.
1208 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1210 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1211 bool matchedCharacter = false;
1212 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1213 mModel->mLogicalModel,
1215 mEventData->mCursorHookPositionX,
1217 CharacterHitTest::TAP,
1220 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1222 // Ignore Shift-Down for text selection for now.
1224 // Get first the line index of the current cursor position index.
1225 CharacterIndex characterIndex = 0u;
1227 if( mEventData->mPrimaryCursorPosition > 0u )
1229 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1232 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1234 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1236 // Retrieve the cursor position info.
1237 CursorInfo cursorInfo;
1238 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1241 // Get the line below.
1242 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1244 // Get the next hit 'y' point.
1245 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1247 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1248 bool matchedCharacter = false;
1249 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1250 mModel->mLogicalModel,
1252 mEventData->mCursorHookPositionX,
1254 CharacterHitTest::TAP,
1259 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1261 // Update selection position after moving the cursor
1262 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1263 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1266 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1268 // Handle text selection
1269 bool selecting = false;
1271 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1273 // Shift-Left/Right to select the text
1274 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1275 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1277 mEventData->mRightSelectionPosition += cursorPositionDelta;
1281 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1283 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1289 // Notify the cursor position to the imf manager.
1290 if( mEventData->mImfManager )
1292 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1293 mEventData->mImfManager.NotifyCursorPosition();
1296 ChangeState( EventData::SELECTING );
1298 mEventData->mUpdateLeftSelectionPosition = true;
1299 mEventData->mUpdateRightSelectionPosition = true;
1300 mEventData->mUpdateGrabHandlePosition = true;
1301 mEventData->mUpdateHighlightBox = true;
1303 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1304 if( mEventData->mGrabHandlePopupEnabled )
1306 mEventData->mDecorator->SetPopupActive( false );
1312 // Handle normal cursor move
1313 ChangeState( EventData::EDITING );
1314 mEventData->mUpdateCursorPosition = true;
1317 mEventData->mUpdateInputStyle = true;
1318 mEventData->mScrollAfterUpdatePosition = true;
1321 void Controller::Impl::OnTapEvent( const Event& event )
1323 if( NULL != mEventData )
1325 const unsigned int tapCount = event.p1.mUint;
1327 if( 1u == tapCount )
1329 if( IsShowingRealText() )
1331 // Convert from control's coords to text's coords.
1332 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1333 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1335 // Keep the tap 'x' position. Used to move the cursor.
1336 mEventData->mCursorHookPositionX = xPosition;
1338 // Whether to touch point hits on a glyph.
1339 bool matchedCharacter = false;
1340 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1341 mModel->mLogicalModel,
1345 CharacterHitTest::TAP,
1348 // When the cursor position is changing, delay cursor blinking
1349 mEventData->mDecorator->DelayCursorBlink();
1353 mEventData->mPrimaryCursorPosition = 0u;
1356 // Update selection position after tapping
1357 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1358 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1360 mEventData->mUpdateCursorPosition = true;
1361 mEventData->mUpdateGrabHandlePosition = true;
1362 mEventData->mScrollAfterUpdatePosition = true;
1363 mEventData->mUpdateInputStyle = true;
1365 // Notify the cursor position to the imf manager.
1366 if( mEventData->mImfManager )
1368 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1369 mEventData->mImfManager.NotifyCursorPosition();
1372 else if( 2u == tapCount )
1374 if( mEventData->mSelectionEnabled )
1376 // Convert from control's coords to text's coords.
1377 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1378 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1380 // Calculates the logical position from the x,y coords.
1381 RepositionSelectionHandles( xPosition,
1383 mEventData->mDoubleTapAction );
1389 void Controller::Impl::OnPanEvent( const Event& event )
1391 if( NULL == mEventData )
1393 // Nothing to do if there is no text input.
1397 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1398 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1400 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1402 // Nothing to do if scrolling is not enabled.
1406 const int state = event.p1.mInt;
1410 case Gesture::Started:
1412 // Will remove the cursor, handles or text's popup, ...
1413 ChangeState( EventData::TEXT_PANNING );
1416 case Gesture::Continuing:
1418 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1419 const Vector2 currentScroll = mModel->mScrollPosition;
1421 if( isHorizontalScrollEnabled )
1423 const float displacementX = event.p2.mFloat;
1424 mModel->mScrollPosition.x += displacementX;
1426 ClampHorizontalScroll( layoutSize );
1429 if( isVerticalScrollEnabled )
1431 const float displacementY = event.p3.mFloat;
1432 mModel->mScrollPosition.y += displacementY;
1434 ClampVerticalScroll( layoutSize );
1437 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1440 case Gesture::Finished:
1441 case Gesture::Cancelled: // FALLTHROUGH
1443 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1444 ChangeState( mEventData->mPreviousState );
1452 void Controller::Impl::OnLongPressEvent( const Event& event )
1454 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1456 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1458 ChangeState( EventData::EDITING_WITH_POPUP );
1459 mEventData->mDecoratorUpdated = true;
1460 mEventData->mUpdateInputStyle = true;
1464 if( mEventData->mSelectionEnabled )
1466 // Convert from control's coords to text's coords.
1467 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1468 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1470 // Calculates the logical position from the x,y coords.
1471 RepositionSelectionHandles( xPosition,
1473 mEventData->mLongPressAction );
1478 void Controller::Impl::OnHandleEvent( const Event& event )
1480 if( NULL == mEventData )
1482 // Nothing to do if there is no text input.
1486 const unsigned int state = event.p1.mUint;
1487 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1488 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1490 if( HANDLE_PRESSED == state )
1492 // Convert from decorator's coords to text's coords.
1493 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1494 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1496 // Need to calculate the handle's new position.
1497 bool matchedCharacter = false;
1498 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1499 mModel->mLogicalModel,
1503 CharacterHitTest::SCROLL,
1506 if( Event::GRAB_HANDLE_EVENT == event.type )
1508 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1510 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1512 // Updates the cursor position if the handle's new position is different than the current one.
1513 mEventData->mUpdateCursorPosition = true;
1514 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1515 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1516 mEventData->mPrimaryCursorPosition = handleNewPosition;
1519 // 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.
1520 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1522 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1524 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1526 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1527 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1529 // Updates the highlight box if the handle's new position is different than the current one.
1530 mEventData->mUpdateHighlightBox = true;
1531 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1532 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1533 mEventData->mLeftSelectionPosition = handleNewPosition;
1536 // 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.
1537 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1539 // Will define the order to scroll the text to match the handle position.
1540 mEventData->mIsLeftHandleSelected = true;
1541 mEventData->mIsRightHandleSelected = false;
1543 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1545 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1547 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1548 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1550 // Updates the highlight box if the handle's new position is different than the current one.
1551 mEventData->mUpdateHighlightBox = true;
1552 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1553 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1554 mEventData->mRightSelectionPosition = handleNewPosition;
1557 // 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.
1558 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1560 // Will define the order to scroll the text to match the handle position.
1561 mEventData->mIsLeftHandleSelected = false;
1562 mEventData->mIsRightHandleSelected = true;
1564 } // end ( HANDLE_PRESSED == state )
1565 else if( ( HANDLE_RELEASED == state ) ||
1566 handleStopScrolling )
1568 CharacterIndex handlePosition = 0u;
1569 if( handleStopScrolling || isSmoothHandlePanEnabled )
1571 // Convert from decorator's coords to text's coords.
1572 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1573 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1575 bool matchedCharacter = false;
1576 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1577 mModel->mLogicalModel,
1581 CharacterHitTest::SCROLL,
1585 if( Event::GRAB_HANDLE_EVENT == event.type )
1587 mEventData->mUpdateCursorPosition = true;
1588 mEventData->mUpdateGrabHandlePosition = true;
1589 mEventData->mUpdateInputStyle = true;
1591 if( !IsClipboardEmpty() )
1593 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1596 if( handleStopScrolling || isSmoothHandlePanEnabled )
1598 mEventData->mScrollAfterUpdatePosition = true;
1599 mEventData->mPrimaryCursorPosition = handlePosition;
1602 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1604 ChangeState( EventData::SELECTING );
1606 mEventData->mUpdateHighlightBox = true;
1607 mEventData->mUpdateLeftSelectionPosition = true;
1608 mEventData->mUpdateRightSelectionPosition = true;
1610 if( handleStopScrolling || isSmoothHandlePanEnabled )
1612 mEventData->mScrollAfterUpdatePosition = true;
1614 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1615 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1617 mEventData->mLeftSelectionPosition = handlePosition;
1621 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1623 ChangeState( EventData::SELECTING );
1625 mEventData->mUpdateHighlightBox = true;
1626 mEventData->mUpdateRightSelectionPosition = true;
1627 mEventData->mUpdateLeftSelectionPosition = true;
1629 if( handleStopScrolling || isSmoothHandlePanEnabled )
1631 mEventData->mScrollAfterUpdatePosition = true;
1632 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1633 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1635 mEventData->mRightSelectionPosition = handlePosition;
1640 mEventData->mDecoratorUpdated = true;
1641 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1642 else if( HANDLE_SCROLLING == state )
1644 const float xSpeed = event.p2.mFloat;
1645 const float ySpeed = event.p3.mFloat;
1646 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1647 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1649 mModel->mScrollPosition.x += xSpeed;
1650 mModel->mScrollPosition.y += ySpeed;
1652 ClampHorizontalScroll( layoutSize );
1653 ClampVerticalScroll( layoutSize );
1655 bool endOfScroll = false;
1656 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1658 // Notify the decorator there is no more text to scroll.
1659 // The decorator won't send more scroll events.
1660 mEventData->mDecorator->NotifyEndOfScroll();
1661 // Still need to set the position of the handle.
1665 // Set the position of the handle.
1666 const bool scrollRightDirection = xSpeed > 0.f;
1667 const bool scrollBottomDirection = ySpeed > 0.f;
1668 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1669 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1671 if( Event::GRAB_HANDLE_EVENT == event.type )
1673 ChangeState( EventData::GRAB_HANDLE_PANNING );
1675 // Get the grab handle position in decorator coords.
1676 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1678 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1680 // Position the grag handle close to either the left or right edge.
1681 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1684 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1686 position.x = mEventData->mCursorHookPositionX;
1688 // Position the grag handle close to either the top or bottom edge.
1689 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1692 // Get the new handle position.
1693 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1694 bool matchedCharacter = false;
1695 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1696 mModel->mLogicalModel,
1698 position.x - mModel->mScrollPosition.x,
1699 position.y - mModel->mScrollPosition.y,
1700 CharacterHitTest::SCROLL,
1703 if( mEventData->mPrimaryCursorPosition != handlePosition )
1705 mEventData->mUpdateCursorPosition = true;
1706 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1707 mEventData->mScrollAfterUpdatePosition = true;
1708 mEventData->mPrimaryCursorPosition = handlePosition;
1710 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1712 // Updates the decorator if the soft handle panning is enabled.
1713 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1715 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1717 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1719 // Get the selection handle position in decorator coords.
1720 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1722 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1724 // Position the selection handle close to either the left or right edge.
1725 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1728 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1730 position.x = mEventData->mCursorHookPositionX;
1732 // Position the grag handle close to either the top or bottom edge.
1733 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1736 // Get the new handle position.
1737 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1738 bool matchedCharacter = false;
1739 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1740 mModel->mLogicalModel,
1742 position.x - mModel->mScrollPosition.x,
1743 position.y - mModel->mScrollPosition.y,
1744 CharacterHitTest::SCROLL,
1747 if( leftSelectionHandleEvent )
1749 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1751 if( differentHandles || endOfScroll )
1753 mEventData->mUpdateHighlightBox = true;
1754 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1755 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1756 mEventData->mLeftSelectionPosition = handlePosition;
1761 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1762 if( differentHandles || endOfScroll )
1764 mEventData->mUpdateHighlightBox = true;
1765 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1766 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1767 mEventData->mRightSelectionPosition = handlePosition;
1771 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1773 RepositionSelectionHandles();
1775 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1778 mEventData->mDecoratorUpdated = true;
1779 } // end ( HANDLE_SCROLLING == state )
1782 void Controller::Impl::OnSelectEvent( const Event& event )
1784 if( NULL == mEventData )
1786 // Nothing to do if there is no text.
1790 if( mEventData->mSelectionEnabled )
1792 // Convert from control's coords to text's coords.
1793 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1794 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1796 // Calculates the logical position from the x,y coords.
1797 RepositionSelectionHandles( xPosition,
1799 Controller::NoTextTap::HIGHLIGHT );
1803 void Controller::Impl::OnSelectAllEvent()
1805 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1807 if( NULL == mEventData )
1809 // Nothing to do if there is no text.
1813 if( mEventData->mSelectionEnabled )
1815 ChangeState( EventData::SELECTING );
1817 mEventData->mLeftSelectionPosition = 0u;
1818 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1820 mEventData->mScrollAfterUpdatePosition = true;
1821 mEventData->mUpdateLeftSelectionPosition = true;
1822 mEventData->mUpdateRightSelectionPosition = true;
1823 mEventData->mUpdateHighlightBox = true;
1827 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1829 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1831 // Nothing to select if handles are in the same place.
1832 selectedText.clear();
1836 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1838 //Get start and end position of selection
1839 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1840 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1842 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1843 const Length numberOfCharacters = utf32Characters.Count();
1845 // Validate the start and end selection points
1846 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1848 //Get text as a UTF8 string
1849 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1851 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1853 // Keep a copy of the current input style.
1854 InputStyle currentInputStyle;
1855 currentInputStyle.Copy( mEventData->mInputStyle );
1857 // Set as input style the style of the first deleted character.
1858 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1860 // Compare if the input style has changed.
1861 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1863 if( hasInputStyleChanged )
1865 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1866 // Queue the input style changed signal.
1867 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1870 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1872 // Mark the paragraphs to be updated.
1873 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1875 mTextUpdateInfo.mCharacterIndex = 0;
1876 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1877 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1878 mTextUpdateInfo.mClearAll = true;
1882 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1883 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1886 // Delete text between handles
1887 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1888 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1889 utf32Characters.Erase( first, last );
1891 // Will show the cursor at the first character of the selection.
1892 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1896 // Will show the cursor at the last character of the selection.
1897 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1900 mEventData->mDecoratorUpdated = true;
1904 void Controller::Impl::ShowClipboard()
1908 mClipboard.ShowClipboard();
1912 void Controller::Impl::HideClipboard()
1914 if( mClipboard && mClipboardHideEnabled )
1916 mClipboard.HideClipboard();
1920 void Controller::Impl::SetClipboardHideEnable(bool enable)
1922 mClipboardHideEnabled = enable;
1925 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1927 //Send string to clipboard
1928 return ( mClipboard && mClipboard.SetItem( source ) );
1931 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1933 std::string selectedText;
1934 RetrieveSelection( selectedText, deleteAfterSending );
1935 CopyStringToClipboard( selectedText );
1936 ChangeState( EventData::EDITING );
1939 void Controller::Impl::RequestGetTextFromClipboard()
1943 mClipboard.RequestItem();
1947 void Controller::Impl::RepositionSelectionHandles()
1949 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1950 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1952 if( selectionStart == selectionEnd )
1954 // Nothing to select if handles are in the same place.
1958 mEventData->mDecorator->ClearHighlights();
1960 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1961 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1962 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1963 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1964 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1965 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1966 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1968 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1969 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1970 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1972 // Swap the indices if the start is greater than the end.
1973 const bool indicesSwapped = selectionStart > selectionEnd;
1975 // Tell the decorator to flip the selection handles if needed.
1976 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1978 if( indicesSwapped )
1980 std::swap( selectionStart, selectionEnd );
1983 // Get the indices to the first and last selected glyphs.
1984 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1985 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1986 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1987 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1989 // Get the lines where the glyphs are laid-out.
1990 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1992 LineIndex lineIndex = 0u;
1993 Length numberOfLines = 0u;
1994 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1995 1u + glyphEnd - glyphStart,
1998 const LineIndex firstLineIndex = lineIndex;
2000 // Create the structure to store some selection box info.
2001 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2002 selectionBoxLinesInfo.Resize( numberOfLines );
2004 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2005 selectionBoxInfo->minX = MAX_FLOAT;
2006 selectionBoxInfo->maxX = MIN_FLOAT;
2008 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2009 float minHighlightX = std::numeric_limits<float>::max();
2010 float maxHighlightX = std::numeric_limits<float>::min();
2012 Vector2 highLightPosition; // The highlight position in decorator's coords.
2014 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2016 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2017 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2020 // Transform to decorator's (control) coords.
2021 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2023 lineRun += firstLineIndex;
2025 // The line height is the addition of the line ascender and the line descender.
2026 // However, the line descender has a negative value, hence the subtraction.
2027 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2029 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2031 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2032 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2033 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2035 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2036 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2037 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2039 // The number of quads of the selection box.
2040 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2041 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2043 // Count the actual number of quads.
2044 unsigned int actualNumberOfQuads = 0u;
2047 // Traverse the glyphs.
2048 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2050 const GlyphInfo& glyph = *( glyphsBuffer + index );
2051 const Vector2& position = *( positionsBuffer + index );
2053 if( splitStartGlyph )
2055 // 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.
2057 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2058 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2059 // Get the direction of the character.
2060 CharacterDirection isCurrentRightToLeft = false;
2061 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2063 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2066 // The end point could be in the middle of the ligature.
2067 // Calculate the number of characters selected.
2068 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2070 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2071 quad.y = selectionBoxInfo->lineOffset;
2072 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2073 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2075 // Store the min and max 'x' for each line.
2076 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2077 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2079 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2080 ++actualNumberOfQuads;
2082 splitStartGlyph = false;
2086 if( splitEndGlyph && ( index == glyphEnd ) )
2088 // 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.
2090 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2091 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2092 // Get the direction of the character.
2093 CharacterDirection isCurrentRightToLeft = false;
2094 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2096 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2099 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2101 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2102 quad.y = selectionBoxInfo->lineOffset;
2103 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2104 quad.w = quad.y + selectionBoxInfo->lineHeight;
2106 // Store the min and max 'x' for each line.
2107 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2108 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2110 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2112 ++actualNumberOfQuads;
2114 splitEndGlyph = false;
2118 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2119 quad.y = selectionBoxInfo->lineOffset;
2120 quad.z = quad.x + glyph.advance;
2121 quad.w = quad.y + selectionBoxInfo->lineHeight;
2123 // Store the min and max 'x' for each line.
2124 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2125 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2127 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2129 ++actualNumberOfQuads;
2131 // Whether to retrieve the next line.
2132 if( index == lastGlyphOfLine )
2135 if( lineIndex < firstLineIndex + numberOfLines )
2137 // Retrieve the next line.
2140 // Get the last glyph of the new line.
2141 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2143 // Keep the offset and height of the current selection box.
2144 const float currentLineOffset = selectionBoxInfo->lineOffset;
2145 const float currentLineHeight = selectionBoxInfo->lineHeight;
2147 // Get the selection box info for the next line.
2150 selectionBoxInfo->minX = MAX_FLOAT;
2151 selectionBoxInfo->maxX = MIN_FLOAT;
2153 // Update the line's vertical offset.
2154 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2156 // The line height is the addition of the line ascender and the line descender.
2157 // However, the line descender has a negative value, hence the subtraction.
2158 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2163 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2164 // The final width is calculated after 'boxifying' the selection.
2165 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2166 endIt = selectionBoxLinesInfo.End();
2170 const SelectionBoxInfo& info = *it;
2172 // Update the size of the highlighted text.
2173 highLightSize.height += info.lineHeight;
2174 minHighlightX = std::min( minHighlightX, info.minX );
2175 maxHighlightX = std::max( maxHighlightX, info.maxX );
2178 // Add extra geometry to 'boxify' the selection.
2180 if( 1u < numberOfLines )
2182 // Boxify the first line.
2183 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2184 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2186 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2187 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2192 quad.y = firstSelectionBoxLineInfo.lineOffset;
2193 quad.z = firstSelectionBoxLineInfo.minX;
2194 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2196 // Boxify at the beginning of the line.
2197 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2199 ++actualNumberOfQuads;
2201 // Update the size of the highlighted text.
2202 minHighlightX = 0.f;
2207 quad.x = firstSelectionBoxLineInfo.maxX;
2208 quad.y = firstSelectionBoxLineInfo.lineOffset;
2209 quad.z = mModel->mVisualModel->mControlSize.width;
2210 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2212 // Boxify at the end of the line.
2213 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2215 ++actualNumberOfQuads;
2217 // Update the size of the highlighted text.
2218 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2221 // Boxify the central lines.
2222 if( 2u < numberOfLines )
2224 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2225 endIt = selectionBoxLinesInfo.End() - 1u;
2229 const SelectionBoxInfo& info = *it;
2232 quad.y = info.lineOffset;
2234 quad.w = info.lineOffset + info.lineHeight;
2236 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2238 ++actualNumberOfQuads;
2241 quad.y = info.lineOffset;
2242 quad.z = mModel->mVisualModel->mControlSize.width;
2243 quad.w = info.lineOffset + info.lineHeight;
2245 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2247 ++actualNumberOfQuads;
2250 // Update the size of the highlighted text.
2251 minHighlightX = 0.f;
2252 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2255 // Boxify the last line.
2256 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2257 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2259 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2260 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2265 quad.y = lastSelectionBoxLineInfo.lineOffset;
2266 quad.z = lastSelectionBoxLineInfo.minX;
2267 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2269 // Boxify at the beginning of the line.
2270 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2272 ++actualNumberOfQuads;
2274 // Update the size of the highlighted text.
2275 minHighlightX = 0.f;
2280 quad.x = lastSelectionBoxLineInfo.maxX;
2281 quad.y = lastSelectionBoxLineInfo.lineOffset;
2282 quad.z = mModel->mVisualModel->mControlSize.width;
2283 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2285 // Boxify at the end of the line.
2286 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2288 ++actualNumberOfQuads;
2290 // Update the size of the highlighted text.
2291 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2295 // Set the actual number of quads.
2296 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2298 // Sets the highlight's size and position. In decorator's coords.
2299 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2300 highLightSize.width = maxHighlightX - minHighlightX;
2302 highLightPosition.x = minHighlightX;
2303 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2304 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2306 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2308 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2310 CursorInfo primaryCursorInfo;
2311 GetCursorPosition( mEventData->mLeftSelectionPosition,
2312 primaryCursorInfo );
2314 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2316 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2318 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2319 primaryCursorInfo.lineHeight );
2321 CursorInfo secondaryCursorInfo;
2322 GetCursorPosition( mEventData->mRightSelectionPosition,
2323 secondaryCursorInfo );
2325 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2327 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2328 secondaryPosition.x,
2329 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2330 secondaryCursorInfo.lineHeight );
2333 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2334 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2336 // Set the flag to update the decorator.
2337 mEventData->mDecoratorUpdated = true;
2340 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2342 if( NULL == mEventData )
2344 // Nothing to do if there is no text input.
2348 if( IsShowingPlaceholderText() )
2350 // Nothing to do if there is the place-holder text.
2354 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2355 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2356 if( ( 0 == numberOfGlyphs ) ||
2357 ( 0 == numberOfLines ) )
2359 // Nothing to do if there is no text.
2363 // Find which word was selected
2364 CharacterIndex selectionStart( 0 );
2365 CharacterIndex selectionEnd( 0 );
2366 CharacterIndex noTextHitIndex( 0 );
2367 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2368 mModel->mLogicalModel,
2375 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2377 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2379 ChangeState( EventData::SELECTING );
2381 mEventData->mLeftSelectionPosition = selectionStart;
2382 mEventData->mRightSelectionPosition = selectionEnd;
2384 mEventData->mUpdateLeftSelectionPosition = true;
2385 mEventData->mUpdateRightSelectionPosition = true;
2386 mEventData->mUpdateHighlightBox = true;
2388 // It may happen an IMF commit event arrives before the selection event
2389 // if the IMF manager is in pre-edit state. The commit event will set the
2390 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2391 // to false, the highlight box won't be updated.
2392 mEventData->mUpdateCursorPosition = false;
2394 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2396 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2398 // Nothing to select. i.e. a white space, out of bounds
2399 ChangeState( EventData::EDITING_WITH_POPUP );
2401 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2403 mEventData->mUpdateCursorPosition = true;
2404 mEventData->mUpdateGrabHandlePosition = true;
2405 mEventData->mScrollAfterUpdatePosition = true;
2406 mEventData->mUpdateInputStyle = true;
2408 else if( Controller::NoTextTap::NO_ACTION == action )
2410 // Nothing to select. i.e. a white space, out of bounds
2411 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2413 mEventData->mUpdateCursorPosition = true;
2414 mEventData->mUpdateGrabHandlePosition = true;
2415 mEventData->mScrollAfterUpdatePosition = true;
2416 mEventData->mUpdateInputStyle = true;
2420 void Controller::Impl::SetPopupButtons()
2423 * Sets the Popup buttons to be shown depending on State.
2425 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2427 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2430 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2432 if( EventData::SELECTING == mEventData->mState )
2434 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2436 if( !IsClipboardEmpty() )
2438 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2439 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2442 if( !mEventData->mAllTextSelected )
2444 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2447 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2449 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2451 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2454 if( !IsClipboardEmpty() )
2456 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2457 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2460 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2462 if ( !IsClipboardEmpty() )
2464 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2465 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2469 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2472 void Controller::Impl::ChangeState( EventData::State newState )
2474 if( NULL == mEventData )
2476 // Nothing to do if there is no text input.
2480 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2482 if( mEventData->mState != newState )
2484 mEventData->mPreviousState = mEventData->mState;
2485 mEventData->mState = newState;
2487 switch( mEventData->mState )
2489 case EventData::INACTIVE:
2491 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2492 mEventData->mDecorator->StopCursorBlink();
2493 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2494 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2495 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2496 mEventData->mDecorator->SetHighlightActive( false );
2497 mEventData->mDecorator->SetPopupActive( false );
2498 mEventData->mDecoratorUpdated = true;
2501 case EventData::INTERRUPTED:
2503 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2504 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2505 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2506 mEventData->mDecorator->SetHighlightActive( false );
2507 mEventData->mDecorator->SetPopupActive( false );
2508 mEventData->mDecoratorUpdated = true;
2511 case EventData::SELECTING:
2513 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2514 mEventData->mDecorator->StopCursorBlink();
2515 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2516 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2517 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2518 mEventData->mDecorator->SetHighlightActive( true );
2519 if( mEventData->mGrabHandlePopupEnabled )
2522 mEventData->mDecorator->SetPopupActive( true );
2524 mEventData->mDecoratorUpdated = true;
2527 case EventData::EDITING:
2529 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2530 if( mEventData->mCursorBlinkEnabled )
2532 mEventData->mDecorator->StartCursorBlink();
2534 // Grab handle is not shown until a tap is received whilst EDITING
2535 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2536 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2537 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2538 mEventData->mDecorator->SetHighlightActive( false );
2539 if( mEventData->mGrabHandlePopupEnabled )
2541 mEventData->mDecorator->SetPopupActive( false );
2543 mEventData->mDecoratorUpdated = true;
2546 case EventData::EDITING_WITH_POPUP:
2548 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2550 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2551 if( mEventData->mCursorBlinkEnabled )
2553 mEventData->mDecorator->StartCursorBlink();
2555 if( mEventData->mSelectionEnabled )
2557 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2558 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2559 mEventData->mDecorator->SetHighlightActive( false );
2563 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2565 if( mEventData->mGrabHandlePopupEnabled )
2568 mEventData->mDecorator->SetPopupActive( true );
2570 mEventData->mDecoratorUpdated = true;
2573 case EventData::EDITING_WITH_GRAB_HANDLE:
2575 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2577 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2578 if( mEventData->mCursorBlinkEnabled )
2580 mEventData->mDecorator->StartCursorBlink();
2582 // Grab handle is not shown until a tap is received whilst EDITING
2583 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2584 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2585 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2586 mEventData->mDecorator->SetHighlightActive( false );
2587 if( mEventData->mGrabHandlePopupEnabled )
2589 mEventData->mDecorator->SetPopupActive( false );
2591 mEventData->mDecoratorUpdated = true;
2594 case EventData::SELECTION_HANDLE_PANNING:
2596 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2597 mEventData->mDecorator->StopCursorBlink();
2598 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2599 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2600 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2601 mEventData->mDecorator->SetHighlightActive( true );
2602 if( mEventData->mGrabHandlePopupEnabled )
2604 mEventData->mDecorator->SetPopupActive( false );
2606 mEventData->mDecoratorUpdated = true;
2609 case EventData::GRAB_HANDLE_PANNING:
2611 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2613 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2614 if( mEventData->mCursorBlinkEnabled )
2616 mEventData->mDecorator->StartCursorBlink();
2618 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2619 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2620 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2621 mEventData->mDecorator->SetHighlightActive( false );
2622 if( mEventData->mGrabHandlePopupEnabled )
2624 mEventData->mDecorator->SetPopupActive( false );
2626 mEventData->mDecoratorUpdated = true;
2629 case EventData::EDITING_WITH_PASTE_POPUP:
2631 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2633 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2634 if( mEventData->mCursorBlinkEnabled )
2636 mEventData->mDecorator->StartCursorBlink();
2639 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2640 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2641 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2642 mEventData->mDecorator->SetHighlightActive( false );
2644 if( mEventData->mGrabHandlePopupEnabled )
2647 mEventData->mDecorator->SetPopupActive( true );
2649 mEventData->mDecoratorUpdated = true;
2652 case EventData::TEXT_PANNING:
2654 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2655 mEventData->mDecorator->StopCursorBlink();
2656 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2657 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2658 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2660 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2661 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2662 mEventData->mDecorator->SetHighlightActive( true );
2665 if( mEventData->mGrabHandlePopupEnabled )
2667 mEventData->mDecorator->SetPopupActive( false );
2670 mEventData->mDecoratorUpdated = true;
2677 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2678 CursorInfo& cursorInfo )
2680 if( !IsShowingRealText() )
2682 // Do not want to use the place-holder text to set the cursor position.
2684 // Use the line's height of the font's family set to set the cursor's size.
2685 // If there is no font's family set, use the default font.
2686 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2688 cursorInfo.lineOffset = 0.f;
2689 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2690 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2692 switch( mModel->mHorizontalAlignment )
2694 case Text::HorizontalAlignment::BEGIN :
2696 cursorInfo.primaryPosition.x = 0.f;
2699 case Text::HorizontalAlignment::CENTER:
2701 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2704 case Text::HorizontalAlignment::END:
2706 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2711 // Nothing else to do.
2715 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2716 GetCursorPositionParameters parameters;
2717 parameters.visualModel = mModel->mVisualModel;
2718 parameters.logicalModel = mModel->mLogicalModel;
2719 parameters.metrics = mMetrics;
2720 parameters.logical = logical;
2721 parameters.isMultiline = isMultiLine;
2723 Text::GetCursorPosition( parameters,
2726 // Adds Outline offset.
2727 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2728 cursorInfo.primaryPosition.x += outlineWidth;
2729 cursorInfo.primaryPosition.y += outlineWidth;
2730 cursorInfo.secondaryPosition.x += outlineWidth;
2731 cursorInfo.secondaryPosition.y += outlineWidth;
2735 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2737 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2738 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2740 if( 0.f > cursorInfo.primaryPosition.x )
2742 cursorInfo.primaryPosition.x = 0.f;
2745 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2746 if( cursorInfo.primaryPosition.x > edgeWidth )
2748 cursorInfo.primaryPosition.x = edgeWidth;
2753 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2755 if( NULL == mEventData )
2757 // Nothing to do if there is no text input.
2761 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2763 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2764 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2766 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2767 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2769 if( numberOfCharacters > 1u )
2771 const Script script = mModel->mLogicalModel->GetScript( index );
2772 if( HasLigatureMustBreak( script ) )
2774 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2775 numberOfCharacters = 1u;
2780 while( 0u == numberOfCharacters )
2783 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2787 if( index < mEventData->mPrimaryCursorPosition )
2789 cursorIndex -= numberOfCharacters;
2793 cursorIndex += numberOfCharacters;
2796 // Will update the cursor hook position.
2797 mEventData->mUpdateCursorHookPosition = true;
2802 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2804 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2805 if( NULL == mEventData )
2807 // Nothing to do if there is no text input.
2808 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2812 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2814 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2816 // Sets the cursor position.
2817 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2820 cursorInfo.primaryCursorHeight,
2821 cursorInfo.lineHeight );
2822 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2824 if( mEventData->mUpdateGrabHandlePosition )
2826 // Sets the grab handle position.
2827 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2829 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2830 cursorInfo.lineHeight );
2833 if( cursorInfo.isSecondaryCursor )
2835 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2836 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2837 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2838 cursorInfo.secondaryCursorHeight,
2839 cursorInfo.lineHeight );
2840 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2843 // Set which cursors are active according the state.
2844 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2846 if( cursorInfo.isSecondaryCursor )
2848 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2852 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2857 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2860 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2863 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2864 const CursorInfo& cursorInfo )
2866 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2867 ( RIGHT_SELECTION_HANDLE != handleType ) )
2872 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2874 // Sets the handle's position.
2875 mEventData->mDecorator->SetPosition( handleType,
2877 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2878 cursorInfo.lineHeight );
2880 // If selection handle at start of the text and other at end of the text then all text is selected.
2881 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2882 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2883 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2886 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2888 // Clamp between -space & -alignment offset.
2890 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2892 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2893 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2894 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2896 mEventData->mDecoratorUpdated = true;
2900 mModel->mScrollPosition.x = 0.f;
2904 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2906 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2908 // Nothing to do if the text is single line.
2912 // Clamp between -space & 0.
2913 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2915 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2916 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2917 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2919 mEventData->mDecoratorUpdated = true;
2923 mModel->mScrollPosition.y = 0.f;
2927 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2929 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2931 // position is in actor's coords.
2932 const float positionEndX = position.x + cursorWidth;
2933 const float positionEndY = position.y + lineHeight;
2935 // Transform the position to decorator coords.
2936 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2937 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2939 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2940 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2942 if( decoratorPositionBeginX < 0.f )
2944 mModel->mScrollPosition.x = -position.x;
2946 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2948 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2951 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2953 if( decoratorPositionBeginY < 0.f )
2955 mModel->mScrollPosition.y = -position.y;
2957 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2959 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2964 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2966 // Get the current cursor position in decorator coords.
2967 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2969 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2973 // Calculate the offset to match the cursor position before the character was deleted.
2974 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2976 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2977 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2979 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2980 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2984 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2985 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2987 // Makes the new cursor position visible if needed.
2988 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2991 void Controller::Impl::RequestRelayout()
2993 if( NULL != mControlInterface )
2995 mControlInterface->RequestTextRelayout();
3001 } // namespace Toolkit