2 * Copyright (c) 2015 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-run-container.h>
40 * @brief Struct used to calculate the selection box.
42 struct SelectionBoxInfo
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
54 const float MAX_FLOAT = std::numeric_limits<float>::max();
55 const float MIN_FLOAT = std::numeric_limits<float>::min();
56 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
69 EventData::EventData( DecoratorPtr decorator )
70 : mDecorator( decorator ),
72 mPlaceholderTextActive(),
73 mPlaceholderTextInactive(),
74 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77 mPrimaryCursorPosition( 0u ),
78 mLeftSelectionPosition( 0u ),
79 mRightSelectionPosition( 0u ),
80 mPreEditStartPosition( 0u ),
82 mCursorHookPositionX( 0.f ),
83 mIsShowingPlaceholderText( false ),
84 mPreEditFlag( false ),
85 mDecoratorUpdated( false ),
86 mCursorBlinkEnabled( true ),
87 mGrabHandleEnabled( true ),
88 mGrabHandlePopupEnabled( true ),
89 mSelectionEnabled( true ),
90 mUpdateCursorPosition( false ),
91 mUpdateGrabHandlePosition( false ),
92 mUpdateLeftSelectionPosition( false ),
93 mUpdateRightSelectionPosition( false ),
94 mUpdateHighlightBox( false ),
95 mScrollAfterUpdatePosition( false ),
96 mScrollAfterDelete( false ),
97 mAllTextSelected( false ),
98 mUpdateInputStyle( false )
100 mImfManager = ImfManager::Get();
103 EventData::~EventData()
106 bool Controller::Impl::ProcessInputEvents()
108 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
109 if( NULL == mEventData )
111 // Nothing to do if there is no text input.
112 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
116 if( mEventData->mDecorator )
118 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
119 iter != mEventData->mEventQueue.end();
124 case Event::CURSOR_KEY_EVENT:
126 OnCursorKeyEvent( *iter );
129 case Event::TAP_EVENT:
134 case Event::LONG_PRESS_EVENT:
136 OnLongPressEvent( *iter );
139 case Event::PAN_EVENT:
144 case Event::GRAB_HANDLE_EVENT:
145 case Event::LEFT_SELECTION_HANDLE_EVENT:
146 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
148 OnHandleEvent( *iter );
153 OnSelectEvent( *iter );
156 case Event::SELECT_ALL:
165 if( mEventData->mUpdateCursorPosition ||
166 mEventData->mUpdateHighlightBox )
171 // The cursor must also be repositioned after inserts into the model
172 if( mEventData->mUpdateCursorPosition )
174 // Updates the cursor position and scrolls the text to make it visible.
175 CursorInfo cursorInfo;
176 // Calculate the cursor position from the new cursor index.
177 GetCursorPosition( mEventData->mPrimaryCursorPosition,
180 if( mEventData->mUpdateCursorHookPosition )
182 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
183 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
184 mEventData->mUpdateCursorHookPosition = false;
187 // Scroll first the text after delete ...
188 if( mEventData->mScrollAfterDelete )
190 ScrollTextToMatchCursor( cursorInfo );
193 // ... then, text can be scrolled to make the cursor visible.
194 if( mEventData->mScrollAfterUpdatePosition )
196 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
197 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
199 mEventData->mScrollAfterUpdatePosition = false;
200 mEventData->mScrollAfterDelete = false;
202 UpdateCursorPosition( cursorInfo );
204 mEventData->mDecoratorUpdated = true;
205 mEventData->mUpdateCursorPosition = false;
206 mEventData->mUpdateGrabHandlePosition = false;
210 CursorInfo leftHandleInfo;
211 CursorInfo rightHandleInfo;
213 if( mEventData->mUpdateHighlightBox )
215 GetCursorPosition( mEventData->mLeftSelectionPosition,
218 GetCursorPosition( mEventData->mRightSelectionPosition,
221 if( mEventData->mScrollAfterUpdatePosition && mEventData->mUpdateLeftSelectionPosition )
223 const Vector2 currentCursorPosition( leftHandleInfo.primaryPosition.x, leftHandleInfo.lineOffset );
224 ScrollToMakePositionVisible( currentCursorPosition, leftHandleInfo.lineHeight );
227 if( mEventData->mScrollAfterUpdatePosition && mEventData->mUpdateRightSelectionPosition )
229 const Vector2 currentCursorPosition( rightHandleInfo.primaryPosition.x, rightHandleInfo.lineOffset );
230 ScrollToMakePositionVisible( currentCursorPosition, rightHandleInfo.lineHeight );
234 if( mEventData->mUpdateLeftSelectionPosition )
236 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
240 mEventData->mDecoratorUpdated = true;
241 mEventData->mUpdateLeftSelectionPosition = false;
244 if( mEventData->mUpdateRightSelectionPosition )
246 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
250 mEventData->mDecoratorUpdated = true;
251 mEventData->mUpdateRightSelectionPosition = false;
254 if( mEventData->mUpdateHighlightBox )
256 RepositionSelectionHandles();
258 mEventData->mUpdateLeftSelectionPosition = false;
259 mEventData->mUpdateRightSelectionPosition = false;
260 mEventData->mUpdateHighlightBox = false;
263 mEventData->mScrollAfterUpdatePosition = false;
266 if( mEventData->mUpdateInputStyle )
268 // Set the default style first.
269 RetrieveDefaultInputStyle( mEventData->mInputStyle );
271 // Get the character index from the cursor index.
272 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
274 // Retrieve the style from the style runs stored in the logical model.
275 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
277 mEventData->mUpdateInputStyle = false;
280 mEventData->mEventQueue.clear();
282 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
284 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
285 mEventData->mDecoratorUpdated = false;
287 return decoratorUpdated;
290 void Controller::Impl::NotifyImfManager()
292 if( mEventData && mEventData->mImfManager )
294 CharacterIndex cursorPosition = GetLogicalCursorPosition();
296 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
298 // Update the cursor position by removing the initial white spaces.
299 if( cursorPosition < numberOfWhiteSpaces )
305 cursorPosition -= numberOfWhiteSpaces;
308 mEventData->mImfManager.SetCursorPosition( cursorPosition );
309 mEventData->mImfManager.NotifyCursorPosition();
313 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
315 CharacterIndex cursorPosition = 0u;
319 if( ( EventData::SELECTING == mEventData->mState ) ||
320 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
322 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
326 cursorPosition = mEventData->mPrimaryCursorPosition;
330 return cursorPosition;
333 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
335 Length numberOfWhiteSpaces = 0u;
337 // Get the buffer to the text.
338 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
340 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
341 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
343 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
349 return numberOfWhiteSpaces;
352 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
354 // Get the total number of characters.
355 Length numberOfCharacters = mLogicalModel->mText.Count();
357 // Retrieve the text.
358 if( 0u != numberOfCharacters )
360 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
364 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
366 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
367 mTextUpdateInfo.mStartGlyphIndex = 0u;
368 mTextUpdateInfo.mStartLineIndex = 0u;
369 numberOfCharacters = 0u;
371 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
372 if( 0u == numberOfParagraphs )
374 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
375 numberOfCharacters = 0u;
377 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
379 // Nothing else to do if there are no paragraphs.
383 // Find the paragraphs to be updated.
384 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
385 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
387 // Text is being added at the end of the current text.
388 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
390 // Text is being added in a new paragraph after the last character of the text.
391 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
392 numberOfCharacters = 0u;
393 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
395 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
396 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
398 // Nothing else to do;
402 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
406 Length numberOfCharactersToUpdate = 0u;
407 if( mTextUpdateInfo.mFullRelayoutNeeded )
409 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
413 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
415 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
416 numberOfCharactersToUpdate,
417 paragraphsToBeUpdated );
420 if( 0u != paragraphsToBeUpdated.Count() )
422 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
423 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
424 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
426 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
427 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
429 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
430 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
431 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
432 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
434 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
435 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
437 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
441 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
445 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
446 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
449 void Controller::Impl::ClearFullModelData( OperationsMask operations )
451 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
453 mLogicalModel->mLineBreakInfo.Clear();
454 mLogicalModel->mParagraphInfo.Clear();
457 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
459 mLogicalModel->mLineBreakInfo.Clear();
462 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
464 mLogicalModel->mScriptRuns.Clear();
467 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
469 mLogicalModel->mFontRuns.Clear();
472 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
474 if( NO_OPERATION != ( BIDI_INFO & operations ) )
476 mLogicalModel->mBidirectionalParagraphInfo.Clear();
477 mLogicalModel->mCharacterDirections.Clear();
480 if( NO_OPERATION != ( REORDER & operations ) )
482 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
483 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
484 endIt = mLogicalModel->mBidirectionalLineInfo.End();
488 BidirectionalLineInfoRun& bidiLineInfo = *it;
490 free( bidiLineInfo.visualToLogicalMap );
491 bidiLineInfo.visualToLogicalMap = NULL;
493 mLogicalModel->mBidirectionalLineInfo.Clear();
497 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
499 mVisualModel->mGlyphs.Clear();
500 mVisualModel->mGlyphsToCharacters.Clear();
501 mVisualModel->mCharactersToGlyph.Clear();
502 mVisualModel->mCharactersPerGlyph.Clear();
503 mVisualModel->mGlyphsPerCharacter.Clear();
504 mVisualModel->mGlyphPositions.Clear();
507 if( NO_OPERATION != ( LAYOUT & operations ) )
509 mVisualModel->mLines.Clear();
512 if( NO_OPERATION != ( COLOR & operations ) )
514 mVisualModel->mColorIndices.Clear();
518 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
520 const CharacterIndex endIndexPlusOne = endIndex + 1u;
522 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
524 // Clear the line break info.
525 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
527 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
528 lineBreakInfoBuffer + endIndexPlusOne );
530 // Clear the paragraphs.
531 ClearCharacterRuns( startIndex,
533 mLogicalModel->mParagraphInfo );
536 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
538 // Clear the word break info.
539 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
541 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
542 wordBreakInfoBuffer + endIndexPlusOne );
545 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
547 // Clear the scripts.
548 ClearCharacterRuns( startIndex,
550 mLogicalModel->mScriptRuns );
553 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
556 ClearCharacterRuns( startIndex,
558 mLogicalModel->mFontRuns );
561 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
563 if( NO_OPERATION != ( BIDI_INFO & operations ) )
565 // Clear the bidirectional paragraph info.
566 ClearCharacterRuns( startIndex,
568 mLogicalModel->mBidirectionalParagraphInfo );
570 // Clear the character's directions.
571 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
573 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
574 characterDirectionsBuffer + endIndexPlusOne );
577 if( NO_OPERATION != ( REORDER & operations ) )
579 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
580 uint32_t endRemoveIndex = startRemoveIndex;
581 ClearCharacterRuns( startIndex,
583 mLogicalModel->mBidirectionalLineInfo,
587 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
589 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
590 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
591 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
595 BidirectionalLineInfoRun& bidiLineInfo = *it;
597 free( bidiLineInfo.visualToLogicalMap );
598 bidiLineInfo.visualToLogicalMap = NULL;
601 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
602 bidirectionalLineInfoBuffer + endRemoveIndex );
607 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
609 const CharacterIndex endIndexPlusOne = endIndex + 1u;
610 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
612 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
613 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
614 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
616 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
617 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
619 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
621 // Update the character to glyph indices.
622 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
623 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
627 CharacterIndex& index = *it;
628 index -= numberOfGlyphsRemoved;
631 // Clear the character to glyph conversion table.
632 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
633 charactersToGlyphBuffer + endIndexPlusOne );
635 // Clear the glyphs per character table.
636 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
637 glyphsPerCharacterBuffer + endIndexPlusOne );
639 // Clear the glyphs buffer.
640 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
641 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
642 glyphsBuffer + endGlyphIndexPlusOne );
644 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
646 // Update the glyph to character indices.
647 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
648 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
652 CharacterIndex& index = *it;
653 index -= numberOfCharactersRemoved;
656 // Clear the glyphs to characters buffer.
657 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
658 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
660 // Clear the characters per glyph buffer.
661 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
662 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
663 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
665 // Clear the positions buffer.
666 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
667 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
668 positionsBuffer + endGlyphIndexPlusOne );
671 if( NO_OPERATION != ( LAYOUT & operations ) )
674 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
675 uint32_t endRemoveIndex = startRemoveIndex;
676 ClearCharacterRuns( startIndex,
678 mVisualModel->mLines,
682 // Will update the glyph runs.
683 startRemoveIndex = mVisualModel->mLines.Count();
684 endRemoveIndex = startRemoveIndex;
685 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
686 endGlyphIndexPlusOne - 1u,
687 mVisualModel->mLines,
691 // Set the line index from where to insert the new laid-out lines.
692 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
694 LineRun* linesBuffer = mVisualModel->mLines.Begin();
695 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
696 linesBuffer + endRemoveIndex );
699 if( NO_OPERATION != ( COLOR & operations ) )
701 if( 0u != mVisualModel->mColorIndices.Count() )
703 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
704 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
705 colorIndexBuffer + endGlyphIndexPlusOne );
710 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
712 if( mTextUpdateInfo.mClearAll ||
713 ( ( 0u == startIndex ) &&
714 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
716 ClearFullModelData( operations );
720 // Clear the model data related with characters.
721 ClearCharacterModelData( startIndex, endIndex, operations );
723 // Clear the model data related with glyphs.
724 ClearGlyphModelData( startIndex, endIndex, operations );
727 // The estimated number of lines. Used to avoid reallocations when layouting.
728 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
730 mVisualModel->ClearCaches();
733 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
735 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
737 // Calculate the operations to be done.
738 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
740 if( NO_OPERATION == operations )
742 // Nothing to do if no operations are pending and required.
746 Vector<Character>& utf32Characters = mLogicalModel->mText;
748 const Length numberOfCharacters = utf32Characters.Count();
750 // Index to the first character of the first paragraph to be updated.
751 CharacterIndex startIndex = 0u;
752 // Number of characters of the paragraphs to be removed.
753 Length paragraphCharacters = 0u;
755 CalculateTextUpdateIndices( paragraphCharacters );
756 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
758 if( mTextUpdateInfo.mClearAll ||
759 ( 0u != paragraphCharacters ) )
761 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
764 mTextUpdateInfo.mClearAll = false;
766 // Whether the model is updated.
767 bool updated = false;
769 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
770 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
772 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
774 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
775 // calculate the bidirectional info for each 'paragraph'.
776 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
777 // is not shaped together).
778 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
780 SetLineBreakInfo( utf32Characters,
782 requestedNumberOfCharacters,
785 // Create the paragraph info.
786 mLogicalModel->CreateParagraphInfo( startIndex,
787 requestedNumberOfCharacters );
791 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
792 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
794 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
795 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
797 SetWordBreakInfo( utf32Characters,
799 requestedNumberOfCharacters,
804 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
805 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
807 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
808 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
810 if( getScripts || validateFonts )
812 // Validates the fonts assigned by the application or assigns default ones.
813 // It makes sure all the characters are going to be rendered by the correct font.
814 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
818 // Retrieves the scripts used in the text.
819 multilanguageSupport.SetScripts( utf32Characters,
821 requestedNumberOfCharacters,
827 // Validate the fonts set through the mark-up string.
828 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
830 // Get the default font id.
831 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
833 // Validates the fonts. If there is a character with no assigned font it sets a default one.
834 // After this call, fonts are validated.
835 multilanguageSupport.ValidateFonts( utf32Characters,
840 requestedNumberOfCharacters,
846 Vector<Character> mirroredUtf32Characters;
847 bool textMirrored = false;
848 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
849 if( NO_OPERATION != ( BIDI_INFO & operations ) )
851 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
852 bidirectionalInfo.Reserve( numberOfParagraphs );
854 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
855 SetBidirectionalInfo( utf32Characters,
859 requestedNumberOfCharacters,
862 if( 0u != bidirectionalInfo.Count() )
864 // Only set the character directions if there is right to left characters.
865 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
866 GetCharactersDirection( bidirectionalInfo,
869 requestedNumberOfCharacters,
872 // This paragraph has right to left text. Some characters may need to be mirrored.
873 // TODO: consider if the mirrored string can be stored as well.
875 textMirrored = GetMirroredText( utf32Characters,
879 requestedNumberOfCharacters,
880 mirroredUtf32Characters );
884 // There is no right to left characters. Clear the directions vector.
885 mLogicalModel->mCharacterDirections.Clear();
890 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
891 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
892 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
893 Vector<GlyphIndex> newParagraphGlyphs;
894 newParagraphGlyphs.Reserve( numberOfParagraphs );
896 const Length currentNumberOfGlyphs = glyphs.Count();
897 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
899 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
901 ShapeText( textToShape,
906 mTextUpdateInfo.mStartGlyphIndex,
907 requestedNumberOfCharacters,
909 glyphsToCharactersMap,
911 newParagraphGlyphs );
913 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
914 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
915 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
919 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
921 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
923 GlyphInfo* glyphsBuffer = glyphs.Begin();
924 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
926 // Update the width and advance of all new paragraph characters.
927 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
929 const GlyphIndex index = *it;
930 GlyphInfo& glyph = *( glyphsBuffer + index );
932 glyph.xBearing = 0.f;
939 if( NO_OPERATION != ( COLOR & operations ) )
941 // Set the color runs in glyphs.
942 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
943 mVisualModel->mCharactersToGlyph,
944 mVisualModel->mGlyphsPerCharacter,
946 mTextUpdateInfo.mStartGlyphIndex,
947 requestedNumberOfCharacters,
948 mVisualModel->mColors,
949 mVisualModel->mColorIndices );
954 if( ( NULL != mEventData ) &&
955 mEventData->mPreEditFlag &&
956 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
958 // Add the underline for the pre-edit text.
959 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
960 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
962 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
963 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
964 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
965 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
967 GlyphRun underlineRun;
968 underlineRun.glyphIndex = glyphStart;
969 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
971 // TODO: At the moment the underline runs are only for pre-edit.
972 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
975 // The estimated number of lines. Used to avoid reallocations when layouting.
976 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
978 // Set the previous number of characters for the next time the text is updated.
979 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
984 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
986 // Sets the default text's color.
987 inputStyle.textColor = mTextColor;
988 inputStyle.isDefaultColor = true;
990 inputStyle.familyName.clear();
991 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
992 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
993 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
994 inputStyle.size = 0.f;
996 inputStyle.familyDefined = false;
997 inputStyle.weightDefined = false;
998 inputStyle.widthDefined = false;
999 inputStyle.slantDefined = false;
1000 inputStyle.sizeDefined = false;
1002 // Sets the default font's family name, weight, width, slant and size.
1005 if( mFontDefaults->familyDefined )
1007 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1008 inputStyle.familyDefined = true;
1011 if( mFontDefaults->weightDefined )
1013 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1014 inputStyle.weightDefined = true;
1017 if( mFontDefaults->widthDefined )
1019 inputStyle.width = mFontDefaults->mFontDescription.width;
1020 inputStyle.widthDefined = true;
1023 if( mFontDefaults->slantDefined )
1025 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1026 inputStyle.slantDefined = true;
1029 if( mFontDefaults->sizeDefined )
1031 inputStyle.size = mFontDefaults->mDefaultPointSize;
1032 inputStyle.sizeDefined = true;
1037 float Controller::Impl::GetDefaultFontLineHeight()
1039 FontId defaultFontId = 0u;
1040 if( NULL == mFontDefaults )
1042 TextAbstraction::FontDescription fontDescription;
1043 defaultFontId = mFontClient.GetFontId( fontDescription );
1047 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1050 Text::FontMetrics fontMetrics;
1051 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1053 return( fontMetrics.ascender - fontMetrics.descender );
1056 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1058 if( NULL == mEventData )
1060 // Nothing to do if there is no text input.
1064 int keyCode = event.p1.mInt;
1066 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1068 if( mEventData->mPrimaryCursorPosition > 0u )
1070 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1073 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1075 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1077 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1080 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1082 // Get first the line index of the current cursor position index.
1083 CharacterIndex characterIndex = 0u;
1085 if( mEventData->mPrimaryCursorPosition > 0u )
1087 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1090 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1092 if( lineIndex > 0u )
1094 // Retrieve the cursor position info.
1095 CursorInfo cursorInfo;
1096 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1099 // Get the line above.
1100 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1102 // Get the next hit 'y' point.
1103 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1105 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1106 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1109 mEventData->mCursorHookPositionX,
1113 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1115 // Get first the line index of the current cursor position index.
1116 CharacterIndex characterIndex = 0u;
1118 if( mEventData->mPrimaryCursorPosition > 0u )
1120 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1123 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1125 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1127 // Retrieve the cursor position info.
1128 CursorInfo cursorInfo;
1129 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1132 // Get the line below.
1133 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1135 // Get the next hit 'y' point.
1136 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1138 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1139 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1142 mEventData->mCursorHookPositionX,
1147 mEventData->mUpdateCursorPosition = true;
1148 mEventData->mUpdateInputStyle = true;
1149 mEventData->mScrollAfterUpdatePosition = true;
1152 void Controller::Impl::OnTapEvent( const Event& event )
1154 if( NULL != mEventData )
1156 const unsigned int tapCount = event.p1.mUint;
1158 if( 1u == tapCount )
1160 if( IsShowingRealText() )
1162 // Convert from control's coords to text's coords.
1163 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1164 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1166 // Keep the tap 'x' position. Used to move the cursor.
1167 mEventData->mCursorHookPositionX = xPosition;
1169 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1175 // When the cursor position is changing, delay cursor blinking
1176 mEventData->mDecorator->DelayCursorBlink();
1180 mEventData->mPrimaryCursorPosition = 0u;
1183 mEventData->mUpdateCursorPosition = true;
1184 mEventData->mUpdateGrabHandlePosition = true;
1185 mEventData->mScrollAfterUpdatePosition = true;
1186 mEventData->mUpdateInputStyle = true;
1188 // Notify the cursor position to the imf manager.
1189 if( mEventData->mImfManager )
1191 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1192 mEventData->mImfManager.NotifyCursorPosition();
1198 void Controller::Impl::OnPanEvent( const Event& event )
1200 if( NULL == mEventData )
1202 // Nothing to do if there is no text input.
1206 int state = event.p1.mInt;
1208 if( ( Gesture::Started == state ) ||
1209 ( Gesture::Continuing == state ) )
1211 if( mEventData->mDecorator )
1213 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1214 const Vector2 currentScroll = mScrollPosition;
1216 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1218 const float displacementX = event.p2.mFloat;
1219 mScrollPosition.x += displacementX;
1221 ClampHorizontalScroll( layoutSize );
1224 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1226 const float displacementY = event.p3.mFloat;
1227 mScrollPosition.y += displacementY;
1229 ClampVerticalScroll( layoutSize );
1232 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1237 void Controller::Impl::OnLongPressEvent( const Event& event )
1239 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1241 if( EventData::EDITING == mEventData->mState )
1243 ChangeState ( EventData::EDITING_WITH_POPUP );
1244 mEventData->mDecoratorUpdated = true;
1248 void Controller::Impl::OnHandleEvent( const Event& event )
1250 if( NULL == mEventData )
1252 // Nothing to do if there is no text input.
1256 const unsigned int state = event.p1.mUint;
1257 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1258 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1260 if( HANDLE_PRESSED == state )
1262 // Convert from decorator's coords to text's coords.
1263 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1264 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1266 // Need to calculate the handle's new position.
1267 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1273 if( Event::GRAB_HANDLE_EVENT == event.type )
1275 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1277 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1279 // Updates the cursor position if the handle's new position is different than the current one.
1280 mEventData->mUpdateCursorPosition = true;
1281 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1282 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1283 mEventData->mPrimaryCursorPosition = handleNewPosition;
1286 // 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.
1287 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1289 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1291 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1293 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1294 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1296 // Updates the highlight box if the handle's new position is different than the current one.
1297 mEventData->mUpdateHighlightBox = true;
1298 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1299 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1300 mEventData->mLeftSelectionPosition = handleNewPosition;
1303 // 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.
1304 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1306 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1308 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1310 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1311 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1313 // Updates the highlight box if the handle's new position is different than the current one.
1314 mEventData->mUpdateHighlightBox = true;
1315 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1316 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1317 mEventData->mRightSelectionPosition = handleNewPosition;
1320 // 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.
1321 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1323 } // end ( HANDLE_PRESSED == state )
1324 else if( ( HANDLE_RELEASED == state ) ||
1325 handleStopScrolling )
1327 CharacterIndex handlePosition = 0u;
1328 if( handleStopScrolling || isSmoothHandlePanEnabled )
1330 // Convert from decorator's coords to text's coords.
1331 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1332 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1334 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1341 if( Event::GRAB_HANDLE_EVENT == event.type )
1343 mEventData->mUpdateCursorPosition = true;
1344 mEventData->mUpdateGrabHandlePosition = true;
1345 mEventData->mUpdateInputStyle = true;
1347 if( !IsClipboardEmpty() )
1349 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1352 if( handleStopScrolling || isSmoothHandlePanEnabled )
1354 mEventData->mScrollAfterUpdatePosition = true;
1355 mEventData->mPrimaryCursorPosition = handlePosition;
1358 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1360 ChangeState( EventData::SELECTING );
1362 mEventData->mUpdateHighlightBox = true;
1363 mEventData->mUpdateLeftSelectionPosition = true;
1365 if( handleStopScrolling || isSmoothHandlePanEnabled )
1367 mEventData->mScrollAfterUpdatePosition = true;
1369 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1370 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1372 mEventData->mLeftSelectionPosition = handlePosition;
1376 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1378 ChangeState( EventData::SELECTING );
1380 mEventData->mUpdateHighlightBox = true;
1381 mEventData->mUpdateRightSelectionPosition = true;
1383 if( handleStopScrolling || isSmoothHandlePanEnabled )
1385 mEventData->mScrollAfterUpdatePosition = true;
1386 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1387 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1389 mEventData->mRightSelectionPosition = handlePosition;
1394 mEventData->mDecoratorUpdated = true;
1395 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1396 else if( HANDLE_SCROLLING == state )
1398 const float xSpeed = event.p2.mFloat;
1399 const float ySpeed = event.p3.mFloat;
1400 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1401 const Vector2 currentScrollPosition = mScrollPosition;
1403 mScrollPosition.x += xSpeed;
1404 mScrollPosition.y += ySpeed;
1406 ClampHorizontalScroll( layoutSize );
1407 ClampVerticalScroll( layoutSize );
1409 bool endOfScroll = false;
1410 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1412 // Notify the decorator there is no more text to scroll.
1413 // The decorator won't send more scroll events.
1414 mEventData->mDecorator->NotifyEndOfScroll();
1415 // Still need to set the position of the handle.
1419 // Set the position of the handle.
1420 const bool scrollRightDirection = xSpeed > 0.f;
1421 const bool scrollBottomDirection = ySpeed > 0.f;
1422 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1423 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1425 if( Event::GRAB_HANDLE_EVENT == event.type )
1427 ChangeState( EventData::GRAB_HANDLE_PANNING );
1429 // Get the grab handle position in decorator coords.
1430 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1432 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1434 // Position the grag handle close to either the left or right edge.
1435 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1438 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1440 position.x = mEventData->mCursorHookPositionX;
1442 // Position the grag handle close to either the top or bottom edge.
1443 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1446 // Get the new handle position.
1447 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1448 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1451 position.x - mScrollPosition.x,
1452 position.y - mScrollPosition.y );
1454 if( mEventData->mPrimaryCursorPosition != handlePosition )
1456 mEventData->mUpdateCursorPosition = true;
1457 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1458 mEventData->mScrollAfterUpdatePosition = true;
1459 mEventData->mPrimaryCursorPosition = handlePosition;
1461 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1463 // Updates the decorator if the soft handle panning is enabled.
1464 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1466 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1468 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1470 // Get the selection handle position in decorator coords.
1471 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1473 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1475 // Position the selection handle close to either the left or right edge.
1476 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1479 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1481 position.x = mEventData->mCursorHookPositionX;
1483 // Position the grag handle close to either the top or bottom edge.
1484 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1487 // Get the new handle position.
1488 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1489 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1492 position.x - mScrollPosition.x,
1493 position.y - mScrollPosition.y );
1495 if( leftSelectionHandleEvent )
1497 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1499 if( differentHandles || endOfScroll )
1501 mEventData->mUpdateHighlightBox = true;
1502 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1503 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1504 mEventData->mLeftSelectionPosition = handlePosition;
1509 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1510 if( differentHandles || endOfScroll )
1512 mEventData->mUpdateHighlightBox = true;
1513 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1514 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1515 mEventData->mRightSelectionPosition = handlePosition;
1519 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1521 RepositionSelectionHandles();
1523 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1526 mEventData->mDecoratorUpdated = true;
1527 } // end ( HANDLE_SCROLLING == state )
1530 void Controller::Impl::OnSelectEvent( const Event& event )
1532 if( NULL == mEventData )
1534 // Nothing to do if there is no text.
1538 if( mEventData->mSelectionEnabled )
1540 // Convert from control's coords to text's coords.
1541 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1542 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1544 // Calculates the logical position from the x,y coords.
1545 RepositionSelectionHandles( xPosition,
1548 mEventData->mUpdateLeftSelectionPosition = true;
1549 mEventData->mUpdateRightSelectionPosition = true;
1550 mEventData->mUpdateHighlightBox = true;
1551 mEventData->mUpdateCursorPosition = false;
1553 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1557 void Controller::Impl::OnSelectAllEvent()
1559 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1561 if( NULL == mEventData )
1563 // Nothing to do if there is no text.
1567 if( mEventData->mSelectionEnabled )
1569 mEventData->mLeftSelectionPosition = 0u;
1570 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1572 mEventData->mScrollAfterUpdatePosition = true;
1573 mEventData->mUpdateLeftSelectionPosition = true;
1574 mEventData->mUpdateRightSelectionPosition = true;
1575 mEventData->mUpdateHighlightBox = true;
1579 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1581 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1583 // Nothing to select if handles are in the same place.
1584 selectedText.clear();
1588 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1590 //Get start and end position of selection
1591 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1592 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1594 Vector<Character>& utf32Characters = mLogicalModel->mText;
1595 const Length numberOfCharacters = utf32Characters.Count();
1597 // Validate the start and end selection points
1598 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1600 //Get text as a UTF8 string
1601 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1603 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1605 // Set as input style the style of the first deleted character.
1606 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1608 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1610 // Mark the paragraphs to be updated.
1611 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1612 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1614 // Delete text between handles
1615 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1616 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1617 utf32Characters.Erase( first, last );
1619 // Will show the cursor at the first character of the selection.
1620 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1624 // Will show the cursor at the last character of the selection.
1625 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1628 mEventData->mDecoratorUpdated = true;
1632 void Controller::Impl::ShowClipboard()
1636 mClipboard.ShowClipboard();
1640 void Controller::Impl::HideClipboard()
1642 if( mClipboard && mClipboardHideEnabled )
1644 mClipboard.HideClipboard();
1648 void Controller::Impl::SetClipboardHideEnable(bool enable)
1650 mClipboardHideEnabled = enable;
1653 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1655 //Send string to clipboard
1656 return ( mClipboard && mClipboard.SetItem( source ) );
1659 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1661 std::string selectedText;
1662 RetrieveSelection( selectedText, deleteAfterSending );
1663 CopyStringToClipboard( selectedText );
1664 ChangeState( EventData::EDITING );
1667 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1671 retrievedString = mClipboard.GetItem( itemIndex );
1675 void Controller::Impl::RepositionSelectionHandles()
1677 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1678 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1680 if( selectionStart == selectionEnd )
1682 // Nothing to select if handles are in the same place.
1686 mEventData->mDecorator->ClearHighlights();
1688 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1689 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1690 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1691 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1692 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1693 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1694 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1696 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1697 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1698 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1700 // Swap the indices if the start is greater than the end.
1701 const bool indicesSwapped = selectionStart > selectionEnd;
1703 // Tell the decorator to flip the selection handles if needed.
1704 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1706 if( indicesSwapped )
1708 std::swap( selectionStart, selectionEnd );
1711 // Get the indices to the first and last selected glyphs.
1712 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1713 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1714 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1715 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1717 // Get the lines where the glyphs are laid-out.
1718 const LineRun* lineRun = mVisualModel->mLines.Begin();
1720 LineIndex lineIndex = 0u;
1721 Length numberOfLines = 0u;
1722 mVisualModel->GetNumberOfLines( glyphStart,
1723 1u + glyphEnd - glyphStart,
1726 const LineIndex firstLineIndex = lineIndex;
1728 // Create the structure to store some selection box info.
1729 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1730 selectionBoxLinesInfo.Resize( numberOfLines );
1732 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1733 selectionBoxInfo->minX = MAX_FLOAT;
1734 selectionBoxInfo->maxX = MIN_FLOAT;
1736 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1738 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1739 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1741 selectionBoxInfo->lineOffset += mScrollPosition.y;
1743 lineRun += firstLineIndex;
1745 // The line height is the addition of the line ascender and the line descender.
1746 // However, the line descender has a negative value, hence the subtraction.
1747 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1749 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1751 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1752 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1753 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1755 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1756 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1757 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1759 // Traverse the glyphs.
1760 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1762 const GlyphInfo& glyph = *( glyphsBuffer + index );
1763 const Vector2& position = *( positionsBuffer + index );
1765 if( splitStartGlyph )
1767 // 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.
1769 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1770 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1771 // Get the direction of the character.
1772 CharacterDirection isCurrentRightToLeft = false;
1773 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1775 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1778 // The end point could be in the middle of the ligature.
1779 // Calculate the number of characters selected.
1780 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1782 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1783 const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1784 const float yPosition = selectionBoxInfo->lineOffset;
1786 // Store the min and max 'x' for each line.
1787 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1788 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1790 mEventData->mDecorator->AddHighlight( xPosition,
1793 yPosition + selectionBoxInfo->lineHeight );
1795 splitStartGlyph = false;
1799 if( splitEndGlyph && ( index == glyphEnd ) )
1801 // 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.
1803 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1804 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1805 // Get the direction of the character.
1806 CharacterDirection isCurrentRightToLeft = false;
1807 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1809 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1812 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1814 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1815 const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1816 const float yPosition = selectionBoxInfo->lineOffset;
1818 // Store the min and max 'x' for each line.
1819 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1820 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1822 mEventData->mDecorator->AddHighlight( xPosition,
1825 yPosition + selectionBoxInfo->lineHeight );
1827 splitEndGlyph = false;
1831 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1832 const float xPositionAdvance = xPosition + glyph.advance;
1833 const float yPosition = selectionBoxInfo->lineOffset;
1835 // Store the min and max 'x' for each line.
1836 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1837 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1839 mEventData->mDecorator->AddHighlight( xPosition,
1842 yPosition + selectionBoxInfo->lineHeight );
1844 // Whether to retrieve the next line.
1845 if( index == lastGlyphOfLine )
1847 // Retrieve the next line.
1850 // Get the last glyph of the new line.
1851 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1854 if( lineIndex < firstLineIndex + numberOfLines )
1856 // Keep the offset and height of the current selection box.
1857 const float currentLineOffset = selectionBoxInfo->lineOffset;
1858 const float currentLineHeight = selectionBoxInfo->lineHeight;
1860 // Get the selection box info for the next line.
1863 selectionBoxInfo->minX = MAX_FLOAT;
1864 selectionBoxInfo->maxX = MIN_FLOAT;
1866 // Update the line's vertical offset.
1867 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1869 // The line height is the addition of the line ascender and the line descender.
1870 // However, the line descender has a negative value, hence the subtraction.
1871 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1876 // Add extra geometry to 'boxify' the selection.
1878 if( 1u < numberOfLines )
1880 // Boxify the first line.
1881 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1882 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1884 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1885 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1889 // Boxify at the beginning of the line.
1890 mEventData->mDecorator->AddHighlight( 0.f,
1891 firstSelectionBoxLineInfo.lineOffset,
1892 firstSelectionBoxLineInfo.minX,
1893 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1898 // Boxify at the end of the line.
1899 mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1900 firstSelectionBoxLineInfo.lineOffset,
1901 mVisualModel->mControlSize.width,
1902 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1905 // Boxify the central lines.
1906 if( 2u < numberOfLines )
1908 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1909 endIt = selectionBoxLinesInfo.End() - 1u;
1913 const SelectionBoxInfo& info = *it;
1915 mEventData->mDecorator->AddHighlight( 0.f,
1918 info.lineOffset + info.lineHeight );
1920 mEventData->mDecorator->AddHighlight( info.maxX,
1922 mVisualModel->mControlSize.width,
1923 info.lineOffset + info.lineHeight );
1927 // Boxify the last line.
1928 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1929 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1931 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1932 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1936 // Boxify at the beginning of the line.
1937 mEventData->mDecorator->AddHighlight( 0.f,
1938 lastSelectionBoxLineInfo.lineOffset,
1939 lastSelectionBoxLineInfo.minX,
1940 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1945 // Boxify at the end of the line.
1946 mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1947 lastSelectionBoxLineInfo.lineOffset,
1948 mVisualModel->mControlSize.width,
1949 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1953 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
1955 CursorInfo primaryCursorInfo;
1956 GetCursorPosition( mEventData->mLeftSelectionPosition,
1957 primaryCursorInfo );
1959 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1961 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1963 primaryCursorInfo.lineOffset + mScrollPosition.y,
1964 primaryCursorInfo.lineHeight );
1966 CursorInfo secondaryCursorInfo;
1967 GetCursorPosition( mEventData->mRightSelectionPosition,
1968 secondaryCursorInfo );
1970 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1972 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1973 secondaryPosition.x,
1974 secondaryCursorInfo.lineOffset + mScrollPosition.y,
1975 secondaryCursorInfo.lineHeight );
1978 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1979 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1981 // Set the flag to update the decorator.
1982 mEventData->mDecoratorUpdated = true;
1985 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1987 if( NULL == mEventData )
1989 // Nothing to do if there is no text input.
1993 if( IsShowingPlaceholderText() )
1995 // Nothing to do if there is the place-holder text.
1999 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2000 const Length numberOfLines = mVisualModel->mLines.Count();
2001 if( ( 0 == numberOfGlyphs ) ||
2002 ( 0 == numberOfLines ) )
2004 // Nothing to do if there is no text.
2008 // Find which word was selected
2009 CharacterIndex selectionStart( 0 );
2010 CharacterIndex selectionEnd( 0 );
2011 FindSelectionIndices( mVisualModel,
2018 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2020 if( selectionStart == selectionEnd )
2022 ChangeState( EventData::EDITING );
2023 // Nothing to select. i.e. a white space, out of bounds
2027 mEventData->mLeftSelectionPosition = selectionStart;
2028 mEventData->mRightSelectionPosition = selectionEnd;
2031 void Controller::Impl::SetPopupButtons()
2034 * Sets the Popup buttons to be shown depending on State.
2036 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2038 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2041 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2043 if( EventData::SELECTING == mEventData->mState )
2045 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2047 if( !IsClipboardEmpty() )
2049 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2050 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2053 if( !mEventData->mAllTextSelected )
2055 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2058 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2060 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2062 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2065 if( !IsClipboardEmpty() )
2067 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2068 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2071 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2073 if ( !IsClipboardEmpty() )
2075 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2076 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2080 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2083 void Controller::Impl::ChangeState( EventData::State newState )
2085 if( NULL == mEventData )
2087 // Nothing to do if there is no text input.
2091 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2093 if( mEventData->mState != newState )
2095 mEventData->mState = newState;
2097 if( EventData::INACTIVE == mEventData->mState )
2099 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2100 mEventData->mDecorator->StopCursorBlink();
2101 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2102 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2103 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2104 mEventData->mDecorator->SetPopupActive( false );
2105 mEventData->mDecoratorUpdated = true;
2108 else if( EventData::INTERRUPTED == mEventData->mState)
2110 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2111 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2112 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2113 mEventData->mDecorator->SetPopupActive( false );
2114 mEventData->mDecoratorUpdated = true;
2117 else if( EventData::SELECTING == mEventData->mState )
2119 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2120 mEventData->mDecorator->StopCursorBlink();
2121 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2122 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2123 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2124 if( mEventData->mGrabHandlePopupEnabled )
2127 mEventData->mDecorator->SetPopupActive( true );
2129 mEventData->mDecoratorUpdated = true;
2131 else if( EventData::EDITING == mEventData->mState )
2133 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2134 if( mEventData->mCursorBlinkEnabled )
2136 mEventData->mDecorator->StartCursorBlink();
2138 // Grab handle is not shown until a tap is received whilst EDITING
2139 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2140 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2141 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2142 if( mEventData->mGrabHandlePopupEnabled )
2144 mEventData->mDecorator->SetPopupActive( false );
2146 mEventData->mDecoratorUpdated = true;
2149 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2151 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2153 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2154 if( mEventData->mCursorBlinkEnabled )
2156 mEventData->mDecorator->StartCursorBlink();
2158 if( mEventData->mSelectionEnabled )
2160 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2161 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2165 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2167 if( mEventData->mGrabHandlePopupEnabled )
2170 mEventData->mDecorator->SetPopupActive( true );
2173 mEventData->mDecoratorUpdated = true;
2175 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
2177 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2179 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2180 if( mEventData->mCursorBlinkEnabled )
2182 mEventData->mDecorator->StartCursorBlink();
2184 // Grab handle is not shown until a tap is received whilst EDITING
2185 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2186 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2187 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2188 if( mEventData->mGrabHandlePopupEnabled )
2190 mEventData->mDecorator->SetPopupActive( false );
2192 mEventData->mDecoratorUpdated = true;
2195 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
2197 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2198 mEventData->mDecorator->StopCursorBlink();
2199 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2200 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2201 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2202 if( mEventData->mGrabHandlePopupEnabled )
2204 mEventData->mDecorator->SetPopupActive( false );
2206 mEventData->mDecoratorUpdated = true;
2208 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
2210 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2212 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2213 if( mEventData->mCursorBlinkEnabled )
2215 mEventData->mDecorator->StartCursorBlink();
2217 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2218 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2219 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2220 if( mEventData->mGrabHandlePopupEnabled )
2222 mEventData->mDecorator->SetPopupActive( false );
2224 mEventData->mDecoratorUpdated = true;
2226 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2228 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2230 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2231 if( mEventData->mCursorBlinkEnabled )
2233 mEventData->mDecorator->StartCursorBlink();
2236 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2237 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2238 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2240 if( mEventData->mGrabHandlePopupEnabled )
2243 mEventData->mDecorator->SetPopupActive( true );
2246 mEventData->mDecoratorUpdated = true;
2251 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2252 CursorInfo& cursorInfo )
2254 if( !IsShowingRealText() )
2256 // Do not want to use the place-holder text to set the cursor position.
2258 // Use the line's height of the font's family set to set the cursor's size.
2259 // If there is no font's family set, use the default font.
2260 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2262 cursorInfo.lineOffset = 0.f;
2263 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2264 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2266 switch( mLayoutEngine.GetHorizontalAlignment() )
2268 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2270 cursorInfo.primaryPosition.x = 0.f;
2273 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2275 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2278 case LayoutEngine::HORIZONTAL_ALIGN_END:
2280 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2285 // Nothing else to do.
2289 Text::GetCursorPosition( mVisualModel,
2295 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2297 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2299 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2300 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2302 if( 0.f > cursorInfo.primaryPosition.x )
2304 cursorInfo.primaryPosition.x = 0.f;
2307 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2308 if( cursorInfo.primaryPosition.x > edgeWidth )
2310 cursorInfo.primaryPosition.x = edgeWidth;
2315 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2317 if( NULL == mEventData )
2319 // Nothing to do if there is no text input.
2323 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2325 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2326 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2328 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2329 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2331 if( numberOfCharacters > 1u )
2333 const Script script = mLogicalModel->GetScript( index );
2334 if( HasLigatureMustBreak( script ) )
2336 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2337 numberOfCharacters = 1u;
2342 while( 0u == numberOfCharacters )
2345 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2349 if( index < mEventData->mPrimaryCursorPosition )
2351 cursorIndex -= numberOfCharacters;
2355 cursorIndex += numberOfCharacters;
2358 // Will update the cursor hook position.
2359 mEventData->mUpdateCursorHookPosition = true;
2364 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2366 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2367 if( NULL == mEventData )
2369 // Nothing to do if there is no text input.
2370 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2374 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2376 // Sets the cursor position.
2377 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2380 cursorInfo.primaryCursorHeight,
2381 cursorInfo.lineHeight );
2382 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2384 if( mEventData->mUpdateGrabHandlePosition )
2386 // Sets the grab handle position.
2387 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2389 cursorInfo.lineOffset + mScrollPosition.y,
2390 cursorInfo.lineHeight );
2393 if( cursorInfo.isSecondaryCursor )
2395 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2396 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2397 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2398 cursorInfo.secondaryCursorHeight,
2399 cursorInfo.lineHeight );
2400 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2403 // Set which cursors are active according the state.
2404 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2406 if( cursorInfo.isSecondaryCursor )
2408 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2412 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2417 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2420 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2423 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2424 const CursorInfo& cursorInfo )
2426 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2427 ( RIGHT_SELECTION_HANDLE != handleType ) )
2432 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2434 // Sets the handle's position.
2435 mEventData->mDecorator->SetPosition( handleType,
2437 cursorInfo.lineOffset + mScrollPosition.y,
2438 cursorInfo.lineHeight );
2440 // If selection handle at start of the text and other at end of the text then all text is selected.
2441 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2442 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2443 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2446 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2448 // Clamp between -space & 0.
2450 if( actualSize.width > mVisualModel->mControlSize.width )
2452 const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2453 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2454 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2456 mEventData->mDecoratorUpdated = true;
2460 mScrollPosition.x = 0.f;
2464 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2466 // Clamp between -space & 0.
2467 if( actualSize.height > mVisualModel->mControlSize.height )
2469 const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2470 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2471 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2473 mEventData->mDecoratorUpdated = true;
2477 mScrollPosition.y = 0.f;
2481 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2483 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2485 // position is in actor's coords.
2486 const float positionEndX = position.x + cursorWidth;
2487 const float positionEndY = position.y + lineHeight;
2489 // Transform the position to decorator coords.
2490 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2491 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2493 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2494 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2496 if( decoratorPositionBeginX < 0.f )
2498 mScrollPosition.x = -position.x;
2500 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2502 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2505 if( decoratorPositionBeginY < 0.f )
2507 mScrollPosition.y = -position.y;
2509 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2511 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2515 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2517 // Get the current cursor position in decorator coords.
2518 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2520 // Calculate the offset to match the cursor position before the character was deleted.
2521 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2522 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2524 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2525 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2527 // Makes the new cursor position visible if needed.
2528 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2531 void Controller::Impl::RequestRelayout()
2533 mControlInterface.RequestTextRelayout();
2538 } // namespace Toolkit