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 mIsLeftHandleSelected( false ),
95 mUpdateHighlightBox( false ),
96 mScrollAfterUpdatePosition( false ),
97 mScrollAfterDelete( false ),
98 mAllTextSelected( false ),
99 mUpdateInputStyle( false )
101 mImfManager = ImfManager::Get();
104 EventData::~EventData()
107 bool Controller::Impl::ProcessInputEvents()
109 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
110 if( NULL == mEventData )
112 // Nothing to do if there is no text input.
113 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
117 if( mEventData->mDecorator )
119 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
120 iter != mEventData->mEventQueue.end();
125 case Event::CURSOR_KEY_EVENT:
127 OnCursorKeyEvent( *iter );
130 case Event::TAP_EVENT:
135 case Event::LONG_PRESS_EVENT:
137 OnLongPressEvent( *iter );
140 case Event::PAN_EVENT:
145 case Event::GRAB_HANDLE_EVENT:
146 case Event::LEFT_SELECTION_HANDLE_EVENT:
147 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
149 OnHandleEvent( *iter );
154 OnSelectEvent( *iter );
157 case Event::SELECT_ALL:
166 if( mEventData->mUpdateCursorPosition ||
167 mEventData->mUpdateHighlightBox )
172 // The cursor must also be repositioned after inserts into the model
173 if( mEventData->mUpdateCursorPosition )
175 // Updates the cursor position and scrolls the text to make it visible.
176 CursorInfo cursorInfo;
177 // Calculate the cursor position from the new cursor index.
178 GetCursorPosition( mEventData->mPrimaryCursorPosition,
181 if( mEventData->mUpdateCursorHookPosition )
183 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
184 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
185 mEventData->mUpdateCursorHookPosition = false;
188 // Scroll first the text after delete ...
189 if( mEventData->mScrollAfterDelete )
191 ScrollTextToMatchCursor( cursorInfo );
194 // ... then, text can be scrolled to make the cursor visible.
195 if( mEventData->mScrollAfterUpdatePosition )
197 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
198 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
200 mEventData->mScrollAfterUpdatePosition = false;
201 mEventData->mScrollAfterDelete = false;
203 UpdateCursorPosition( cursorInfo );
205 mEventData->mDecoratorUpdated = true;
206 mEventData->mUpdateCursorPosition = false;
207 mEventData->mUpdateGrabHandlePosition = false;
211 CursorInfo leftHandleInfo;
212 CursorInfo rightHandleInfo;
214 if( mEventData->mUpdateHighlightBox )
216 GetCursorPosition( mEventData->mLeftSelectionPosition,
219 GetCursorPosition( mEventData->mRightSelectionPosition,
222 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
224 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
226 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
227 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
231 if( mEventData->mUpdateLeftSelectionPosition )
233 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
237 mEventData->mDecoratorUpdated = true;
238 mEventData->mUpdateLeftSelectionPosition = false;
241 if( mEventData->mUpdateRightSelectionPosition )
243 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
247 mEventData->mDecoratorUpdated = true;
248 mEventData->mUpdateRightSelectionPosition = false;
251 if( mEventData->mUpdateHighlightBox )
253 RepositionSelectionHandles();
255 mEventData->mUpdateLeftSelectionPosition = false;
256 mEventData->mUpdateRightSelectionPosition = false;
257 mEventData->mUpdateHighlightBox = false;
260 mEventData->mScrollAfterUpdatePosition = false;
263 if( mEventData->mUpdateInputStyle )
265 // Set the default style first.
266 RetrieveDefaultInputStyle( mEventData->mInputStyle );
268 // Get the character index from the cursor index.
269 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
271 // Retrieve the style from the style runs stored in the logical model.
272 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
274 mEventData->mUpdateInputStyle = false;
277 mEventData->mEventQueue.clear();
279 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
281 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
282 mEventData->mDecoratorUpdated = false;
284 return decoratorUpdated;
287 void Controller::Impl::NotifyImfManager()
289 if( mEventData && mEventData->mImfManager )
291 CharacterIndex cursorPosition = GetLogicalCursorPosition();
293 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
295 // Update the cursor position by removing the initial white spaces.
296 if( cursorPosition < numberOfWhiteSpaces )
302 cursorPosition -= numberOfWhiteSpaces;
305 mEventData->mImfManager.SetCursorPosition( cursorPosition );
306 mEventData->mImfManager.NotifyCursorPosition();
310 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
312 CharacterIndex cursorPosition = 0u;
316 if( ( EventData::SELECTING == mEventData->mState ) ||
317 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
319 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
323 cursorPosition = mEventData->mPrimaryCursorPosition;
327 return cursorPosition;
330 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
332 Length numberOfWhiteSpaces = 0u;
334 // Get the buffer to the text.
335 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
337 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
338 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
340 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
346 return numberOfWhiteSpaces;
349 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
351 // Get the total number of characters.
352 Length numberOfCharacters = mLogicalModel->mText.Count();
354 // Retrieve the text.
355 if( 0u != numberOfCharacters )
357 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
361 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
363 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
364 mTextUpdateInfo.mStartGlyphIndex = 0u;
365 mTextUpdateInfo.mStartLineIndex = 0u;
366 numberOfCharacters = 0u;
368 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
369 if( 0u == numberOfParagraphs )
371 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
372 numberOfCharacters = 0u;
374 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
376 // Nothing else to do if there are no paragraphs.
380 // Find the paragraphs to be updated.
381 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
382 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
384 // Text is being added at the end of the current text.
385 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
387 // Text is being added in a new paragraph after the last character of the text.
388 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
389 numberOfCharacters = 0u;
390 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
392 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
393 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
395 // Nothing else to do;
399 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
403 Length numberOfCharactersToUpdate = 0u;
404 if( mTextUpdateInfo.mFullRelayoutNeeded )
406 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
410 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
412 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
413 numberOfCharactersToUpdate,
414 paragraphsToBeUpdated );
417 if( 0u != paragraphsToBeUpdated.Count() )
419 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
420 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
421 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
423 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
424 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
426 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
427 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
428 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
429 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
431 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
432 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
434 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
438 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
442 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
443 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
446 void Controller::Impl::ClearFullModelData( OperationsMask operations )
448 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
450 mLogicalModel->mLineBreakInfo.Clear();
451 mLogicalModel->mParagraphInfo.Clear();
454 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
456 mLogicalModel->mLineBreakInfo.Clear();
459 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
461 mLogicalModel->mScriptRuns.Clear();
464 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
466 mLogicalModel->mFontRuns.Clear();
469 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
471 if( NO_OPERATION != ( BIDI_INFO & operations ) )
473 mLogicalModel->mBidirectionalParagraphInfo.Clear();
474 mLogicalModel->mCharacterDirections.Clear();
477 if( NO_OPERATION != ( REORDER & operations ) )
479 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
480 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
481 endIt = mLogicalModel->mBidirectionalLineInfo.End();
485 BidirectionalLineInfoRun& bidiLineInfo = *it;
487 free( bidiLineInfo.visualToLogicalMap );
488 bidiLineInfo.visualToLogicalMap = NULL;
490 mLogicalModel->mBidirectionalLineInfo.Clear();
494 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
496 mVisualModel->mGlyphs.Clear();
497 mVisualModel->mGlyphsToCharacters.Clear();
498 mVisualModel->mCharactersToGlyph.Clear();
499 mVisualModel->mCharactersPerGlyph.Clear();
500 mVisualModel->mGlyphsPerCharacter.Clear();
501 mVisualModel->mGlyphPositions.Clear();
504 if( NO_OPERATION != ( LAYOUT & operations ) )
506 mVisualModel->mLines.Clear();
509 if( NO_OPERATION != ( COLOR & operations ) )
511 mVisualModel->mColorIndices.Clear();
515 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
517 const CharacterIndex endIndexPlusOne = endIndex + 1u;
519 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
521 // Clear the line break info.
522 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
524 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
525 lineBreakInfoBuffer + endIndexPlusOne );
527 // Clear the paragraphs.
528 ClearCharacterRuns( startIndex,
530 mLogicalModel->mParagraphInfo );
533 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
535 // Clear the word break info.
536 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
538 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
539 wordBreakInfoBuffer + endIndexPlusOne );
542 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
544 // Clear the scripts.
545 ClearCharacterRuns( startIndex,
547 mLogicalModel->mScriptRuns );
550 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
553 ClearCharacterRuns( startIndex,
555 mLogicalModel->mFontRuns );
558 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
560 if( NO_OPERATION != ( BIDI_INFO & operations ) )
562 // Clear the bidirectional paragraph info.
563 ClearCharacterRuns( startIndex,
565 mLogicalModel->mBidirectionalParagraphInfo );
567 // Clear the character's directions.
568 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
570 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
571 characterDirectionsBuffer + endIndexPlusOne );
574 if( NO_OPERATION != ( REORDER & operations ) )
576 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
577 uint32_t endRemoveIndex = startRemoveIndex;
578 ClearCharacterRuns( startIndex,
580 mLogicalModel->mBidirectionalLineInfo,
584 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
586 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
587 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
588 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
592 BidirectionalLineInfoRun& bidiLineInfo = *it;
594 free( bidiLineInfo.visualToLogicalMap );
595 bidiLineInfo.visualToLogicalMap = NULL;
598 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
599 bidirectionalLineInfoBuffer + endRemoveIndex );
604 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
606 const CharacterIndex endIndexPlusOne = endIndex + 1u;
607 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
609 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
610 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
611 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
613 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
614 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
616 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
618 // Update the character to glyph indices.
619 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
620 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
624 CharacterIndex& index = *it;
625 index -= numberOfGlyphsRemoved;
628 // Clear the character to glyph conversion table.
629 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
630 charactersToGlyphBuffer + endIndexPlusOne );
632 // Clear the glyphs per character table.
633 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
634 glyphsPerCharacterBuffer + endIndexPlusOne );
636 // Clear the glyphs buffer.
637 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
638 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
639 glyphsBuffer + endGlyphIndexPlusOne );
641 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
643 // Update the glyph to character indices.
644 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
645 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
649 CharacterIndex& index = *it;
650 index -= numberOfCharactersRemoved;
653 // Clear the glyphs to characters buffer.
654 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
655 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
657 // Clear the characters per glyph buffer.
658 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
659 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
660 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
662 // Clear the positions buffer.
663 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
664 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
665 positionsBuffer + endGlyphIndexPlusOne );
668 if( NO_OPERATION != ( LAYOUT & operations ) )
671 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
672 uint32_t endRemoveIndex = startRemoveIndex;
673 ClearCharacterRuns( startIndex,
675 mVisualModel->mLines,
679 // Will update the glyph runs.
680 startRemoveIndex = mVisualModel->mLines.Count();
681 endRemoveIndex = startRemoveIndex;
682 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
683 endGlyphIndexPlusOne - 1u,
684 mVisualModel->mLines,
688 // Set the line index from where to insert the new laid-out lines.
689 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
691 LineRun* linesBuffer = mVisualModel->mLines.Begin();
692 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
693 linesBuffer + endRemoveIndex );
696 if( NO_OPERATION != ( COLOR & operations ) )
698 if( 0u != mVisualModel->mColorIndices.Count() )
700 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
701 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
702 colorIndexBuffer + endGlyphIndexPlusOne );
707 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
709 if( mTextUpdateInfo.mClearAll ||
710 ( ( 0u == startIndex ) &&
711 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
713 ClearFullModelData( operations );
717 // Clear the model data related with characters.
718 ClearCharacterModelData( startIndex, endIndex, operations );
720 // Clear the model data related with glyphs.
721 ClearGlyphModelData( startIndex, endIndex, operations );
724 // The estimated number of lines. Used to avoid reallocations when layouting.
725 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
727 mVisualModel->ClearCaches();
730 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
732 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
734 // Calculate the operations to be done.
735 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
737 if( NO_OPERATION == operations )
739 // Nothing to do if no operations are pending and required.
743 Vector<Character>& utf32Characters = mLogicalModel->mText;
745 const Length numberOfCharacters = utf32Characters.Count();
747 // Index to the first character of the first paragraph to be updated.
748 CharacterIndex startIndex = 0u;
749 // Number of characters of the paragraphs to be removed.
750 Length paragraphCharacters = 0u;
752 CalculateTextUpdateIndices( paragraphCharacters );
753 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
755 if( mTextUpdateInfo.mClearAll ||
756 ( 0u != paragraphCharacters ) )
758 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
761 mTextUpdateInfo.mClearAll = false;
763 // Whether the model is updated.
764 bool updated = false;
766 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
767 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
769 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
771 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
772 // calculate the bidirectional info for each 'paragraph'.
773 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
774 // is not shaped together).
775 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
777 SetLineBreakInfo( utf32Characters,
779 requestedNumberOfCharacters,
782 // Create the paragraph info.
783 mLogicalModel->CreateParagraphInfo( startIndex,
784 requestedNumberOfCharacters );
788 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
789 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
791 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
792 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
794 SetWordBreakInfo( utf32Characters,
796 requestedNumberOfCharacters,
801 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
802 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
804 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
805 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
807 if( getScripts || validateFonts )
809 // Validates the fonts assigned by the application or assigns default ones.
810 // It makes sure all the characters are going to be rendered by the correct font.
811 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
815 // Retrieves the scripts used in the text.
816 multilanguageSupport.SetScripts( utf32Characters,
818 requestedNumberOfCharacters,
824 // Validate the fonts set through the mark-up string.
825 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
827 // Get the default font's description.
828 TextAbstraction::FontDescription defaultFontDescription;
829 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
830 if( NULL != mFontDefaults )
832 defaultFontDescription = mFontDefaults->mFontDescription;
833 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
836 // Validates the fonts. If there is a character with no assigned font it sets a default one.
837 // After this call, fonts are validated.
838 multilanguageSupport.ValidateFonts( utf32Characters,
841 defaultFontDescription,
844 requestedNumberOfCharacters,
850 Vector<Character> mirroredUtf32Characters;
851 bool textMirrored = false;
852 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
853 if( NO_OPERATION != ( BIDI_INFO & operations ) )
855 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
856 bidirectionalInfo.Reserve( numberOfParagraphs );
858 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
859 SetBidirectionalInfo( utf32Characters,
863 requestedNumberOfCharacters,
866 if( 0u != bidirectionalInfo.Count() )
868 // Only set the character directions if there is right to left characters.
869 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
870 GetCharactersDirection( bidirectionalInfo,
873 requestedNumberOfCharacters,
876 // This paragraph has right to left text. Some characters may need to be mirrored.
877 // TODO: consider if the mirrored string can be stored as well.
879 textMirrored = GetMirroredText( utf32Characters,
883 requestedNumberOfCharacters,
884 mirroredUtf32Characters );
888 // There is no right to left characters. Clear the directions vector.
889 mLogicalModel->mCharacterDirections.Clear();
894 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
895 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
896 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
897 Vector<GlyphIndex> newParagraphGlyphs;
898 newParagraphGlyphs.Reserve( numberOfParagraphs );
900 const Length currentNumberOfGlyphs = glyphs.Count();
901 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
903 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
905 ShapeText( textToShape,
910 mTextUpdateInfo.mStartGlyphIndex,
911 requestedNumberOfCharacters,
913 glyphsToCharactersMap,
915 newParagraphGlyphs );
917 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
918 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
919 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
923 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
925 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
927 GlyphInfo* glyphsBuffer = glyphs.Begin();
928 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
930 // Update the width and advance of all new paragraph characters.
931 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
933 const GlyphIndex index = *it;
934 GlyphInfo& glyph = *( glyphsBuffer + index );
936 glyph.xBearing = 0.f;
943 if( NO_OPERATION != ( COLOR & operations ) )
945 // Set the color runs in glyphs.
946 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
947 mVisualModel->mCharactersToGlyph,
948 mVisualModel->mGlyphsPerCharacter,
950 mTextUpdateInfo.mStartGlyphIndex,
951 requestedNumberOfCharacters,
952 mVisualModel->mColors,
953 mVisualModel->mColorIndices );
958 if( ( NULL != mEventData ) &&
959 mEventData->mPreEditFlag &&
960 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
962 // Add the underline for the pre-edit text.
963 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
964 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
966 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
967 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
968 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
969 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
971 GlyphRun underlineRun;
972 underlineRun.glyphIndex = glyphStart;
973 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
975 // TODO: At the moment the underline runs are only for pre-edit.
976 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
979 // The estimated number of lines. Used to avoid reallocations when layouting.
980 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
982 // Set the previous number of characters for the next time the text is updated.
983 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
988 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
990 // Sets the default text's color.
991 inputStyle.textColor = mTextColor;
992 inputStyle.isDefaultColor = true;
994 inputStyle.familyName.clear();
995 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
996 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
997 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
998 inputStyle.size = 0.f;
1000 inputStyle.familyDefined = false;
1001 inputStyle.weightDefined = false;
1002 inputStyle.widthDefined = false;
1003 inputStyle.slantDefined = false;
1004 inputStyle.sizeDefined = false;
1006 // Sets the default font's family name, weight, width, slant and size.
1009 if( mFontDefaults->familyDefined )
1011 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1012 inputStyle.familyDefined = true;
1015 if( mFontDefaults->weightDefined )
1017 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1018 inputStyle.weightDefined = true;
1021 if( mFontDefaults->widthDefined )
1023 inputStyle.width = mFontDefaults->mFontDescription.width;
1024 inputStyle.widthDefined = true;
1027 if( mFontDefaults->slantDefined )
1029 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1030 inputStyle.slantDefined = true;
1033 if( mFontDefaults->sizeDefined )
1035 inputStyle.size = mFontDefaults->mDefaultPointSize;
1036 inputStyle.sizeDefined = true;
1041 float Controller::Impl::GetDefaultFontLineHeight()
1043 FontId defaultFontId = 0u;
1044 if( NULL == mFontDefaults )
1046 TextAbstraction::FontDescription fontDescription;
1047 defaultFontId = mFontClient.GetFontId( fontDescription );
1051 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1054 Text::FontMetrics fontMetrics;
1055 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1057 return( fontMetrics.ascender - fontMetrics.descender );
1060 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1062 if( NULL == mEventData )
1064 // Nothing to do if there is no text input.
1068 int keyCode = event.p1.mInt;
1070 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1072 if( mEventData->mPrimaryCursorPosition > 0u )
1074 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1077 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1079 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1081 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1084 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1086 // Get first the line index of the current cursor position index.
1087 CharacterIndex characterIndex = 0u;
1089 if( mEventData->mPrimaryCursorPosition > 0u )
1091 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1094 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1096 if( lineIndex > 0u )
1098 // Retrieve the cursor position info.
1099 CursorInfo cursorInfo;
1100 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1103 // Get the line above.
1104 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1106 // Get the next hit 'y' point.
1107 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1109 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1110 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1113 mEventData->mCursorHookPositionX,
1117 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1119 // Get first the line index of the current cursor position index.
1120 CharacterIndex characterIndex = 0u;
1122 if( mEventData->mPrimaryCursorPosition > 0u )
1124 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1127 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1129 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1131 // Retrieve the cursor position info.
1132 CursorInfo cursorInfo;
1133 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1136 // Get the line below.
1137 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1139 // Get the next hit 'y' point.
1140 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1142 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1143 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1146 mEventData->mCursorHookPositionX,
1151 mEventData->mUpdateCursorPosition = true;
1152 mEventData->mUpdateInputStyle = true;
1153 mEventData->mScrollAfterUpdatePosition = true;
1156 void Controller::Impl::OnTapEvent( const Event& event )
1158 if( NULL != mEventData )
1160 const unsigned int tapCount = event.p1.mUint;
1162 if( 1u == tapCount )
1164 if( IsShowingRealText() )
1166 // Convert from control's coords to text's coords.
1167 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1168 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1170 // Keep the tap 'x' position. Used to move the cursor.
1171 mEventData->mCursorHookPositionX = xPosition;
1173 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1179 // When the cursor position is changing, delay cursor blinking
1180 mEventData->mDecorator->DelayCursorBlink();
1184 mEventData->mPrimaryCursorPosition = 0u;
1187 mEventData->mUpdateCursorPosition = true;
1188 mEventData->mUpdateGrabHandlePosition = true;
1189 mEventData->mScrollAfterUpdatePosition = true;
1190 mEventData->mUpdateInputStyle = true;
1192 // Notify the cursor position to the imf manager.
1193 if( mEventData->mImfManager )
1195 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1196 mEventData->mImfManager.NotifyCursorPosition();
1202 void Controller::Impl::OnPanEvent( const Event& event )
1204 if( NULL == mEventData )
1206 // Nothing to do if there is no text input.
1210 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1211 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1213 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1215 // Nothing to do if scrolling is not enabled.
1219 const int state = event.p1.mInt;
1223 case Gesture::Started:
1225 // Will remove the cursor, handles or text's popup, ...
1226 ChangeState( EventData::TEXT_PANNING );
1229 case Gesture::Continuing:
1231 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1232 const Vector2 currentScroll = mScrollPosition;
1234 if( isHorizontalScrollEnabled )
1236 const float displacementX = event.p2.mFloat;
1237 mScrollPosition.x += displacementX;
1239 ClampHorizontalScroll( layoutSize );
1242 if( isVerticalScrollEnabled )
1244 const float displacementY = event.p3.mFloat;
1245 mScrollPosition.y += displacementY;
1247 ClampVerticalScroll( layoutSize );
1250 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1253 case Gesture::Finished:
1254 case Gesture::Cancelled: // FALLTHROUGH
1256 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1257 ChangeState( mEventData->mPreviousState );
1265 void Controller::Impl::OnLongPressEvent( const Event& event )
1267 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1269 if( EventData::EDITING == mEventData->mState )
1271 ChangeState ( EventData::EDITING_WITH_POPUP );
1272 mEventData->mDecoratorUpdated = true;
1276 void Controller::Impl::OnHandleEvent( const Event& event )
1278 if( NULL == mEventData )
1280 // Nothing to do if there is no text input.
1284 const unsigned int state = event.p1.mUint;
1285 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1286 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1288 if( HANDLE_PRESSED == state )
1290 // Convert from decorator's coords to text's coords.
1291 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1292 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1294 // Need to calculate the handle's new position.
1295 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1301 if( Event::GRAB_HANDLE_EVENT == event.type )
1303 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1305 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1307 // Updates the cursor position if the handle's new position is different than the current one.
1308 mEventData->mUpdateCursorPosition = true;
1309 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1310 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1311 mEventData->mPrimaryCursorPosition = handleNewPosition;
1314 // 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.
1315 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1317 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1319 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1321 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1322 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1324 // Updates the highlight box if the handle's new position is different than the current one.
1325 mEventData->mUpdateHighlightBox = true;
1326 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1327 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1328 mEventData->mLeftSelectionPosition = handleNewPosition;
1331 // 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.
1332 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1334 // Will define the order to scroll the text to match the handle position.
1335 mEventData->mIsLeftHandleSelected = true;
1337 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1339 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1341 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1342 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1344 // Updates the highlight box if the handle's new position is different than the current one.
1345 mEventData->mUpdateHighlightBox = true;
1346 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1347 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1348 mEventData->mRightSelectionPosition = handleNewPosition;
1351 // 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.
1352 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1354 // Will define the order to scroll the text to match the handle position.
1355 mEventData->mIsLeftHandleSelected = false;
1357 } // end ( HANDLE_PRESSED == state )
1358 else if( ( HANDLE_RELEASED == state ) ||
1359 handleStopScrolling )
1361 CharacterIndex handlePosition = 0u;
1362 if( handleStopScrolling || isSmoothHandlePanEnabled )
1364 // Convert from decorator's coords to text's coords.
1365 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1366 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1368 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1375 if( Event::GRAB_HANDLE_EVENT == event.type )
1377 mEventData->mUpdateCursorPosition = true;
1378 mEventData->mUpdateGrabHandlePosition = true;
1379 mEventData->mUpdateInputStyle = true;
1381 if( !IsClipboardEmpty() )
1383 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1386 if( handleStopScrolling || isSmoothHandlePanEnabled )
1388 mEventData->mScrollAfterUpdatePosition = true;
1389 mEventData->mPrimaryCursorPosition = handlePosition;
1392 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1394 ChangeState( EventData::SELECTING );
1396 mEventData->mUpdateHighlightBox = true;
1397 mEventData->mUpdateLeftSelectionPosition = true;
1398 mEventData->mUpdateRightSelectionPosition = true;
1400 if( handleStopScrolling || isSmoothHandlePanEnabled )
1402 mEventData->mScrollAfterUpdatePosition = true;
1404 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1405 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1407 mEventData->mLeftSelectionPosition = handlePosition;
1411 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1413 ChangeState( EventData::SELECTING );
1415 mEventData->mUpdateHighlightBox = true;
1416 mEventData->mUpdateRightSelectionPosition = true;
1417 mEventData->mUpdateLeftSelectionPosition = true;
1419 if( handleStopScrolling || isSmoothHandlePanEnabled )
1421 mEventData->mScrollAfterUpdatePosition = true;
1422 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1423 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1425 mEventData->mRightSelectionPosition = handlePosition;
1430 mEventData->mDecoratorUpdated = true;
1431 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1432 else if( HANDLE_SCROLLING == state )
1434 const float xSpeed = event.p2.mFloat;
1435 const float ySpeed = event.p3.mFloat;
1436 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1437 const Vector2 currentScrollPosition = mScrollPosition;
1439 mScrollPosition.x += xSpeed;
1440 mScrollPosition.y += ySpeed;
1442 ClampHorizontalScroll( layoutSize );
1443 ClampVerticalScroll( layoutSize );
1445 bool endOfScroll = false;
1446 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1448 // Notify the decorator there is no more text to scroll.
1449 // The decorator won't send more scroll events.
1450 mEventData->mDecorator->NotifyEndOfScroll();
1451 // Still need to set the position of the handle.
1455 // Set the position of the handle.
1456 const bool scrollRightDirection = xSpeed > 0.f;
1457 const bool scrollBottomDirection = ySpeed > 0.f;
1458 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1459 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1461 if( Event::GRAB_HANDLE_EVENT == event.type )
1463 ChangeState( EventData::GRAB_HANDLE_PANNING );
1465 // Get the grab handle position in decorator coords.
1466 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1468 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1470 // Position the grag handle close to either the left or right edge.
1471 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1474 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1476 position.x = mEventData->mCursorHookPositionX;
1478 // Position the grag handle close to either the top or bottom edge.
1479 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1482 // Get the new handle position.
1483 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1484 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1487 position.x - mScrollPosition.x,
1488 position.y - mScrollPosition.y );
1490 if( mEventData->mPrimaryCursorPosition != handlePosition )
1492 mEventData->mUpdateCursorPosition = true;
1493 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1494 mEventData->mScrollAfterUpdatePosition = true;
1495 mEventData->mPrimaryCursorPosition = handlePosition;
1497 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1499 // Updates the decorator if the soft handle panning is enabled.
1500 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1502 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1504 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1506 // Get the selection handle position in decorator coords.
1507 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1509 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1511 // Position the selection handle close to either the left or right edge.
1512 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1515 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1517 position.x = mEventData->mCursorHookPositionX;
1519 // Position the grag handle close to either the top or bottom edge.
1520 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1523 // Get the new handle position.
1524 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1525 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1528 position.x - mScrollPosition.x,
1529 position.y - mScrollPosition.y );
1531 if( leftSelectionHandleEvent )
1533 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1535 if( differentHandles || endOfScroll )
1537 mEventData->mUpdateHighlightBox = true;
1538 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1539 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1540 mEventData->mLeftSelectionPosition = handlePosition;
1545 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1546 if( differentHandles || endOfScroll )
1548 mEventData->mUpdateHighlightBox = true;
1549 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1550 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1551 mEventData->mRightSelectionPosition = handlePosition;
1555 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1557 RepositionSelectionHandles();
1559 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1562 mEventData->mDecoratorUpdated = true;
1563 } // end ( HANDLE_SCROLLING == state )
1566 void Controller::Impl::OnSelectEvent( const Event& event )
1568 if( NULL == mEventData )
1570 // Nothing to do if there is no text.
1574 if( mEventData->mSelectionEnabled )
1576 // Convert from control's coords to text's coords.
1577 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1578 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1580 // Calculates the logical position from the x,y coords.
1581 RepositionSelectionHandles( xPosition,
1586 void Controller::Impl::OnSelectAllEvent()
1588 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1590 if( NULL == mEventData )
1592 // Nothing to do if there is no text.
1596 if( mEventData->mSelectionEnabled )
1598 ChangeState( EventData::SELECTING );
1600 mEventData->mLeftSelectionPosition = 0u;
1601 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1603 mEventData->mScrollAfterUpdatePosition = true;
1604 mEventData->mUpdateLeftSelectionPosition = true;
1605 mEventData->mUpdateRightSelectionPosition = true;
1606 mEventData->mUpdateHighlightBox = true;
1610 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1612 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1614 // Nothing to select if handles are in the same place.
1615 selectedText.clear();
1619 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1621 //Get start and end position of selection
1622 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1623 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1625 Vector<Character>& utf32Characters = mLogicalModel->mText;
1626 const Length numberOfCharacters = utf32Characters.Count();
1628 // Validate the start and end selection points
1629 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1631 //Get text as a UTF8 string
1632 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1634 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1636 // Set as input style the style of the first deleted character.
1637 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1639 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1641 // Mark the paragraphs to be updated.
1642 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1643 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1645 // Delete text between handles
1646 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1647 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1648 utf32Characters.Erase( first, last );
1650 // Will show the cursor at the first character of the selection.
1651 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1655 // Will show the cursor at the last character of the selection.
1656 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1659 mEventData->mDecoratorUpdated = true;
1663 void Controller::Impl::ShowClipboard()
1667 mClipboard.ShowClipboard();
1671 void Controller::Impl::HideClipboard()
1673 if( mClipboard && mClipboardHideEnabled )
1675 mClipboard.HideClipboard();
1679 void Controller::Impl::SetClipboardHideEnable(bool enable)
1681 mClipboardHideEnabled = enable;
1684 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1686 //Send string to clipboard
1687 return ( mClipboard && mClipboard.SetItem( source ) );
1690 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1692 std::string selectedText;
1693 RetrieveSelection( selectedText, deleteAfterSending );
1694 CopyStringToClipboard( selectedText );
1695 ChangeState( EventData::EDITING );
1698 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1702 retrievedString = mClipboard.GetItem( itemIndex );
1706 void Controller::Impl::RepositionSelectionHandles()
1708 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1709 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1711 if( selectionStart == selectionEnd )
1713 // Nothing to select if handles are in the same place.
1717 mEventData->mDecorator->ClearHighlights();
1719 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1720 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1721 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1722 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1723 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1724 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1725 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1727 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1728 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1729 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1731 // Swap the indices if the start is greater than the end.
1732 const bool indicesSwapped = selectionStart > selectionEnd;
1734 // Tell the decorator to flip the selection handles if needed.
1735 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1737 if( indicesSwapped )
1739 std::swap( selectionStart, selectionEnd );
1742 // Get the indices to the first and last selected glyphs.
1743 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1744 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1745 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1746 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1748 // Get the lines where the glyphs are laid-out.
1749 const LineRun* lineRun = mVisualModel->mLines.Begin();
1751 LineIndex lineIndex = 0u;
1752 Length numberOfLines = 0u;
1753 mVisualModel->GetNumberOfLines( glyphStart,
1754 1u + glyphEnd - glyphStart,
1757 const LineIndex firstLineIndex = lineIndex;
1759 // Create the structure to store some selection box info.
1760 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1761 selectionBoxLinesInfo.Resize( numberOfLines );
1763 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1764 selectionBoxInfo->minX = MAX_FLOAT;
1765 selectionBoxInfo->maxX = MIN_FLOAT;
1767 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1768 float minHighlightX = std::numeric_limits<float>::max();
1769 float maxHighlightX = std::numeric_limits<float>::min();
1771 Vector2 highLightPosition; // The highlight position in decorator's coords.
1773 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1775 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1776 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1779 // Transform to decorator's (control) coords.
1780 selectionBoxInfo->lineOffset += mScrollPosition.y;
1782 lineRun += firstLineIndex;
1784 // The line height is the addition of the line ascender and the line descender.
1785 // However, the line descender has a negative value, hence the subtraction.
1786 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1788 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1790 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1791 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1792 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1794 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1795 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1796 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1798 // The number of quads of the selection box.
1799 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1800 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1802 // Count the actual number of quads.
1803 unsigned int actualNumberOfQuads = 0u;
1806 // Traverse the glyphs.
1807 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1809 const GlyphInfo& glyph = *( glyphsBuffer + index );
1810 const Vector2& position = *( positionsBuffer + index );
1812 if( splitStartGlyph )
1814 // 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.
1816 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1817 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1818 // Get the direction of the character.
1819 CharacterDirection isCurrentRightToLeft = false;
1820 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1822 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1825 // The end point could be in the middle of the ligature.
1826 // Calculate the number of characters selected.
1827 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1829 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1830 quad.y = selectionBoxInfo->lineOffset;
1831 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1832 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1834 // Store the min and max 'x' for each line.
1835 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1836 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1838 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1839 ++actualNumberOfQuads;
1841 splitStartGlyph = false;
1845 if( splitEndGlyph && ( index == glyphEnd ) )
1847 // 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.
1849 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1850 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1851 // Get the direction of the character.
1852 CharacterDirection isCurrentRightToLeft = false;
1853 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1855 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1858 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1860 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1861 quad.y = selectionBoxInfo->lineOffset;
1862 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1863 quad.w = quad.y + selectionBoxInfo->lineHeight;
1865 // Store the min and max 'x' for each line.
1866 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1867 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1869 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1871 ++actualNumberOfQuads;
1873 splitEndGlyph = false;
1877 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1878 quad.y = selectionBoxInfo->lineOffset;
1879 quad.z = quad.x + glyph.advance;
1880 quad.w = quad.y + selectionBoxInfo->lineHeight;
1882 // Store the min and max 'x' for each line.
1883 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1884 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1886 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1888 ++actualNumberOfQuads;
1890 // Whether to retrieve the next line.
1891 if( index == lastGlyphOfLine )
1893 // Retrieve the next line.
1896 // Get the last glyph of the new line.
1897 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1900 if( lineIndex < firstLineIndex + numberOfLines )
1902 // Keep the offset and height of the current selection box.
1903 const float currentLineOffset = selectionBoxInfo->lineOffset;
1904 const float currentLineHeight = selectionBoxInfo->lineHeight;
1906 // Get the selection box info for the next line.
1909 selectionBoxInfo->minX = MAX_FLOAT;
1910 selectionBoxInfo->maxX = MIN_FLOAT;
1912 // Update the line's vertical offset.
1913 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1915 // The line height is the addition of the line ascender and the line descender.
1916 // However, the line descender has a negative value, hence the subtraction.
1917 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1922 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1923 // The final width is calculated after 'boxifying' the selection.
1924 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1925 endIt = selectionBoxLinesInfo.End();
1929 const SelectionBoxInfo& info = *it;
1931 // Update the size of the highlighted text.
1932 highLightSize.height += info.lineHeight;
1933 minHighlightX = std::min( minHighlightX, info.minX );
1934 maxHighlightX = std::max( maxHighlightX, info.maxX );
1937 // Add extra geometry to 'boxify' the selection.
1939 if( 1u < numberOfLines )
1941 // Boxify the first line.
1942 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1943 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1945 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1946 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1951 quad.y = firstSelectionBoxLineInfo.lineOffset;
1952 quad.z = firstSelectionBoxLineInfo.minX;
1953 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1955 // Boxify at the beginning of the line.
1956 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1958 ++actualNumberOfQuads;
1960 // Update the size of the highlighted text.
1961 minHighlightX = 0.f;
1966 quad.x = firstSelectionBoxLineInfo.maxX;
1967 quad.y = firstSelectionBoxLineInfo.lineOffset;
1968 quad.z = mVisualModel->mControlSize.width;
1969 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1971 // Boxify at the end of the line.
1972 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1974 ++actualNumberOfQuads;
1976 // Update the size of the highlighted text.
1977 maxHighlightX = mVisualModel->mControlSize.width;
1980 // Boxify the central lines.
1981 if( 2u < numberOfLines )
1983 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1984 endIt = selectionBoxLinesInfo.End() - 1u;
1988 const SelectionBoxInfo& info = *it;
1991 quad.y = info.lineOffset;
1993 quad.w = info.lineOffset + info.lineHeight;
1995 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1997 ++actualNumberOfQuads;
2000 quad.y = info.lineOffset;
2001 quad.z = mVisualModel->mControlSize.width;
2002 quad.w = info.lineOffset + info.lineHeight;
2004 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2006 ++actualNumberOfQuads;
2009 // Update the size of the highlighted text.
2010 minHighlightX = 0.f;
2011 maxHighlightX = mVisualModel->mControlSize.width;
2014 // Boxify the last line.
2015 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2016 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2018 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2019 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2024 quad.y = lastSelectionBoxLineInfo.lineOffset;
2025 quad.z = lastSelectionBoxLineInfo.minX;
2026 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2028 // Boxify at the beginning of the line.
2029 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2031 ++actualNumberOfQuads;
2033 // Update the size of the highlighted text.
2034 minHighlightX = 0.f;
2039 quad.x = lastSelectionBoxLineInfo.maxX;
2040 quad.y = lastSelectionBoxLineInfo.lineOffset;
2041 quad.z = mVisualModel->mControlSize.width;
2042 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2044 // Boxify at the end of the line.
2045 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2047 ++actualNumberOfQuads;
2049 // Update the size of the highlighted text.
2050 maxHighlightX = mVisualModel->mControlSize.width;
2054 // Set the actual number of quads.
2055 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2057 // Sets the highlight's size and position. In decorator's coords.
2058 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2059 highLightSize.width = maxHighlightX - minHighlightX;
2061 highLightPosition.x = minHighlightX;
2062 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2063 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2065 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2067 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2069 CursorInfo primaryCursorInfo;
2070 GetCursorPosition( mEventData->mLeftSelectionPosition,
2071 primaryCursorInfo );
2073 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2075 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2077 primaryCursorInfo.lineOffset + mScrollPosition.y,
2078 primaryCursorInfo.lineHeight );
2080 CursorInfo secondaryCursorInfo;
2081 GetCursorPosition( mEventData->mRightSelectionPosition,
2082 secondaryCursorInfo );
2084 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2086 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2087 secondaryPosition.x,
2088 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2089 secondaryCursorInfo.lineHeight );
2092 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2093 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2095 // Set the flag to update the decorator.
2096 mEventData->mDecoratorUpdated = true;
2099 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2101 if( NULL == mEventData )
2103 // Nothing to do if there is no text input.
2107 if( IsShowingPlaceholderText() )
2109 // Nothing to do if there is the place-holder text.
2113 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2114 const Length numberOfLines = mVisualModel->mLines.Count();
2115 if( ( 0 == numberOfGlyphs ) ||
2116 ( 0 == numberOfLines ) )
2118 // Nothing to do if there is no text.
2122 // Find which word was selected
2123 CharacterIndex selectionStart( 0 );
2124 CharacterIndex selectionEnd( 0 );
2125 const bool indicesFound = FindSelectionIndices( mVisualModel,
2132 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2136 ChangeState( EventData::SELECTING );
2138 mEventData->mLeftSelectionPosition = selectionStart;
2139 mEventData->mRightSelectionPosition = selectionEnd;
2141 mEventData->mUpdateLeftSelectionPosition = true;
2142 mEventData->mUpdateRightSelectionPosition = true;
2143 mEventData->mUpdateHighlightBox = true;
2145 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2149 // Nothing to select. i.e. a white space, out of bounds
2150 ChangeState( EventData::EDITING );
2152 mEventData->mPrimaryCursorPosition = selectionEnd;
2154 mEventData->mUpdateCursorPosition = true;
2155 mEventData->mUpdateGrabHandlePosition = true;
2156 mEventData->mScrollAfterUpdatePosition = true;
2157 mEventData->mUpdateInputStyle = true;
2161 void Controller::Impl::SetPopupButtons()
2164 * Sets the Popup buttons to be shown depending on State.
2166 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2168 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2171 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2173 if( EventData::SELECTING == mEventData->mState )
2175 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2177 if( !IsClipboardEmpty() )
2179 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2180 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2183 if( !mEventData->mAllTextSelected )
2185 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2188 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2190 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2192 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2195 if( !IsClipboardEmpty() )
2197 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2198 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2201 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2203 if ( !IsClipboardEmpty() )
2205 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2206 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2210 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2213 void Controller::Impl::ChangeState( EventData::State newState )
2215 if( NULL == mEventData )
2217 // Nothing to do if there is no text input.
2221 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2223 if( mEventData->mState != newState )
2225 mEventData->mPreviousState = mEventData->mState;
2226 mEventData->mState = newState;
2228 switch( mEventData->mState )
2230 case EventData::INACTIVE:
2232 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2233 mEventData->mDecorator->StopCursorBlink();
2234 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2235 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2236 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2237 mEventData->mDecorator->SetHighlightActive( false );
2238 mEventData->mDecorator->SetPopupActive( false );
2239 mEventData->mDecoratorUpdated = true;
2243 case EventData::INTERRUPTED:
2245 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2246 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2247 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2248 mEventData->mDecorator->SetHighlightActive( false );
2249 mEventData->mDecorator->SetPopupActive( false );
2250 mEventData->mDecoratorUpdated = true;
2254 case EventData::SELECTING:
2256 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2257 mEventData->mDecorator->StopCursorBlink();
2258 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2259 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2260 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2261 mEventData->mDecorator->SetHighlightActive( true );
2262 if( mEventData->mGrabHandlePopupEnabled )
2265 mEventData->mDecorator->SetPopupActive( true );
2267 mEventData->mDecoratorUpdated = true;
2270 case EventData::EDITING:
2272 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2273 if( mEventData->mCursorBlinkEnabled )
2275 mEventData->mDecorator->StartCursorBlink();
2277 // Grab handle is not shown until a tap is received whilst EDITING
2278 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2279 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2280 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2281 mEventData->mDecorator->SetHighlightActive( false );
2282 if( mEventData->mGrabHandlePopupEnabled )
2284 mEventData->mDecorator->SetPopupActive( false );
2286 mEventData->mDecoratorUpdated = true;
2290 case EventData::EDITING_WITH_POPUP:
2292 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2294 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2295 if( mEventData->mCursorBlinkEnabled )
2297 mEventData->mDecorator->StartCursorBlink();
2299 if( mEventData->mSelectionEnabled )
2301 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2302 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2303 mEventData->mDecorator->SetHighlightActive( false );
2307 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2309 if( mEventData->mGrabHandlePopupEnabled )
2312 mEventData->mDecorator->SetPopupActive( true );
2315 mEventData->mDecoratorUpdated = true;
2318 case EventData::EDITING_WITH_GRAB_HANDLE:
2320 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2322 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2323 if( mEventData->mCursorBlinkEnabled )
2325 mEventData->mDecorator->StartCursorBlink();
2327 // Grab handle is not shown until a tap is received whilst EDITING
2328 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2329 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2330 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2331 mEventData->mDecorator->SetHighlightActive( false );
2332 if( mEventData->mGrabHandlePopupEnabled )
2334 mEventData->mDecorator->SetPopupActive( false );
2336 mEventData->mDecoratorUpdated = true;
2340 case EventData::SELECTION_HANDLE_PANNING:
2342 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2343 mEventData->mDecorator->StopCursorBlink();
2344 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2345 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2346 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2347 mEventData->mDecorator->SetHighlightActive( true );
2348 if( mEventData->mGrabHandlePopupEnabled )
2350 mEventData->mDecorator->SetPopupActive( false );
2352 mEventData->mDecoratorUpdated = true;
2355 case EventData::GRAB_HANDLE_PANNING:
2357 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2359 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2360 if( mEventData->mCursorBlinkEnabled )
2362 mEventData->mDecorator->StartCursorBlink();
2364 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2365 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2366 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2367 mEventData->mDecorator->SetHighlightActive( false );
2368 if( mEventData->mGrabHandlePopupEnabled )
2370 mEventData->mDecorator->SetPopupActive( false );
2372 mEventData->mDecoratorUpdated = true;
2375 case EventData::EDITING_WITH_PASTE_POPUP:
2377 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2379 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2380 if( mEventData->mCursorBlinkEnabled )
2382 mEventData->mDecorator->StartCursorBlink();
2385 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2386 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2387 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2388 mEventData->mDecorator->SetHighlightActive( false );
2390 if( mEventData->mGrabHandlePopupEnabled )
2393 mEventData->mDecorator->SetPopupActive( true );
2396 mEventData->mDecoratorUpdated = true;
2399 case EventData::TEXT_PANNING:
2401 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2402 mEventData->mDecorator->StopCursorBlink();
2403 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2404 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2405 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2407 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2408 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2409 mEventData->mDecorator->SetHighlightActive( true );
2412 if( mEventData->mGrabHandlePopupEnabled )
2414 mEventData->mDecorator->SetPopupActive( false );
2417 mEventData->mDecoratorUpdated = true;
2424 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2425 CursorInfo& cursorInfo )
2427 if( !IsShowingRealText() )
2429 // Do not want to use the place-holder text to set the cursor position.
2431 // Use the line's height of the font's family set to set the cursor's size.
2432 // If there is no font's family set, use the default font.
2433 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2435 cursorInfo.lineOffset = 0.f;
2436 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2437 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2439 switch( mLayoutEngine.GetHorizontalAlignment() )
2441 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2443 cursorInfo.primaryPosition.x = 0.f;
2446 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2448 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2451 case LayoutEngine::HORIZONTAL_ALIGN_END:
2453 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2458 // Nothing else to do.
2462 Text::GetCursorPosition( mVisualModel,
2468 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2470 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2472 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2473 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2475 if( 0.f > cursorInfo.primaryPosition.x )
2477 cursorInfo.primaryPosition.x = 0.f;
2480 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2481 if( cursorInfo.primaryPosition.x > edgeWidth )
2483 cursorInfo.primaryPosition.x = edgeWidth;
2488 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2490 if( NULL == mEventData )
2492 // Nothing to do if there is no text input.
2496 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2498 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2499 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2501 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2502 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2504 if( numberOfCharacters > 1u )
2506 const Script script = mLogicalModel->GetScript( index );
2507 if( HasLigatureMustBreak( script ) )
2509 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2510 numberOfCharacters = 1u;
2515 while( 0u == numberOfCharacters )
2518 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2522 if( index < mEventData->mPrimaryCursorPosition )
2524 cursorIndex -= numberOfCharacters;
2528 cursorIndex += numberOfCharacters;
2531 // Will update the cursor hook position.
2532 mEventData->mUpdateCursorHookPosition = true;
2537 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2539 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2540 if( NULL == mEventData )
2542 // Nothing to do if there is no text input.
2543 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2547 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2549 // Sets the cursor position.
2550 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2553 cursorInfo.primaryCursorHeight,
2554 cursorInfo.lineHeight );
2555 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2557 if( mEventData->mUpdateGrabHandlePosition )
2559 // Sets the grab handle position.
2560 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2562 cursorInfo.lineOffset + mScrollPosition.y,
2563 cursorInfo.lineHeight );
2566 if( cursorInfo.isSecondaryCursor )
2568 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2569 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2570 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2571 cursorInfo.secondaryCursorHeight,
2572 cursorInfo.lineHeight );
2573 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2576 // Set which cursors are active according the state.
2577 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2579 if( cursorInfo.isSecondaryCursor )
2581 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2585 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2590 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2593 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2596 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2597 const CursorInfo& cursorInfo )
2599 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2600 ( RIGHT_SELECTION_HANDLE != handleType ) )
2605 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2607 // Sets the handle's position.
2608 mEventData->mDecorator->SetPosition( handleType,
2610 cursorInfo.lineOffset + mScrollPosition.y,
2611 cursorInfo.lineHeight );
2613 // If selection handle at start of the text and other at end of the text then all text is selected.
2614 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2615 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2616 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2619 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2621 // Clamp between -space & 0.
2623 if( layoutSize.width > mVisualModel->mControlSize.width )
2625 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2626 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2627 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2629 mEventData->mDecoratorUpdated = true;
2633 mScrollPosition.x = 0.f;
2637 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2639 // Clamp between -space & 0.
2640 if( layoutSize.height > mVisualModel->mControlSize.height )
2642 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2643 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2644 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2646 mEventData->mDecoratorUpdated = true;
2650 mScrollPosition.y = 0.f;
2654 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2656 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2658 // position is in actor's coords.
2659 const float positionEndX = position.x + cursorWidth;
2660 const float positionEndY = position.y + lineHeight;
2662 // Transform the position to decorator coords.
2663 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2664 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2666 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2667 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2669 if( decoratorPositionBeginX < 0.f )
2671 mScrollPosition.x = -position.x;
2673 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2675 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2678 if( decoratorPositionBeginY < 0.f )
2680 mScrollPosition.y = -position.y;
2682 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2684 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2688 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2690 // Get the current cursor position in decorator coords.
2691 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2693 // Calculate the offset to match the cursor position before the character was deleted.
2694 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2695 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2697 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2698 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2700 // Makes the new cursor position visible if needed.
2701 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2704 void Controller::Impl::RequestRelayout()
2706 mControlInterface.RequestTextRelayout();
2711 } // namespace Toolkit