2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderFont( NULL ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false ),
109 mIsPlaceholderPixelSize( false ),
110 mIsPlaceholderElideEnabled( false ),
111 mPlaceholderEllipsisFlag( false ),
112 mShiftSelectionFlag( true )
114 mImfManager = ImfManager::Get();
117 EventData::~EventData()
120 bool Controller::Impl::ProcessInputEvents()
122 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
123 if( NULL == mEventData )
125 // Nothing to do if there is no text input.
126 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
130 if( mEventData->mDecorator )
132 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
133 iter != mEventData->mEventQueue.end();
138 case Event::CURSOR_KEY_EVENT:
140 OnCursorKeyEvent( *iter );
143 case Event::TAP_EVENT:
148 case Event::LONG_PRESS_EVENT:
150 OnLongPressEvent( *iter );
153 case Event::PAN_EVENT:
158 case Event::GRAB_HANDLE_EVENT:
159 case Event::LEFT_SELECTION_HANDLE_EVENT:
160 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
162 OnHandleEvent( *iter );
167 OnSelectEvent( *iter );
170 case Event::SELECT_ALL:
179 if( mEventData->mUpdateCursorPosition ||
180 mEventData->mUpdateHighlightBox )
185 // The cursor must also be repositioned after inserts into the model
186 if( mEventData->mUpdateCursorPosition )
188 // Updates the cursor position and scrolls the text to make it visible.
189 CursorInfo cursorInfo;
190 // Calculate the cursor position from the new cursor index.
191 GetCursorPosition( mEventData->mPrimaryCursorPosition,
194 if( mEventData->mUpdateCursorHookPosition )
196 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
197 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
198 mEventData->mUpdateCursorHookPosition = false;
201 // Scroll first the text after delete ...
202 if( mEventData->mScrollAfterDelete )
204 ScrollTextToMatchCursor( cursorInfo );
207 // ... then, text can be scrolled to make the cursor visible.
208 if( mEventData->mScrollAfterUpdatePosition )
210 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
211 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
213 mEventData->mScrollAfterUpdatePosition = false;
214 mEventData->mScrollAfterDelete = false;
216 UpdateCursorPosition( cursorInfo );
218 mEventData->mDecoratorUpdated = true;
219 mEventData->mUpdateCursorPosition = false;
220 mEventData->mUpdateGrabHandlePosition = false;
224 CursorInfo leftHandleInfo;
225 CursorInfo rightHandleInfo;
227 if( mEventData->mUpdateHighlightBox )
229 GetCursorPosition( mEventData->mLeftSelectionPosition,
232 GetCursorPosition( mEventData->mRightSelectionPosition,
235 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
237 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
239 CursorInfo& infoLeft = leftHandleInfo;
241 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
242 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
244 CursorInfo& infoRight = rightHandleInfo;
246 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
247 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
251 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
253 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
254 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
259 if( mEventData->mUpdateLeftSelectionPosition )
261 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
265 mEventData->mDecoratorUpdated = true;
266 mEventData->mUpdateLeftSelectionPosition = false;
269 if( mEventData->mUpdateRightSelectionPosition )
271 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
275 mEventData->mDecoratorUpdated = true;
276 mEventData->mUpdateRightSelectionPosition = false;
279 if( mEventData->mUpdateHighlightBox )
281 RepositionSelectionHandles();
283 mEventData->mUpdateLeftSelectionPosition = false;
284 mEventData->mUpdateRightSelectionPosition = false;
285 mEventData->mUpdateHighlightBox = false;
286 mEventData->mIsLeftHandleSelected = false;
287 mEventData->mIsRightHandleSelected = false;
290 mEventData->mScrollAfterUpdatePosition = false;
293 if( mEventData->mUpdateInputStyle )
295 // Keep a copy of the current input style.
296 InputStyle currentInputStyle;
297 currentInputStyle.Copy( mEventData->mInputStyle );
299 // Set the default style first.
300 RetrieveDefaultInputStyle( mEventData->mInputStyle );
302 // Get the character index from the cursor index.
303 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
305 // Retrieve the style from the style runs stored in the logical model.
306 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
308 // Compare if the input style has changed.
309 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
311 if( hasInputStyleChanged )
313 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
314 // Queue the input style changed signal.
315 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
318 mEventData->mUpdateInputStyle = false;
321 mEventData->mEventQueue.clear();
323 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
325 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
326 mEventData->mDecoratorUpdated = false;
328 return decoratorUpdated;
331 void Controller::Impl::NotifyImfManager()
333 if( mEventData && mEventData->mImfManager )
335 CharacterIndex cursorPosition = GetLogicalCursorPosition();
337 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
339 // Update the cursor position by removing the initial white spaces.
340 if( cursorPosition < numberOfWhiteSpaces )
346 cursorPosition -= numberOfWhiteSpaces;
349 mEventData->mImfManager.SetCursorPosition( cursorPosition );
350 mEventData->mImfManager.NotifyCursorPosition();
354 void Controller::Impl::NotifyImfMultiLineStatus()
358 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
359 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
363 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
365 CharacterIndex cursorPosition = 0u;
369 if( ( EventData::SELECTING == mEventData->mState ) ||
370 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
372 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
376 cursorPosition = mEventData->mPrimaryCursorPosition;
380 return cursorPosition;
383 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
385 Length numberOfWhiteSpaces = 0u;
387 // Get the buffer to the text.
388 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
390 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
391 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
393 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
399 return numberOfWhiteSpaces;
402 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
404 // Get the total number of characters.
405 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
407 // Retrieve the text.
408 if( 0u != numberOfCharacters )
410 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
414 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
416 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
417 mTextUpdateInfo.mStartGlyphIndex = 0u;
418 mTextUpdateInfo.mStartLineIndex = 0u;
419 numberOfCharacters = 0u;
421 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
422 if( 0u == numberOfParagraphs )
424 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
425 numberOfCharacters = 0u;
427 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
429 // Nothing else to do if there are no paragraphs.
433 // Find the paragraphs to be updated.
434 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
435 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
437 // Text is being added at the end of the current text.
438 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
440 // Text is being added in a new paragraph after the last character of the text.
441 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
442 numberOfCharacters = 0u;
443 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
445 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
446 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
448 // Nothing else to do;
452 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
456 Length numberOfCharactersToUpdate = 0u;
457 if( mTextUpdateInfo.mFullRelayoutNeeded )
459 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
463 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
465 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
466 numberOfCharactersToUpdate,
467 paragraphsToBeUpdated );
470 if( 0u != paragraphsToBeUpdated.Count() )
472 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
473 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
474 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
476 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
477 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
479 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
480 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
481 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
482 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
484 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
485 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
487 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
495 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
496 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
499 void Controller::Impl::ClearFullModelData( OperationsMask operations )
501 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
503 mModel->mLogicalModel->mLineBreakInfo.Clear();
504 mModel->mLogicalModel->mParagraphInfo.Clear();
507 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
509 mModel->mLogicalModel->mLineBreakInfo.Clear();
512 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
514 mModel->mLogicalModel->mScriptRuns.Clear();
517 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
519 mModel->mLogicalModel->mFontRuns.Clear();
522 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
524 if( NO_OPERATION != ( BIDI_INFO & operations ) )
526 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
527 mModel->mLogicalModel->mCharacterDirections.Clear();
530 if( NO_OPERATION != ( REORDER & operations ) )
532 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
533 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
534 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
538 BidirectionalLineInfoRun& bidiLineInfo = *it;
540 free( bidiLineInfo.visualToLogicalMap );
541 bidiLineInfo.visualToLogicalMap = NULL;
543 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
547 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
549 mModel->mVisualModel->mGlyphs.Clear();
550 mModel->mVisualModel->mGlyphsToCharacters.Clear();
551 mModel->mVisualModel->mCharactersToGlyph.Clear();
552 mModel->mVisualModel->mCharactersPerGlyph.Clear();
553 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
554 mModel->mVisualModel->mGlyphPositions.Clear();
557 if( NO_OPERATION != ( LAYOUT & operations ) )
559 mModel->mVisualModel->mLines.Clear();
562 if( NO_OPERATION != ( COLOR & operations ) )
564 mModel->mVisualModel->mColorIndices.Clear();
568 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
570 const CharacterIndex endIndexPlusOne = endIndex + 1u;
572 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
574 // Clear the line break info.
575 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
577 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
578 lineBreakInfoBuffer + endIndexPlusOne );
580 // Clear the paragraphs.
581 ClearCharacterRuns( startIndex,
583 mModel->mLogicalModel->mParagraphInfo );
586 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
588 // Clear the word break info.
589 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
591 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
592 wordBreakInfoBuffer + endIndexPlusOne );
595 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
597 // Clear the scripts.
598 ClearCharacterRuns( startIndex,
600 mModel->mLogicalModel->mScriptRuns );
603 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
606 ClearCharacterRuns( startIndex,
608 mModel->mLogicalModel->mFontRuns );
611 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
613 if( NO_OPERATION != ( BIDI_INFO & operations ) )
615 // Clear the bidirectional paragraph info.
616 ClearCharacterRuns( startIndex,
618 mModel->mLogicalModel->mBidirectionalParagraphInfo );
620 // Clear the character's directions.
621 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
623 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
624 characterDirectionsBuffer + endIndexPlusOne );
627 if( NO_OPERATION != ( REORDER & operations ) )
629 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
630 uint32_t endRemoveIndex = startRemoveIndex;
631 ClearCharacterRuns( startIndex,
633 mModel->mLogicalModel->mBidirectionalLineInfo,
637 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
639 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
640 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
641 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
645 BidirectionalLineInfoRun& bidiLineInfo = *it;
647 free( bidiLineInfo.visualToLogicalMap );
648 bidiLineInfo.visualToLogicalMap = NULL;
651 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
652 bidirectionalLineInfoBuffer + endRemoveIndex );
657 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
659 const CharacterIndex endIndexPlusOne = endIndex + 1u;
660 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
662 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
663 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
664 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
666 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
667 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
669 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
671 // Update the character to glyph indices.
672 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
673 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
677 CharacterIndex& index = *it;
678 index -= numberOfGlyphsRemoved;
681 // Clear the character to glyph conversion table.
682 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
683 charactersToGlyphBuffer + endIndexPlusOne );
685 // Clear the glyphs per character table.
686 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
687 glyphsPerCharacterBuffer + endIndexPlusOne );
689 // Clear the glyphs buffer.
690 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
691 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
692 glyphsBuffer + endGlyphIndexPlusOne );
694 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
696 // Update the glyph to character indices.
697 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
698 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
702 CharacterIndex& index = *it;
703 index -= numberOfCharactersRemoved;
706 // Clear the glyphs to characters buffer.
707 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
708 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
710 // Clear the characters per glyph buffer.
711 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
712 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
713 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
715 // Clear the positions buffer.
716 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
717 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
718 positionsBuffer + endGlyphIndexPlusOne );
721 if( NO_OPERATION != ( LAYOUT & operations ) )
724 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
725 uint32_t endRemoveIndex = startRemoveIndex;
726 ClearCharacterRuns( startIndex,
728 mModel->mVisualModel->mLines,
732 // Will update the glyph runs.
733 startRemoveIndex = mModel->mVisualModel->mLines.Count();
734 endRemoveIndex = startRemoveIndex;
735 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
736 endGlyphIndexPlusOne - 1u,
737 mModel->mVisualModel->mLines,
741 // Set the line index from where to insert the new laid-out lines.
742 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
744 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
745 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
746 linesBuffer + endRemoveIndex );
749 if( NO_OPERATION != ( COLOR & operations ) )
751 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
753 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
754 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
755 colorIndexBuffer + endGlyphIndexPlusOne );
760 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
762 if( mTextUpdateInfo.mClearAll ||
763 ( ( 0u == startIndex ) &&
764 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
766 ClearFullModelData( operations );
770 // Clear the model data related with characters.
771 ClearCharacterModelData( startIndex, endIndex, operations );
773 // Clear the model data related with glyphs.
774 ClearGlyphModelData( startIndex, endIndex, operations );
777 // The estimated number of lines. Used to avoid reallocations when layouting.
778 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
780 mModel->mVisualModel->ClearCaches();
783 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
785 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
787 // Calculate the operations to be done.
788 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
790 if( NO_OPERATION == operations )
792 // Nothing to do if no operations are pending and required.
796 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
797 Vector<Character> displayCharacters;
798 bool useHiddenText = false;
799 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
801 mHiddenInput->Substitute( srcCharacters,displayCharacters );
802 useHiddenText = true;
805 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
806 const Length numberOfCharacters = utf32Characters.Count();
808 // Index to the first character of the first paragraph to be updated.
809 CharacterIndex startIndex = 0u;
810 // Number of characters of the paragraphs to be removed.
811 Length paragraphCharacters = 0u;
813 CalculateTextUpdateIndices( paragraphCharacters );
814 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
816 if( mTextUpdateInfo.mClearAll ||
817 ( 0u != paragraphCharacters ) )
819 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
822 mTextUpdateInfo.mClearAll = false;
824 // Whether the model is updated.
825 bool updated = false;
827 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
828 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
830 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
832 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
833 // calculate the bidirectional info for each 'paragraph'.
834 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
835 // is not shaped together).
836 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
838 SetLineBreakInfo( utf32Characters,
840 requestedNumberOfCharacters,
843 // Create the paragraph info.
844 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
845 requestedNumberOfCharacters );
849 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
850 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
852 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
853 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
855 SetWordBreakInfo( utf32Characters,
857 requestedNumberOfCharacters,
862 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
863 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
865 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
866 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
868 if( getScripts || validateFonts )
870 // Validates the fonts assigned by the application or assigns default ones.
871 // It makes sure all the characters are going to be rendered by the correct font.
872 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
876 // Retrieves the scripts used in the text.
877 multilanguageSupport.SetScripts( utf32Characters,
879 requestedNumberOfCharacters,
885 // Validate the fonts set through the mark-up string.
886 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
888 // Get the default font's description.
889 TextAbstraction::FontDescription defaultFontDescription;
890 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
892 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
894 // If the placeholder font is set specifically, only placeholder font is changed.
895 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
896 if( mEventData->mPlaceholderFont->sizeDefined )
898 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
901 else if( NULL != mFontDefaults )
903 // Set the normal font and the placeholder font.
904 defaultFontDescription = mFontDefaults->mFontDescription;
905 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
908 // Validates the fonts. If there is a character with no assigned font it sets a default one.
909 // After this call, fonts are validated.
910 multilanguageSupport.ValidateFonts( utf32Characters,
913 defaultFontDescription,
916 requestedNumberOfCharacters,
922 Vector<Character> mirroredUtf32Characters;
923 bool textMirrored = false;
924 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
925 if( NO_OPERATION != ( BIDI_INFO & operations ) )
927 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
928 bidirectionalInfo.Reserve( numberOfParagraphs );
930 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
931 SetBidirectionalInfo( utf32Characters,
935 requestedNumberOfCharacters,
938 if( 0u != bidirectionalInfo.Count() )
940 // Only set the character directions if there is right to left characters.
941 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
942 GetCharactersDirection( bidirectionalInfo,
945 requestedNumberOfCharacters,
948 // This paragraph has right to left text. Some characters may need to be mirrored.
949 // TODO: consider if the mirrored string can be stored as well.
951 textMirrored = GetMirroredText( utf32Characters,
955 requestedNumberOfCharacters,
956 mirroredUtf32Characters );
960 // There is no right to left characters. Clear the directions vector.
961 mModel->mLogicalModel->mCharacterDirections.Clear();
966 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
967 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
968 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
969 Vector<GlyphIndex> newParagraphGlyphs;
970 newParagraphGlyphs.Reserve( numberOfParagraphs );
972 const Length currentNumberOfGlyphs = glyphs.Count();
973 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
975 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
977 ShapeText( textToShape,
982 mTextUpdateInfo.mStartGlyphIndex,
983 requestedNumberOfCharacters,
985 glyphsToCharactersMap,
987 newParagraphGlyphs );
989 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
990 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
991 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
995 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
997 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
999 GlyphInfo* glyphsBuffer = glyphs.Begin();
1000 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1002 // Update the width and advance of all new paragraph characters.
1003 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1005 const GlyphIndex index = *it;
1006 GlyphInfo& glyph = *( glyphsBuffer + index );
1008 glyph.xBearing = 0.f;
1010 glyph.advance = 0.f;
1015 if( NO_OPERATION != ( COLOR & operations ) )
1017 // Set the color runs in glyphs.
1018 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1019 mModel->mVisualModel->mCharactersToGlyph,
1020 mModel->mVisualModel->mGlyphsPerCharacter,
1022 mTextUpdateInfo.mStartGlyphIndex,
1023 requestedNumberOfCharacters,
1024 mModel->mVisualModel->mColors,
1025 mModel->mVisualModel->mColorIndices );
1030 if( ( NULL != mEventData ) &&
1031 mEventData->mPreEditFlag &&
1032 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1034 // Add the underline for the pre-edit text.
1035 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1036 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1038 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1039 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1040 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1041 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1043 GlyphRun underlineRun;
1044 underlineRun.glyphIndex = glyphStart;
1045 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1047 // TODO: At the moment the underline runs are only for pre-edit.
1048 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1051 // The estimated number of lines. Used to avoid reallocations when layouting.
1052 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1054 // Set the previous number of characters for the next time the text is updated.
1055 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1060 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1062 // Sets the default text's color.
1063 inputStyle.textColor = mTextColor;
1064 inputStyle.isDefaultColor = true;
1066 inputStyle.familyName.clear();
1067 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1068 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1069 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1070 inputStyle.size = 0.f;
1072 inputStyle.lineSpacing = 0.f;
1074 inputStyle.underlineProperties.clear();
1075 inputStyle.shadowProperties.clear();
1076 inputStyle.embossProperties.clear();
1077 inputStyle.outlineProperties.clear();
1079 inputStyle.isFamilyDefined = false;
1080 inputStyle.isWeightDefined = false;
1081 inputStyle.isWidthDefined = false;
1082 inputStyle.isSlantDefined = false;
1083 inputStyle.isSizeDefined = false;
1085 inputStyle.isLineSpacingDefined = false;
1087 inputStyle.isUnderlineDefined = false;
1088 inputStyle.isShadowDefined = false;
1089 inputStyle.isEmbossDefined = false;
1090 inputStyle.isOutlineDefined = false;
1092 // Sets the default font's family name, weight, width, slant and size.
1095 if( mFontDefaults->familyDefined )
1097 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1098 inputStyle.isFamilyDefined = true;
1101 if( mFontDefaults->weightDefined )
1103 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1104 inputStyle.isWeightDefined = true;
1107 if( mFontDefaults->widthDefined )
1109 inputStyle.width = mFontDefaults->mFontDescription.width;
1110 inputStyle.isWidthDefined = true;
1113 if( mFontDefaults->slantDefined )
1115 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1116 inputStyle.isSlantDefined = true;
1119 if( mFontDefaults->sizeDefined )
1121 inputStyle.size = mFontDefaults->mDefaultPointSize;
1122 inputStyle.isSizeDefined = true;
1127 float Controller::Impl::GetDefaultFontLineHeight()
1129 FontId defaultFontId = 0u;
1130 if( NULL == mFontDefaults )
1132 TextAbstraction::FontDescription fontDescription;
1133 defaultFontId = mFontClient.GetFontId( fontDescription );
1137 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1140 Text::FontMetrics fontMetrics;
1141 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1143 return( fontMetrics.ascender - fontMetrics.descender );
1146 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1148 if( NULL == mEventData || !IsShowingRealText() )
1150 // Nothing to do if there is no text input.
1154 int keyCode = event.p1.mInt;
1155 bool isShiftModifier = event.p2.mBool;
1157 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1159 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1161 if( mEventData->mPrimaryCursorPosition > 0u )
1163 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1166 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1168 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1170 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1173 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1175 // Ignore Shift-Up for text selection for now.
1177 // Get first the line index of the current cursor position index.
1178 CharacterIndex characterIndex = 0u;
1180 if( mEventData->mPrimaryCursorPosition > 0u )
1182 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1185 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1186 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1188 // Retrieve the cursor position info.
1189 CursorInfo cursorInfo;
1190 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1193 // Get the line above.
1194 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1196 // Get the next hit 'y' point.
1197 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1199 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1200 bool matchedCharacter = false;
1201 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1202 mModel->mLogicalModel,
1204 mEventData->mCursorHookPositionX,
1206 CharacterHitTest::TAP,
1209 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1211 // Ignore Shift-Down for text selection for now.
1213 // Get first the line index of the current cursor position index.
1214 CharacterIndex characterIndex = 0u;
1216 if( mEventData->mPrimaryCursorPosition > 0u )
1218 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1221 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1223 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1225 // Retrieve the cursor position info.
1226 CursorInfo cursorInfo;
1227 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1230 // Get the line below.
1231 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1233 // Get the next hit 'y' point.
1234 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1236 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1237 bool matchedCharacter = false;
1238 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1239 mModel->mLogicalModel,
1241 mEventData->mCursorHookPositionX,
1243 CharacterHitTest::TAP,
1248 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1250 // Update selection position after moving the cursor
1251 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1252 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1255 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1257 // Handle text selection
1258 bool selecting = false;
1260 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1262 // Shift-Left/Right to select the text
1263 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1264 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1266 mEventData->mRightSelectionPosition += cursorPositionDelta;
1270 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1272 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1278 // Notify the cursor position to the imf manager.
1279 if( mEventData->mImfManager )
1281 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1282 mEventData->mImfManager.NotifyCursorPosition();
1285 ChangeState( EventData::SELECTING );
1287 mEventData->mUpdateLeftSelectionPosition = true;
1288 mEventData->mUpdateRightSelectionPosition = true;
1289 mEventData->mUpdateGrabHandlePosition = true;
1290 mEventData->mUpdateHighlightBox = true;
1292 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1293 if( mEventData->mGrabHandlePopupEnabled )
1295 mEventData->mDecorator->SetPopupActive( false );
1301 // Handle normal cursor move
1302 ChangeState( EventData::EDITING );
1303 mEventData->mUpdateCursorPosition = true;
1306 mEventData->mUpdateInputStyle = true;
1307 mEventData->mScrollAfterUpdatePosition = true;
1310 void Controller::Impl::OnTapEvent( const Event& event )
1312 if( NULL != mEventData )
1314 const unsigned int tapCount = event.p1.mUint;
1316 if( 1u == tapCount )
1318 if( IsShowingRealText() )
1320 // Convert from control's coords to text's coords.
1321 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1322 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1324 // Keep the tap 'x' position. Used to move the cursor.
1325 mEventData->mCursorHookPositionX = xPosition;
1327 // Whether to touch point hits on a glyph.
1328 bool matchedCharacter = false;
1329 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1330 mModel->mLogicalModel,
1334 CharacterHitTest::TAP,
1337 // When the cursor position is changing, delay cursor blinking
1338 mEventData->mDecorator->DelayCursorBlink();
1342 mEventData->mPrimaryCursorPosition = 0u;
1345 // Update selection position after tapping
1346 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1347 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1349 mEventData->mUpdateCursorPosition = true;
1350 mEventData->mUpdateGrabHandlePosition = true;
1351 mEventData->mScrollAfterUpdatePosition = true;
1352 mEventData->mUpdateInputStyle = true;
1354 // Notify the cursor position to the imf manager.
1355 if( mEventData->mImfManager )
1357 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1358 mEventData->mImfManager.NotifyCursorPosition();
1361 else if( 2u == tapCount )
1363 if( mEventData->mSelectionEnabled )
1365 // Convert from control's coords to text's coords.
1366 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1367 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1369 // Calculates the logical position from the x,y coords.
1370 RepositionSelectionHandles( xPosition,
1372 mEventData->mDoubleTapAction );
1378 void Controller::Impl::OnPanEvent( const Event& event )
1380 if( NULL == mEventData )
1382 // Nothing to do if there is no text input.
1386 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1387 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1389 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1391 // Nothing to do if scrolling is not enabled.
1395 const int state = event.p1.mInt;
1399 case Gesture::Started:
1401 // Will remove the cursor, handles or text's popup, ...
1402 ChangeState( EventData::TEXT_PANNING );
1405 case Gesture::Continuing:
1407 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1408 const Vector2 currentScroll = mModel->mScrollPosition;
1410 if( isHorizontalScrollEnabled )
1412 const float displacementX = event.p2.mFloat;
1413 mModel->mScrollPosition.x += displacementX;
1415 ClampHorizontalScroll( layoutSize );
1418 if( isVerticalScrollEnabled )
1420 const float displacementY = event.p3.mFloat;
1421 mModel->mScrollPosition.y += displacementY;
1423 ClampVerticalScroll( layoutSize );
1426 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1429 case Gesture::Finished:
1430 case Gesture::Cancelled: // FALLTHROUGH
1432 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1433 ChangeState( mEventData->mPreviousState );
1441 void Controller::Impl::OnLongPressEvent( const Event& event )
1443 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1445 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1447 ChangeState( EventData::EDITING_WITH_POPUP );
1448 mEventData->mDecoratorUpdated = true;
1449 mEventData->mUpdateInputStyle = true;
1453 if( mEventData->mSelectionEnabled )
1455 // Convert from control's coords to text's coords.
1456 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1457 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1459 // Calculates the logical position from the x,y coords.
1460 RepositionSelectionHandles( xPosition,
1462 mEventData->mLongPressAction );
1467 void Controller::Impl::OnHandleEvent( const Event& event )
1469 if( NULL == mEventData )
1471 // Nothing to do if there is no text input.
1475 const unsigned int state = event.p1.mUint;
1476 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1477 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1479 if( HANDLE_PRESSED == state )
1481 // Convert from decorator's coords to text's coords.
1482 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1483 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1485 // Need to calculate the handle's new position.
1486 bool matchedCharacter = false;
1487 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1488 mModel->mLogicalModel,
1492 CharacterHitTest::SCROLL,
1495 if( Event::GRAB_HANDLE_EVENT == event.type )
1497 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1499 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1501 // Updates the cursor position if the handle's new position is different than the current one.
1502 mEventData->mUpdateCursorPosition = true;
1503 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1504 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1505 mEventData->mPrimaryCursorPosition = handleNewPosition;
1508 // 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.
1509 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1511 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1513 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1515 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1516 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1518 // Updates the highlight box if the handle's new position is different than the current one.
1519 mEventData->mUpdateHighlightBox = true;
1520 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1521 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1522 mEventData->mLeftSelectionPosition = handleNewPosition;
1525 // 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.
1526 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1528 // Will define the order to scroll the text to match the handle position.
1529 mEventData->mIsLeftHandleSelected = true;
1530 mEventData->mIsRightHandleSelected = false;
1532 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1534 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1536 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1537 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1539 // Updates the highlight box if the handle's new position is different than the current one.
1540 mEventData->mUpdateHighlightBox = true;
1541 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1542 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1543 mEventData->mRightSelectionPosition = handleNewPosition;
1546 // 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.
1547 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1549 // Will define the order to scroll the text to match the handle position.
1550 mEventData->mIsLeftHandleSelected = false;
1551 mEventData->mIsRightHandleSelected = true;
1553 } // end ( HANDLE_PRESSED == state )
1554 else if( ( HANDLE_RELEASED == state ) ||
1555 handleStopScrolling )
1557 CharacterIndex handlePosition = 0u;
1558 if( handleStopScrolling || isSmoothHandlePanEnabled )
1560 // Convert from decorator's coords to text's coords.
1561 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1562 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1564 bool matchedCharacter = false;
1565 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1566 mModel->mLogicalModel,
1570 CharacterHitTest::SCROLL,
1574 if( Event::GRAB_HANDLE_EVENT == event.type )
1576 mEventData->mUpdateCursorPosition = true;
1577 mEventData->mUpdateGrabHandlePosition = true;
1578 mEventData->mUpdateInputStyle = true;
1580 if( !IsClipboardEmpty() )
1582 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1585 if( handleStopScrolling || isSmoothHandlePanEnabled )
1587 mEventData->mScrollAfterUpdatePosition = true;
1588 mEventData->mPrimaryCursorPosition = handlePosition;
1591 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1593 ChangeState( EventData::SELECTING );
1595 mEventData->mUpdateHighlightBox = true;
1596 mEventData->mUpdateLeftSelectionPosition = true;
1597 mEventData->mUpdateRightSelectionPosition = true;
1599 if( handleStopScrolling || isSmoothHandlePanEnabled )
1601 mEventData->mScrollAfterUpdatePosition = true;
1603 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1604 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1606 mEventData->mLeftSelectionPosition = handlePosition;
1610 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1612 ChangeState( EventData::SELECTING );
1614 mEventData->mUpdateHighlightBox = true;
1615 mEventData->mUpdateRightSelectionPosition = true;
1616 mEventData->mUpdateLeftSelectionPosition = true;
1618 if( handleStopScrolling || isSmoothHandlePanEnabled )
1620 mEventData->mScrollAfterUpdatePosition = true;
1621 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1622 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1624 mEventData->mRightSelectionPosition = handlePosition;
1629 mEventData->mDecoratorUpdated = true;
1630 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1631 else if( HANDLE_SCROLLING == state )
1633 const float xSpeed = event.p2.mFloat;
1634 const float ySpeed = event.p3.mFloat;
1635 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1636 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1638 mModel->mScrollPosition.x += xSpeed;
1639 mModel->mScrollPosition.y += ySpeed;
1641 ClampHorizontalScroll( layoutSize );
1642 ClampVerticalScroll( layoutSize );
1644 bool endOfScroll = false;
1645 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1647 // Notify the decorator there is no more text to scroll.
1648 // The decorator won't send more scroll events.
1649 mEventData->mDecorator->NotifyEndOfScroll();
1650 // Still need to set the position of the handle.
1654 // Set the position of the handle.
1655 const bool scrollRightDirection = xSpeed > 0.f;
1656 const bool scrollBottomDirection = ySpeed > 0.f;
1657 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1658 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1660 if( Event::GRAB_HANDLE_EVENT == event.type )
1662 ChangeState( EventData::GRAB_HANDLE_PANNING );
1664 // Get the grab handle position in decorator coords.
1665 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1667 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1669 // Position the grag handle close to either the left or right edge.
1670 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1673 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1675 position.x = mEventData->mCursorHookPositionX;
1677 // Position the grag handle close to either the top or bottom edge.
1678 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1681 // Get the new handle position.
1682 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1683 bool matchedCharacter = false;
1684 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1685 mModel->mLogicalModel,
1687 position.x - mModel->mScrollPosition.x,
1688 position.y - mModel->mScrollPosition.y,
1689 CharacterHitTest::SCROLL,
1692 if( mEventData->mPrimaryCursorPosition != handlePosition )
1694 mEventData->mUpdateCursorPosition = true;
1695 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1696 mEventData->mScrollAfterUpdatePosition = true;
1697 mEventData->mPrimaryCursorPosition = handlePosition;
1699 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1701 // Updates the decorator if the soft handle panning is enabled.
1702 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1704 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1706 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1708 // Get the selection handle position in decorator coords.
1709 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1711 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1713 // Position the selection handle close to either the left or right edge.
1714 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1717 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1719 position.x = mEventData->mCursorHookPositionX;
1721 // Position the grag handle close to either the top or bottom edge.
1722 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1725 // Get the new handle position.
1726 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1727 bool matchedCharacter = false;
1728 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1729 mModel->mLogicalModel,
1731 position.x - mModel->mScrollPosition.x,
1732 position.y - mModel->mScrollPosition.y,
1733 CharacterHitTest::SCROLL,
1736 if( leftSelectionHandleEvent )
1738 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1740 if( differentHandles || endOfScroll )
1742 mEventData->mUpdateHighlightBox = true;
1743 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1744 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1745 mEventData->mLeftSelectionPosition = handlePosition;
1750 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1751 if( differentHandles || endOfScroll )
1753 mEventData->mUpdateHighlightBox = true;
1754 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1755 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1756 mEventData->mRightSelectionPosition = handlePosition;
1760 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1762 RepositionSelectionHandles();
1764 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1767 mEventData->mDecoratorUpdated = true;
1768 } // end ( HANDLE_SCROLLING == state )
1771 void Controller::Impl::OnSelectEvent( const Event& event )
1773 if( NULL == mEventData )
1775 // Nothing to do if there is no text.
1779 if( mEventData->mSelectionEnabled )
1781 // Convert from control's coords to text's coords.
1782 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1783 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1785 // Calculates the logical position from the x,y coords.
1786 RepositionSelectionHandles( xPosition,
1788 Controller::NoTextTap::HIGHLIGHT );
1792 void Controller::Impl::OnSelectAllEvent()
1794 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1796 if( NULL == mEventData )
1798 // Nothing to do if there is no text.
1802 if( mEventData->mSelectionEnabled )
1804 ChangeState( EventData::SELECTING );
1806 mEventData->mLeftSelectionPosition = 0u;
1807 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1809 mEventData->mScrollAfterUpdatePosition = true;
1810 mEventData->mUpdateLeftSelectionPosition = true;
1811 mEventData->mUpdateRightSelectionPosition = true;
1812 mEventData->mUpdateHighlightBox = true;
1816 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1818 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1820 // Nothing to select if handles are in the same place.
1821 selectedText.clear();
1825 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1827 //Get start and end position of selection
1828 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1829 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1831 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1832 const Length numberOfCharacters = utf32Characters.Count();
1834 // Validate the start and end selection points
1835 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1837 //Get text as a UTF8 string
1838 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1840 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1842 // Keep a copy of the current input style.
1843 InputStyle currentInputStyle;
1844 currentInputStyle.Copy( mEventData->mInputStyle );
1846 // Set as input style the style of the first deleted character.
1847 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1849 // Compare if the input style has changed.
1850 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1852 if( hasInputStyleChanged )
1854 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1855 // Queue the input style changed signal.
1856 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1859 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1861 // Mark the paragraphs to be updated.
1862 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1864 mTextUpdateInfo.mCharacterIndex = 0;
1865 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1866 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1867 mTextUpdateInfo.mClearAll = true;
1871 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1872 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1875 // Delete text between handles
1876 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1877 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1878 utf32Characters.Erase( first, last );
1880 // Will show the cursor at the first character of the selection.
1881 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1885 // Will show the cursor at the last character of the selection.
1886 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1889 mEventData->mDecoratorUpdated = true;
1893 void Controller::Impl::ShowClipboard()
1897 mClipboard.ShowClipboard();
1901 void Controller::Impl::HideClipboard()
1903 if( mClipboard && mClipboardHideEnabled )
1905 mClipboard.HideClipboard();
1909 void Controller::Impl::SetClipboardHideEnable(bool enable)
1911 mClipboardHideEnabled = enable;
1914 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1916 //Send string to clipboard
1917 return ( mClipboard && mClipboard.SetItem( source ) );
1920 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1922 std::string selectedText;
1923 RetrieveSelection( selectedText, deleteAfterSending );
1924 CopyStringToClipboard( selectedText );
1925 ChangeState( EventData::EDITING );
1928 void Controller::Impl::RequestGetTextFromClipboard()
1932 mClipboard.RequestItem();
1936 void Controller::Impl::RepositionSelectionHandles()
1938 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1939 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1941 if( selectionStart == selectionEnd )
1943 // Nothing to select if handles are in the same place.
1947 mEventData->mDecorator->ClearHighlights();
1949 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1950 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1951 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1952 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1953 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1954 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1955 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1957 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1958 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1959 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1961 // Swap the indices if the start is greater than the end.
1962 const bool indicesSwapped = selectionStart > selectionEnd;
1964 // Tell the decorator to flip the selection handles if needed.
1965 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1967 if( indicesSwapped )
1969 std::swap( selectionStart, selectionEnd );
1972 // Get the indices to the first and last selected glyphs.
1973 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1974 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1975 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1976 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1978 // Get the lines where the glyphs are laid-out.
1979 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1981 LineIndex lineIndex = 0u;
1982 Length numberOfLines = 0u;
1983 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1984 1u + glyphEnd - glyphStart,
1987 const LineIndex firstLineIndex = lineIndex;
1989 // Create the structure to store some selection box info.
1990 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1991 selectionBoxLinesInfo.Resize( numberOfLines );
1993 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1994 selectionBoxInfo->minX = MAX_FLOAT;
1995 selectionBoxInfo->maxX = MIN_FLOAT;
1997 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1998 float minHighlightX = std::numeric_limits<float>::max();
1999 float maxHighlightX = std::numeric_limits<float>::min();
2001 Vector2 highLightPosition; // The highlight position in decorator's coords.
2003 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2005 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2006 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2009 // Transform to decorator's (control) coords.
2010 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2012 lineRun += firstLineIndex;
2014 // The line height is the addition of the line ascender and the line descender.
2015 // However, the line descender has a negative value, hence the subtraction.
2016 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2018 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2020 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2021 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2022 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2024 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2025 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2026 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2028 // The number of quads of the selection box.
2029 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2030 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2032 // Count the actual number of quads.
2033 unsigned int actualNumberOfQuads = 0u;
2036 // Traverse the glyphs.
2037 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2039 const GlyphInfo& glyph = *( glyphsBuffer + index );
2040 const Vector2& position = *( positionsBuffer + index );
2042 if( splitStartGlyph )
2044 // 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.
2046 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2047 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2048 // Get the direction of the character.
2049 CharacterDirection isCurrentRightToLeft = false;
2050 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2052 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2055 // The end point could be in the middle of the ligature.
2056 // Calculate the number of characters selected.
2057 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2059 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2060 quad.y = selectionBoxInfo->lineOffset;
2061 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2062 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2064 // Store the min and max 'x' for each line.
2065 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2066 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2068 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2069 ++actualNumberOfQuads;
2071 splitStartGlyph = false;
2075 if( splitEndGlyph && ( index == glyphEnd ) )
2077 // 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.
2079 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2080 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2081 // Get the direction of the character.
2082 CharacterDirection isCurrentRightToLeft = false;
2083 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2085 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2088 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2090 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2091 quad.y = selectionBoxInfo->lineOffset;
2092 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2093 quad.w = quad.y + selectionBoxInfo->lineHeight;
2095 // Store the min and max 'x' for each line.
2096 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2097 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2099 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2101 ++actualNumberOfQuads;
2103 splitEndGlyph = false;
2107 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2108 quad.y = selectionBoxInfo->lineOffset;
2109 quad.z = quad.x + glyph.advance;
2110 quad.w = quad.y + selectionBoxInfo->lineHeight;
2112 // Store the min and max 'x' for each line.
2113 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2114 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2116 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2118 ++actualNumberOfQuads;
2120 // Whether to retrieve the next line.
2121 if( index == lastGlyphOfLine )
2124 if( lineIndex < firstLineIndex + numberOfLines )
2126 // Retrieve the next line.
2129 // Get the last glyph of the new line.
2130 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2132 // Keep the offset and height of the current selection box.
2133 const float currentLineOffset = selectionBoxInfo->lineOffset;
2134 const float currentLineHeight = selectionBoxInfo->lineHeight;
2136 // Get the selection box info for the next line.
2139 selectionBoxInfo->minX = MAX_FLOAT;
2140 selectionBoxInfo->maxX = MIN_FLOAT;
2142 // Update the line's vertical offset.
2143 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2145 // The line height is the addition of the line ascender and the line descender.
2146 // However, the line descender has a negative value, hence the subtraction.
2147 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2152 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2153 // The final width is calculated after 'boxifying' the selection.
2154 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2155 endIt = selectionBoxLinesInfo.End();
2159 const SelectionBoxInfo& info = *it;
2161 // Update the size of the highlighted text.
2162 highLightSize.height += info.lineHeight;
2163 minHighlightX = std::min( minHighlightX, info.minX );
2164 maxHighlightX = std::max( maxHighlightX, info.maxX );
2167 // Add extra geometry to 'boxify' the selection.
2169 if( 1u < numberOfLines )
2171 // Boxify the first line.
2172 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2173 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2175 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2176 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2181 quad.y = firstSelectionBoxLineInfo.lineOffset;
2182 quad.z = firstSelectionBoxLineInfo.minX;
2183 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2185 // Boxify at the beginning of the line.
2186 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2188 ++actualNumberOfQuads;
2190 // Update the size of the highlighted text.
2191 minHighlightX = 0.f;
2196 quad.x = firstSelectionBoxLineInfo.maxX;
2197 quad.y = firstSelectionBoxLineInfo.lineOffset;
2198 quad.z = mModel->mVisualModel->mControlSize.width;
2199 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2201 // Boxify at the end of the line.
2202 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2204 ++actualNumberOfQuads;
2206 // Update the size of the highlighted text.
2207 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2210 // Boxify the central lines.
2211 if( 2u < numberOfLines )
2213 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2214 endIt = selectionBoxLinesInfo.End() - 1u;
2218 const SelectionBoxInfo& info = *it;
2221 quad.y = info.lineOffset;
2223 quad.w = info.lineOffset + info.lineHeight;
2225 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2227 ++actualNumberOfQuads;
2230 quad.y = info.lineOffset;
2231 quad.z = mModel->mVisualModel->mControlSize.width;
2232 quad.w = info.lineOffset + info.lineHeight;
2234 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2236 ++actualNumberOfQuads;
2239 // Update the size of the highlighted text.
2240 minHighlightX = 0.f;
2241 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2244 // Boxify the last line.
2245 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2246 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2248 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2249 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2254 quad.y = lastSelectionBoxLineInfo.lineOffset;
2255 quad.z = lastSelectionBoxLineInfo.minX;
2256 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2258 // Boxify at the beginning of the line.
2259 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2261 ++actualNumberOfQuads;
2263 // Update the size of the highlighted text.
2264 minHighlightX = 0.f;
2269 quad.x = lastSelectionBoxLineInfo.maxX;
2270 quad.y = lastSelectionBoxLineInfo.lineOffset;
2271 quad.z = mModel->mVisualModel->mControlSize.width;
2272 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2274 // Boxify at the end of the line.
2275 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2277 ++actualNumberOfQuads;
2279 // Update the size of the highlighted text.
2280 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2284 // Set the actual number of quads.
2285 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2287 // Sets the highlight's size and position. In decorator's coords.
2288 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2289 highLightSize.width = maxHighlightX - minHighlightX;
2291 highLightPosition.x = minHighlightX;
2292 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2293 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2295 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2297 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2299 CursorInfo primaryCursorInfo;
2300 GetCursorPosition( mEventData->mLeftSelectionPosition,
2301 primaryCursorInfo );
2303 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2305 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2307 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2308 primaryCursorInfo.lineHeight );
2310 CursorInfo secondaryCursorInfo;
2311 GetCursorPosition( mEventData->mRightSelectionPosition,
2312 secondaryCursorInfo );
2314 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2316 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2317 secondaryPosition.x,
2318 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2319 secondaryCursorInfo.lineHeight );
2322 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2323 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2325 // Set the flag to update the decorator.
2326 mEventData->mDecoratorUpdated = true;
2329 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2331 if( NULL == mEventData )
2333 // Nothing to do if there is no text input.
2337 if( IsShowingPlaceholderText() )
2339 // Nothing to do if there is the place-holder text.
2343 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2344 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2345 if( ( 0 == numberOfGlyphs ) ||
2346 ( 0 == numberOfLines ) )
2348 // Nothing to do if there is no text.
2352 // Find which word was selected
2353 CharacterIndex selectionStart( 0 );
2354 CharacterIndex selectionEnd( 0 );
2355 CharacterIndex noTextHitIndex( 0 );
2356 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2357 mModel->mLogicalModel,
2364 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2366 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2368 ChangeState( EventData::SELECTING );
2370 mEventData->mLeftSelectionPosition = selectionStart;
2371 mEventData->mRightSelectionPosition = selectionEnd;
2373 mEventData->mUpdateLeftSelectionPosition = true;
2374 mEventData->mUpdateRightSelectionPosition = true;
2375 mEventData->mUpdateHighlightBox = true;
2377 // It may happen an IMF commit event arrives before the selection event
2378 // if the IMF manager is in pre-edit state. The commit event will set the
2379 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2380 // to false, the highlight box won't be updated.
2381 mEventData->mUpdateCursorPosition = false;
2383 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2385 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2387 // Nothing to select. i.e. a white space, out of bounds
2388 ChangeState( EventData::EDITING_WITH_POPUP );
2390 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2392 mEventData->mUpdateCursorPosition = true;
2393 mEventData->mUpdateGrabHandlePosition = true;
2394 mEventData->mScrollAfterUpdatePosition = true;
2395 mEventData->mUpdateInputStyle = true;
2397 else if( Controller::NoTextTap::NO_ACTION == action )
2399 // Nothing to select. i.e. a white space, out of bounds
2400 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2402 mEventData->mUpdateCursorPosition = true;
2403 mEventData->mUpdateGrabHandlePosition = true;
2404 mEventData->mScrollAfterUpdatePosition = true;
2405 mEventData->mUpdateInputStyle = true;
2409 void Controller::Impl::SetPopupButtons()
2412 * Sets the Popup buttons to be shown depending on State.
2414 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2416 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2419 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2421 if( EventData::SELECTING == mEventData->mState )
2423 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2425 if( !IsClipboardEmpty() )
2427 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2428 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2431 if( !mEventData->mAllTextSelected )
2433 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2436 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2438 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2440 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2443 if( !IsClipboardEmpty() )
2445 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2446 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2449 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2451 if ( !IsClipboardEmpty() )
2453 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2454 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2458 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2461 void Controller::Impl::ChangeState( EventData::State newState )
2463 if( NULL == mEventData )
2465 // Nothing to do if there is no text input.
2469 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2471 if( mEventData->mState != newState )
2473 mEventData->mPreviousState = mEventData->mState;
2474 mEventData->mState = newState;
2476 switch( mEventData->mState )
2478 case EventData::INACTIVE:
2480 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2481 mEventData->mDecorator->StopCursorBlink();
2482 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2483 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2484 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2485 mEventData->mDecorator->SetHighlightActive( false );
2486 mEventData->mDecorator->SetPopupActive( false );
2487 mEventData->mDecoratorUpdated = true;
2490 case EventData::INTERRUPTED:
2492 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2493 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2494 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2495 mEventData->mDecorator->SetHighlightActive( false );
2496 mEventData->mDecorator->SetPopupActive( false );
2497 mEventData->mDecoratorUpdated = true;
2500 case EventData::SELECTING:
2502 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2503 mEventData->mDecorator->StopCursorBlink();
2504 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2505 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2506 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2507 mEventData->mDecorator->SetHighlightActive( true );
2508 if( mEventData->mGrabHandlePopupEnabled )
2511 mEventData->mDecorator->SetPopupActive( true );
2513 mEventData->mDecoratorUpdated = true;
2516 case EventData::EDITING:
2518 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2519 if( mEventData->mCursorBlinkEnabled )
2521 mEventData->mDecorator->StartCursorBlink();
2523 // Grab handle is not shown until a tap is received whilst EDITING
2524 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2525 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2526 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2527 mEventData->mDecorator->SetHighlightActive( false );
2528 if( mEventData->mGrabHandlePopupEnabled )
2530 mEventData->mDecorator->SetPopupActive( false );
2532 mEventData->mDecoratorUpdated = true;
2535 case EventData::EDITING_WITH_POPUP:
2537 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2539 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2540 if( mEventData->mCursorBlinkEnabled )
2542 mEventData->mDecorator->StartCursorBlink();
2544 if( mEventData->mSelectionEnabled )
2546 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2547 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2548 mEventData->mDecorator->SetHighlightActive( false );
2552 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2554 if( mEventData->mGrabHandlePopupEnabled )
2557 mEventData->mDecorator->SetPopupActive( true );
2559 mEventData->mDecoratorUpdated = true;
2562 case EventData::EDITING_WITH_GRAB_HANDLE:
2564 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2566 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2567 if( mEventData->mCursorBlinkEnabled )
2569 mEventData->mDecorator->StartCursorBlink();
2571 // Grab handle is not shown until a tap is received whilst EDITING
2572 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2573 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2574 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2575 mEventData->mDecorator->SetHighlightActive( false );
2576 if( mEventData->mGrabHandlePopupEnabled )
2578 mEventData->mDecorator->SetPopupActive( false );
2580 mEventData->mDecoratorUpdated = true;
2583 case EventData::SELECTION_HANDLE_PANNING:
2585 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2586 mEventData->mDecorator->StopCursorBlink();
2587 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2588 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2589 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2590 mEventData->mDecorator->SetHighlightActive( true );
2591 if( mEventData->mGrabHandlePopupEnabled )
2593 mEventData->mDecorator->SetPopupActive( false );
2595 mEventData->mDecoratorUpdated = true;
2598 case EventData::GRAB_HANDLE_PANNING:
2600 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2602 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2603 if( mEventData->mCursorBlinkEnabled )
2605 mEventData->mDecorator->StartCursorBlink();
2607 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2608 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2609 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2610 mEventData->mDecorator->SetHighlightActive( false );
2611 if( mEventData->mGrabHandlePopupEnabled )
2613 mEventData->mDecorator->SetPopupActive( false );
2615 mEventData->mDecoratorUpdated = true;
2618 case EventData::EDITING_WITH_PASTE_POPUP:
2620 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2622 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2623 if( mEventData->mCursorBlinkEnabled )
2625 mEventData->mDecorator->StartCursorBlink();
2628 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2629 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2630 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2631 mEventData->mDecorator->SetHighlightActive( false );
2633 if( mEventData->mGrabHandlePopupEnabled )
2636 mEventData->mDecorator->SetPopupActive( true );
2638 mEventData->mDecoratorUpdated = true;
2641 case EventData::TEXT_PANNING:
2643 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2644 mEventData->mDecorator->StopCursorBlink();
2645 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2646 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2647 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2649 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2650 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2651 mEventData->mDecorator->SetHighlightActive( true );
2654 if( mEventData->mGrabHandlePopupEnabled )
2656 mEventData->mDecorator->SetPopupActive( false );
2659 mEventData->mDecoratorUpdated = true;
2666 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2667 CursorInfo& cursorInfo )
2669 if( !IsShowingRealText() )
2671 // Do not want to use the place-holder text to set the cursor position.
2673 // Use the line's height of the font's family set to set the cursor's size.
2674 // If there is no font's family set, use the default font.
2675 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2677 cursorInfo.lineOffset = 0.f;
2678 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2679 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2681 switch( mModel->mHorizontalAlignment )
2683 case Text::HorizontalAlignment::BEGIN :
2685 cursorInfo.primaryPosition.x = 0.f;
2688 case Text::HorizontalAlignment::CENTER:
2690 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2693 case Text::HorizontalAlignment::END:
2695 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2700 // Nothing else to do.
2704 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2705 GetCursorPositionParameters parameters;
2706 parameters.visualModel = mModel->mVisualModel;
2707 parameters.logicalModel = mModel->mLogicalModel;
2708 parameters.metrics = mMetrics;
2709 parameters.logical = logical;
2710 parameters.isMultiline = isMultiLine;
2712 Text::GetCursorPosition( parameters,
2717 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2719 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2720 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2722 if( 0.f > cursorInfo.primaryPosition.x )
2724 cursorInfo.primaryPosition.x = 0.f;
2727 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2728 if( cursorInfo.primaryPosition.x > edgeWidth )
2730 cursorInfo.primaryPosition.x = edgeWidth;
2735 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2737 if( NULL == mEventData )
2739 // Nothing to do if there is no text input.
2743 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2745 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2746 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2748 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2749 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2751 if( numberOfCharacters > 1u )
2753 const Script script = mModel->mLogicalModel->GetScript( index );
2754 if( HasLigatureMustBreak( script ) )
2756 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2757 numberOfCharacters = 1u;
2762 while( 0u == numberOfCharacters )
2765 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2769 if( index < mEventData->mPrimaryCursorPosition )
2771 cursorIndex -= numberOfCharacters;
2775 cursorIndex += numberOfCharacters;
2778 // Will update the cursor hook position.
2779 mEventData->mUpdateCursorHookPosition = true;
2784 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2786 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2787 if( NULL == mEventData )
2789 // Nothing to do if there is no text input.
2790 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2794 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2796 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2798 // Sets the cursor position.
2799 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2802 cursorInfo.primaryCursorHeight,
2803 cursorInfo.lineHeight );
2804 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2806 if( mEventData->mUpdateGrabHandlePosition )
2808 // Sets the grab handle position.
2809 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2811 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2812 cursorInfo.lineHeight );
2815 if( cursorInfo.isSecondaryCursor )
2817 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2818 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2819 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2820 cursorInfo.secondaryCursorHeight,
2821 cursorInfo.lineHeight );
2822 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2825 // Set which cursors are active according the state.
2826 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2828 if( cursorInfo.isSecondaryCursor )
2830 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2834 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2839 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2842 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2845 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2846 const CursorInfo& cursorInfo )
2848 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2849 ( RIGHT_SELECTION_HANDLE != handleType ) )
2854 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2856 // Sets the handle's position.
2857 mEventData->mDecorator->SetPosition( handleType,
2859 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2860 cursorInfo.lineHeight );
2862 // If selection handle at start of the text and other at end of the text then all text is selected.
2863 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2864 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2865 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2868 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2870 // Clamp between -space & -alignment offset.
2872 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2874 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2875 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2876 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2878 mEventData->mDecoratorUpdated = true;
2882 mModel->mScrollPosition.x = 0.f;
2886 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2888 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2890 // Nothing to do if the text is single line.
2894 // Clamp between -space & 0.
2895 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2897 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2898 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2899 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2901 mEventData->mDecoratorUpdated = true;
2905 mModel->mScrollPosition.y = 0.f;
2909 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2911 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2913 // position is in actor's coords.
2914 const float positionEndX = position.x + cursorWidth;
2915 const float positionEndY = position.y + lineHeight;
2917 // Transform the position to decorator coords.
2918 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2919 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2921 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2922 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2924 if( decoratorPositionBeginX < 0.f )
2926 mModel->mScrollPosition.x = -position.x;
2928 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2930 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2933 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2935 if( decoratorPositionBeginY < 0.f )
2937 mModel->mScrollPosition.y = -position.y;
2939 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2941 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2946 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2948 // Get the current cursor position in decorator coords.
2949 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2951 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2955 // Calculate the offset to match the cursor position before the character was deleted.
2956 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2958 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2959 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2961 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2962 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2966 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2967 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2969 // Makes the new cursor position visible if needed.
2970 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2973 void Controller::Impl::RequestRelayout()
2975 if( NULL != mControlInterface )
2977 mControlInterface->RequestTextRelayout();
2983 } // namespace Toolkit