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>
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/color-segmentation.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/multi-language-support.h>
31 #include <dali-toolkit/internal/text/segmentation.h>
32 #include <dali-toolkit/internal/text/shaper.h>
33 #include <dali-toolkit/internal/text/text-run-container.h>
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
53 EventData::EventData( DecoratorPtr decorator )
54 : mDecorator( decorator ),
56 mPlaceholderTextActive(),
57 mPlaceholderTextInactive(),
58 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
61 mPrimaryCursorPosition( 0u ),
62 mLeftSelectionPosition( 0u ),
63 mRightSelectionPosition( 0u ),
64 mPreEditStartPosition( 0u ),
66 mIsShowingPlaceholderText( false ),
67 mPreEditFlag( false ),
68 mDecoratorUpdated( false ),
69 mCursorBlinkEnabled( true ),
70 mGrabHandleEnabled( true ),
71 mGrabHandlePopupEnabled( true ),
72 mSelectionEnabled( true ),
73 mHorizontalScrollingEnabled( true ),
74 mVerticalScrollingEnabled( false ),
75 mUpdateCursorPosition( false ),
76 mUpdateLeftSelectionPosition( false ),
77 mUpdateRightSelectionPosition( false ),
78 mScrollAfterUpdatePosition( false ),
79 mScrollAfterDelete( false ),
80 mAllTextSelected( false ),
81 mUpdateInputStyle( false )
83 mImfManager = ImfManager::Get();
86 EventData::~EventData()
89 bool Controller::Impl::ProcessInputEvents()
91 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
92 if( NULL == mEventData )
94 // Nothing to do if there is no text input.
95 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
99 if( mEventData->mDecorator )
101 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
102 iter != mEventData->mEventQueue.end();
107 case Event::CURSOR_KEY_EVENT:
109 OnCursorKeyEvent( *iter );
112 case Event::TAP_EVENT:
117 case Event::LONG_PRESS_EVENT:
119 OnLongPressEvent( *iter );
122 case Event::PAN_EVENT:
127 case Event::GRAB_HANDLE_EVENT:
128 case Event::LEFT_SELECTION_HANDLE_EVENT:
129 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
131 OnHandleEvent( *iter );
136 OnSelectEvent( *iter );
139 case Event::SELECT_ALL:
148 // The cursor must also be repositioned after inserts into the model
149 if( mEventData->mUpdateCursorPosition )
151 // Updates the cursor position and scrolls the text to make it visible.
152 CursorInfo cursorInfo;
153 GetCursorPosition( mEventData->mPrimaryCursorPosition,
156 // Scroll first the text after delete ...
157 if( mEventData->mScrollAfterDelete )
159 ScrollTextToMatchCursor( cursorInfo );
162 // ... then, text can be scrolled to make the cursor visible.
163 if( mEventData->mScrollAfterUpdatePosition )
165 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
167 mEventData->mScrollAfterUpdatePosition = false;
168 mEventData->mScrollAfterDelete = false;
170 UpdateCursorPosition( cursorInfo );
172 mEventData->mDecoratorUpdated = true;
173 mEventData->mUpdateCursorPosition = false;
177 bool leftScroll = false;
178 bool rightScroll = false;
180 CursorInfo leftHandleInfo;
181 CursorInfo rightHandleInfo;
183 if( mEventData->mUpdateLeftSelectionPosition )
185 GetCursorPosition( mEventData->mLeftSelectionPosition,
188 if( mEventData->mScrollAfterUpdatePosition )
190 ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
195 if( mEventData->mUpdateRightSelectionPosition )
197 GetCursorPosition( mEventData->mRightSelectionPosition,
200 if( mEventData->mScrollAfterUpdatePosition )
202 ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
207 if( mEventData->mUpdateLeftSelectionPosition )
209 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
213 mEventData->mDecoratorUpdated = true;
216 if( mEventData->mUpdateRightSelectionPosition )
218 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
222 mEventData->mDecoratorUpdated = true;
225 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
227 RepositionSelectionHandles();
229 mEventData->mUpdateLeftSelectionPosition = false;
230 mEventData->mUpdateRightSelectionPosition = false;
233 if( leftScroll || rightScroll )
235 mEventData->mScrollAfterUpdatePosition = false;
239 if( mEventData->mUpdateInputStyle )
241 // Set the default style first.
242 RetrieveDefaultInputStyle( mEventData->mInputStyle );
244 // Get the character index from the cursor index.
245 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
247 // Retrieve the style from the style runs stored in the logical model.
248 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
250 mEventData->mUpdateInputStyle = false;
253 mEventData->mEventQueue.clear();
255 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
257 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
258 mEventData->mDecoratorUpdated = false;
260 return decoratorUpdated;
263 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
265 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
266 mTextUpdateInfo.mStartGlyphIndex = 0u;
267 mTextUpdateInfo.mStartLineIndex = 0u;
268 numberOfCharacters = 0u;
270 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
271 if( 0u == numberOfParagraphs )
273 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
274 numberOfCharacters = 0u;
276 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
278 // Nothing else to do if there are no paragraphs.
282 // Find the paragraphs to be updated.
283 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
284 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
286 // Text is being added at the end of the current text.
287 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
289 // Text is being added in a new paragraph after the last character of the text.
290 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
291 numberOfCharacters = 0u;
292 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
294 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
295 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
297 // Nothing else to do;
301 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
305 Length numberOfCharactersToUpdate = 0u;
306 if( mTextUpdateInfo.mFullRelayoutNeeded )
308 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
312 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
314 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
315 numberOfCharactersToUpdate,
316 paragraphsToBeUpdated );
319 if( 0u != paragraphsToBeUpdated.Count() )
321 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
322 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
323 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
325 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
326 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
328 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
329 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
330 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
331 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
333 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
334 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
336 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
340 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
344 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
345 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
348 void Controller::Impl::ClearFullModelData( OperationsMask operations )
350 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
352 mLogicalModel->mLineBreakInfo.Clear();
353 mLogicalModel->mParagraphInfo.Clear();
356 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
358 mLogicalModel->mLineBreakInfo.Clear();
361 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
363 mLogicalModel->mScriptRuns.Clear();
366 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
368 mLogicalModel->mFontRuns.Clear();
371 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
373 if( NO_OPERATION != ( BIDI_INFO & operations ) )
375 mLogicalModel->mBidirectionalParagraphInfo.Clear();
376 mLogicalModel->mCharacterDirections.Clear();
379 if( NO_OPERATION != ( REORDER & operations ) )
381 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
382 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
383 endIt = mLogicalModel->mBidirectionalLineInfo.End();
387 BidirectionalLineInfoRun& bidiLineInfo = *it;
389 free( bidiLineInfo.visualToLogicalMap );
390 bidiLineInfo.visualToLogicalMap = NULL;
392 mLogicalModel->mBidirectionalLineInfo.Clear();
396 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
398 mVisualModel->mGlyphs.Clear();
399 mVisualModel->mGlyphsToCharacters.Clear();
400 mVisualModel->mCharactersToGlyph.Clear();
401 mVisualModel->mCharactersPerGlyph.Clear();
402 mVisualModel->mGlyphsPerCharacter.Clear();
403 mVisualModel->mGlyphPositions.Clear();
406 if( NO_OPERATION != ( LAYOUT & operations ) )
408 mVisualModel->mLines.Clear();
411 if( NO_OPERATION != ( COLOR & operations ) )
413 mVisualModel->mColorIndices.Clear();
417 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
419 const CharacterIndex endIndexPlusOne = endIndex + 1u;
421 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
423 // Clear the line break info.
424 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
426 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
427 lineBreakInfoBuffer + endIndexPlusOne );
429 // Clear the paragraphs.
430 ClearCharacterRuns( startIndex,
432 mLogicalModel->mParagraphInfo );
435 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
437 // Clear the word break info.
438 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
440 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
441 wordBreakInfoBuffer + endIndexPlusOne );
444 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
446 // Clear the scripts.
447 ClearCharacterRuns( startIndex,
449 mLogicalModel->mScriptRuns );
452 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
455 ClearCharacterRuns( startIndex,
457 mLogicalModel->mFontRuns );
460 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
462 if( NO_OPERATION != ( BIDI_INFO & operations ) )
464 // Clear the bidirectional paragraph info.
465 ClearCharacterRuns( startIndex,
467 mLogicalModel->mBidirectionalParagraphInfo );
469 // Clear the character's directions.
470 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
472 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
473 characterDirectionsBuffer + endIndexPlusOne );
476 if( NO_OPERATION != ( REORDER & operations ) )
478 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
479 uint32_t endRemoveIndex = startRemoveIndex;
480 ClearCharacterRuns( startIndex,
482 mLogicalModel->mBidirectionalLineInfo,
486 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
488 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
489 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
490 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
494 BidirectionalLineInfoRun& bidiLineInfo = *it;
496 free( bidiLineInfo.visualToLogicalMap );
497 bidiLineInfo.visualToLogicalMap = NULL;
500 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
501 bidirectionalLineInfoBuffer + endRemoveIndex );
506 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
508 const CharacterIndex endIndexPlusOne = endIndex + 1u;
509 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
511 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
512 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
513 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
515 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
516 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
518 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
520 // Update the character to glyph indices.
521 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
522 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
526 CharacterIndex& index = *it;
527 index -= numberOfGlyphsRemoved;
530 // Clear the character to glyph conversion table.
531 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
532 charactersToGlyphBuffer + endIndexPlusOne );
534 // Clear the glyphs per character table.
535 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
536 glyphsPerCharacterBuffer + endIndexPlusOne );
538 // Clear the glyphs buffer.
539 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
540 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
541 glyphsBuffer + endGlyphIndexPlusOne );
543 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
545 // Update the glyph to character indices.
546 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
547 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
551 CharacterIndex& index = *it;
552 index -= numberOfCharactersRemoved;
555 // Clear the glyphs to characters buffer.
556 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
557 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
559 // Clear the characters per glyph buffer.
560 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
561 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
562 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
564 // Clear the positions buffer.
565 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
566 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
567 positionsBuffer + endGlyphIndexPlusOne );
570 if( NO_OPERATION != ( LAYOUT & operations ) )
573 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
574 uint32_t endRemoveIndex = startRemoveIndex;
575 ClearCharacterRuns( startIndex,
577 mVisualModel->mLines,
581 // Will update the glyph runs.
582 startRemoveIndex = mVisualModel->mLines.Count();
583 endRemoveIndex = startRemoveIndex;
584 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
585 endGlyphIndexPlusOne - 1u,
586 mVisualModel->mLines,
590 // Set the line index from where to insert the new laid-out lines.
591 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
593 LineRun* linesBuffer = mVisualModel->mLines.Begin();
594 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
595 linesBuffer + endRemoveIndex );
598 if( NO_OPERATION != ( COLOR & operations ) )
600 if( 0u != mVisualModel->mColorIndices.Count() )
602 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
603 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
604 colorIndexBuffer + endGlyphIndexPlusOne );
609 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
611 if( mTextUpdateInfo.mClearAll ||
612 ( ( 0u == startIndex ) &&
613 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
615 ClearFullModelData( operations );
619 // Clear the model data related with characters.
620 ClearCharacterModelData( startIndex, endIndex, operations );
622 // Clear the model data related with glyphs.
623 ClearGlyphModelData( startIndex, endIndex, operations );
626 // The estimated number of lines. Used to avoid reallocations when layouting.
627 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
629 mVisualModel->ClearCaches();
632 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
634 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
636 // Calculate the operations to be done.
637 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
639 if( NO_OPERATION == operations )
641 // Nothing to do if no operations are pending and required.
645 Vector<Character>& utf32Characters = mLogicalModel->mText;
647 const Length numberOfCharacters = utf32Characters.Count();
649 // Index to the first character of the first paragraph to be updated.
650 CharacterIndex startIndex = 0u;
651 // Number of characters of the paragraphs to be removed.
652 Length paragraphCharacters = 0u;
654 CalculateTextUpdateIndices( paragraphCharacters );
655 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
657 if( mTextUpdateInfo.mClearAll ||
658 ( 0u != paragraphCharacters ) )
660 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
663 mTextUpdateInfo.mClearAll = false;
665 // Whether the model is updated.
666 bool updated = false;
668 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
669 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
671 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
673 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
674 // calculate the bidirectional info for each 'paragraph'.
675 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
676 // is not shaped together).
677 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
679 SetLineBreakInfo( utf32Characters,
681 requestedNumberOfCharacters,
684 // Create the paragraph info.
685 mLogicalModel->CreateParagraphInfo( startIndex,
686 requestedNumberOfCharacters );
690 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
691 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
693 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
694 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
696 SetWordBreakInfo( utf32Characters,
698 requestedNumberOfCharacters,
703 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
704 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
706 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
707 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
709 if( getScripts || validateFonts )
711 // Validates the fonts assigned by the application or assigns default ones.
712 // It makes sure all the characters are going to be rendered by the correct font.
713 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
717 // Retrieves the scripts used in the text.
718 multilanguageSupport.SetScripts( utf32Characters,
720 requestedNumberOfCharacters,
726 // Validate the fonts set through the mark-up string.
727 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
729 // Get the default font id.
730 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
732 // Validates the fonts. If there is a character with no assigned font it sets a default one.
733 // After this call, fonts are validated.
734 multilanguageSupport.ValidateFonts( utf32Characters,
739 requestedNumberOfCharacters,
745 Vector<Character> mirroredUtf32Characters;
746 bool textMirrored = false;
747 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
748 if( NO_OPERATION != ( BIDI_INFO & operations ) )
750 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
751 bidirectionalInfo.Reserve( numberOfParagraphs );
753 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
754 SetBidirectionalInfo( utf32Characters,
758 requestedNumberOfCharacters,
761 if( 0u != bidirectionalInfo.Count() )
763 // Only set the character directions if there is right to left characters.
764 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
765 GetCharactersDirection( bidirectionalInfo,
768 requestedNumberOfCharacters,
771 // This paragraph has right to left text. Some characters may need to be mirrored.
772 // TODO: consider if the mirrored string can be stored as well.
774 textMirrored = GetMirroredText( utf32Characters,
778 requestedNumberOfCharacters,
779 mirroredUtf32Characters );
783 // There is no right to left characters. Clear the directions vector.
784 mLogicalModel->mCharacterDirections.Clear();
789 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
790 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
791 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
792 Vector<GlyphIndex> newParagraphGlyphs;
793 newParagraphGlyphs.Reserve( numberOfParagraphs );
795 const Length currentNumberOfGlyphs = glyphs.Count();
796 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
798 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
800 ShapeText( textToShape,
805 mTextUpdateInfo.mStartGlyphIndex,
806 requestedNumberOfCharacters,
808 glyphsToCharactersMap,
810 newParagraphGlyphs );
812 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
813 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
814 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
818 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
820 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
822 GlyphInfo* glyphsBuffer = glyphs.Begin();
823 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
825 // Update the width and advance of all new paragraph characters.
826 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
828 const GlyphIndex index = *it;
829 GlyphInfo& glyph = *( glyphsBuffer + index );
831 glyph.xBearing = 0.f;
838 if( NO_OPERATION != ( COLOR & operations ) )
840 // Set the color runs in glyphs.
841 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
842 mVisualModel->mCharactersToGlyph,
843 mVisualModel->mGlyphsPerCharacter,
845 mTextUpdateInfo.mStartGlyphIndex,
846 requestedNumberOfCharacters,
847 mVisualModel->mColors,
848 mVisualModel->mColorIndices );
853 if( ( NULL != mEventData ) &&
854 mEventData->mPreEditFlag &&
855 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
857 // Add the underline for the pre-edit text.
858 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
859 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
861 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
862 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
863 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
864 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
866 GlyphRun underlineRun;
867 underlineRun.glyphIndex = glyphStart;
868 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
870 // TODO: At the moment the underline runs are only for pre-edit.
871 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
874 // The estimated number of lines. Used to avoid reallocations when layouting.
875 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
877 // Set the previous number of characters for the next time the text is updated.
878 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
883 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
885 // Sets the default text's color.
886 inputStyle.textColor = mTextColor;
887 inputStyle.isDefaultColor = true;
889 inputStyle.familyName.clear();
890 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
891 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
892 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
893 inputStyle.size = 0.f;
895 inputStyle.familyDefined = false;
896 inputStyle.weightDefined = false;
897 inputStyle.widthDefined = false;
898 inputStyle.slantDefined = false;
899 inputStyle.sizeDefined = false;
901 // Sets the default font's family name, weight, width, slant and size.
904 if( mFontDefaults->familyDefined )
906 inputStyle.familyName = mFontDefaults->mFontDescription.family;
907 inputStyle.familyDefined = true;
910 if( mFontDefaults->weightDefined )
912 inputStyle.weight = mFontDefaults->mFontDescription.weight;
913 inputStyle.weightDefined = true;
916 if( mFontDefaults->widthDefined )
918 inputStyle.width = mFontDefaults->mFontDescription.width;
919 inputStyle.widthDefined = true;
922 if( mFontDefaults->slantDefined )
924 inputStyle.slant = mFontDefaults->mFontDescription.slant;
925 inputStyle.slantDefined = true;
928 if( mFontDefaults->sizeDefined )
930 inputStyle.size = mFontDefaults->mDefaultPointSize;
931 inputStyle.sizeDefined = true;
936 float Controller::Impl::GetDefaultFontLineHeight()
938 FontId defaultFontId = 0u;
939 if( NULL == mFontDefaults )
941 TextAbstraction::FontDescription fontDescription;
942 defaultFontId = mFontClient.GetFontId( fontDescription );
946 defaultFontId = mFontDefaults->GetFontId( mFontClient );
949 Text::FontMetrics fontMetrics;
950 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
952 return( fontMetrics.ascender - fontMetrics.descender );
955 void Controller::Impl::OnCursorKeyEvent( const Event& event )
957 if( NULL == mEventData )
959 // Nothing to do if there is no text input.
963 int keyCode = event.p1.mInt;
965 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
967 if( mEventData->mPrimaryCursorPosition > 0u )
969 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
972 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
974 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
976 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
979 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
983 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
988 mEventData->mUpdateCursorPosition = true;
989 mEventData->mUpdateInputStyle = true;
990 mEventData->mScrollAfterUpdatePosition = true;
993 void Controller::Impl::OnTapEvent( const Event& event )
995 if( NULL != mEventData )
997 const unsigned int tapCount = event.p1.mUint;
1001 if( IsShowingRealText() )
1003 // Convert from control's coords to text's coords.
1004 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1005 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1007 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1013 // When the cursor position is changing, delay cursor blinking
1014 mEventData->mDecorator->DelayCursorBlink();
1018 mEventData->mPrimaryCursorPosition = 0u;
1021 mEventData->mUpdateCursorPosition = true;
1022 mEventData->mScrollAfterUpdatePosition = true;
1023 mEventData->mUpdateInputStyle = true;
1025 // Notify the cursor position to the imf manager.
1026 if( mEventData->mImfManager )
1028 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1029 mEventData->mImfManager.NotifyCursorPosition();
1035 void Controller::Impl::OnPanEvent( const Event& event )
1037 if( NULL == mEventData )
1039 // Nothing to do if there is no text input.
1043 int state = event.p1.mInt;
1045 if( ( Gesture::Started == state ) ||
1046 ( Gesture::Continuing == state ) )
1048 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1049 const Vector2 currentScroll = mScrollPosition;
1051 if( mEventData->mHorizontalScrollingEnabled )
1053 const float displacementX = event.p2.mFloat;
1054 mScrollPosition.x += displacementX;
1056 ClampHorizontalScroll( actualSize );
1059 if( mEventData->mVerticalScrollingEnabled )
1061 const float displacementY = event.p3.mFloat;
1062 mScrollPosition.y += displacementY;
1064 ClampVerticalScroll( actualSize );
1067 if( mEventData->mDecorator )
1069 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1074 void Controller::Impl::OnLongPressEvent( const Event& event )
1076 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1078 if( EventData::EDITING == mEventData->mState )
1080 ChangeState ( EventData::EDITING_WITH_POPUP );
1081 mEventData->mDecoratorUpdated = true;
1085 void Controller::Impl::OnHandleEvent( const Event& event )
1087 if( NULL == mEventData )
1089 // Nothing to do if there is no text input.
1093 const unsigned int state = event.p1.mUint;
1094 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1096 if( HANDLE_PRESSED == state )
1098 // Convert from decorator's coords to text's coords.
1099 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1100 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1102 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1108 if( Event::GRAB_HANDLE_EVENT == event.type )
1110 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1112 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1114 mEventData->mPrimaryCursorPosition = handleNewPosition;
1115 mEventData->mUpdateCursorPosition = true;
1118 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1120 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1122 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1123 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1125 mEventData->mLeftSelectionPosition = handleNewPosition;
1127 mEventData->mUpdateLeftSelectionPosition = true;
1130 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1132 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1134 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1135 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1137 mEventData->mRightSelectionPosition = handleNewPosition;
1139 mEventData->mUpdateRightSelectionPosition = true;
1142 } // end ( HANDLE_PRESSED == state )
1143 else if( ( HANDLE_RELEASED == state ) ||
1144 handleStopScrolling )
1146 CharacterIndex handlePosition = 0u;
1147 if( handleStopScrolling )
1149 // Convert from decorator's coords to text's coords.
1150 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1151 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1153 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1160 if( Event::GRAB_HANDLE_EVENT == event.type )
1162 mEventData->mUpdateCursorPosition = true;
1163 mEventData->mUpdateInputStyle = true;
1165 if( !IsClipboardEmpty() )
1167 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1170 if( handleStopScrolling )
1172 mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1173 mEventData->mPrimaryCursorPosition = handlePosition;
1176 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1178 ChangeState( EventData::SELECTING );
1180 if( handleStopScrolling )
1182 mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1183 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1185 if( mEventData->mUpdateLeftSelectionPosition )
1187 mEventData->mLeftSelectionPosition = handlePosition;
1191 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1193 ChangeState( EventData::SELECTING );
1195 if( handleStopScrolling )
1197 mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1198 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1199 if( mEventData->mUpdateRightSelectionPosition )
1201 mEventData->mRightSelectionPosition = handlePosition;
1206 mEventData->mDecoratorUpdated = true;
1207 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1208 else if( HANDLE_SCROLLING == state )
1210 const float xSpeed = event.p2.mFloat;
1211 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1212 const Vector2 currentScrollPosition = mScrollPosition;
1214 mScrollPosition.x += xSpeed;
1216 ClampHorizontalScroll( actualSize );
1218 bool endOfScroll = false;
1219 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1221 // Notify the decorator there is no more text to scroll.
1222 // The decorator won't send more scroll events.
1223 mEventData->mDecorator->NotifyEndOfScroll();
1224 // Still need to set the position of the handle.
1228 // Set the position of the handle.
1229 const bool scrollRightDirection = xSpeed > 0.f;
1230 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1231 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1233 if( Event::GRAB_HANDLE_EVENT == event.type )
1235 ChangeState( EventData::GRAB_HANDLE_PANNING );
1237 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1239 // Position the grag handle close to either the left or right edge.
1240 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1242 // Get the new handle position.
1243 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1244 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1247 position.x - mScrollPosition.x,
1248 position.y - mScrollPosition.y );
1250 mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1251 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1252 mEventData->mPrimaryCursorPosition = handlePosition;
1253 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1255 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1257 // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1258 // Think if something can be done to save power.
1260 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1262 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1264 // Position the selection handle close to either the left or right edge.
1265 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1267 // Get the new handle position.
1268 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1269 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1272 position.x - mScrollPosition.x,
1273 position.y - mScrollPosition.y );
1275 if( leftSelectionHandleEvent )
1277 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1278 mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1279 if( differentHandles )
1281 mEventData->mLeftSelectionPosition = handlePosition;
1286 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1287 mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1288 if( differentHandles )
1290 mEventData->mRightSelectionPosition = handlePosition;
1294 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1296 RepositionSelectionHandles();
1298 mEventData->mScrollAfterUpdatePosition = true;
1301 mEventData->mDecoratorUpdated = true;
1302 } // end ( HANDLE_SCROLLING == state )
1305 void Controller::Impl::OnSelectEvent( const Event& event )
1307 if( NULL == mEventData )
1309 // Nothing to do if there is no text.
1313 if( mEventData->mSelectionEnabled )
1315 // Convert from control's coords to text's coords.
1316 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1317 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1319 // Calculates the logical position from the x,y coords.
1320 RepositionSelectionHandles( xPosition,
1323 mEventData->mUpdateLeftSelectionPosition = true;
1324 mEventData->mUpdateRightSelectionPosition = true;
1325 mEventData->mUpdateCursorPosition = false;
1327 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1331 void Controller::Impl::OnSelectAllEvent()
1333 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1335 if( NULL == mEventData )
1337 // Nothing to do if there is no text.
1341 if( mEventData->mSelectionEnabled )
1343 mEventData->mLeftSelectionPosition = 0u;
1344 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1346 mEventData->mScrollAfterUpdatePosition = true;
1347 mEventData->mUpdateLeftSelectionPosition = true;
1348 mEventData->mUpdateRightSelectionPosition = true;
1352 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1354 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1356 // Nothing to select if handles are in the same place.
1357 selectedText.clear();
1361 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1363 //Get start and end position of selection
1364 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1365 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1367 Vector<Character>& utf32Characters = mLogicalModel->mText;
1368 const Length numberOfCharacters = utf32Characters.Count();
1370 // Validate the start and end selection points
1371 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1373 //Get text as a UTF8 string
1374 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1376 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1378 // Set as input style the style of the first deleted character.
1379 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1381 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1383 // Mark the paragraphs to be updated.
1384 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1385 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1387 // Delete text between handles
1388 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1389 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1390 utf32Characters.Erase( first, last );
1392 // Scroll after delete.
1393 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1395 mEventData->mDecoratorUpdated = true;
1399 void Controller::Impl::ShowClipboard()
1403 mClipboard.ShowClipboard();
1407 void Controller::Impl::HideClipboard()
1409 if( mClipboard && mClipboardHideEnabled )
1411 mClipboard.HideClipboard();
1415 void Controller::Impl::SetClipboardHideEnable(bool enable)
1417 mClipboardHideEnabled = enable;
1420 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1422 //Send string to clipboard
1423 return ( mClipboard && mClipboard.SetItem( source ) );
1426 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1428 std::string selectedText;
1429 RetrieveSelection( selectedText, deleteAfterSending );
1430 CopyStringToClipboard( selectedText );
1431 ChangeState( EventData::EDITING );
1434 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1438 retrievedString = mClipboard.GetItem( itemIndex );
1442 void Controller::Impl::RepositionSelectionHandles()
1444 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1445 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1447 if( selectionStart == selectionEnd )
1449 // Nothing to select if handles are in the same place.
1453 mEventData->mDecorator->ClearHighlights();
1455 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1456 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1457 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1458 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1459 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1460 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1461 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1463 // TODO: Better algorithm to create the highlight box.
1464 // TODO: Multi-line.
1466 // Get the height of the line.
1467 const Vector<LineRun>& lines = mVisualModel->mLines;
1468 const LineRun& firstLine = *lines.Begin();
1469 const float height = firstLine.ascender + -firstLine.descender;
1471 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1472 const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1473 const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1475 // Swap the indices if the start is greater than the end.
1476 const bool indicesSwapped = selectionStart > selectionEnd;
1478 // Tell the decorator to flip the selection handles if needed.
1479 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1481 if( indicesSwapped )
1483 std::swap( selectionStart, selectionEnd );
1486 // Get the indices to the first and last selected glyphs.
1487 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1488 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1489 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1490 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1492 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1493 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1494 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1496 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1497 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1498 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1500 // Traverse the glyphs.
1501 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1503 const GlyphInfo& glyph = *( glyphsBuffer + index );
1504 const Vector2& position = *( positionsBuffer + index );
1506 if( splitStartGlyph )
1508 // 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.
1510 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1511 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1512 // Get the direction of the character.
1513 CharacterDirection isCurrentRightToLeft = false;
1514 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1516 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1519 // The end point could be in the middle of the ligature.
1520 // Calculate the number of characters selected.
1521 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1523 const float xPosition = firstLine.alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1525 mEventData->mDecorator->AddHighlight( xPosition,
1527 xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1528 mScrollPosition.y + height );
1530 splitStartGlyph = false;
1534 if( splitEndGlyph && ( index == glyphEnd ) )
1536 // 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.
1538 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1539 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1540 // Get the direction of the character.
1541 CharacterDirection isCurrentRightToLeft = false;
1542 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1544 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1547 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1549 const float xPosition = firstLine.alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1550 mEventData->mDecorator->AddHighlight( xPosition,
1552 xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1553 mScrollPosition.y + height );
1555 splitEndGlyph = false;
1559 const float xPosition = firstLine.alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1560 mEventData->mDecorator->AddHighlight( xPosition,
1562 xPosition + glyph.advance,
1563 mScrollPosition.y + height );
1566 CursorInfo primaryCursorInfo;
1567 GetCursorPosition( mEventData->mLeftSelectionPosition,
1568 primaryCursorInfo );
1570 CursorInfo secondaryCursorInfo;
1571 GetCursorPosition( mEventData->mRightSelectionPosition,
1572 secondaryCursorInfo );
1574 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1575 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1577 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1579 primaryCursorInfo.lineOffset + mScrollPosition.y,
1580 primaryCursorInfo.lineHeight );
1582 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1583 secondaryPosition.x,
1584 secondaryCursorInfo.lineOffset + mScrollPosition.y,
1585 secondaryCursorInfo.lineHeight );
1587 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1588 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1590 // Set the flag to update the decorator.
1591 mEventData->mDecoratorUpdated = true;
1594 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1596 if( NULL == mEventData )
1598 // Nothing to do if there is no text input.
1602 if( IsShowingPlaceholderText() )
1604 // Nothing to do if there is the place-holder text.
1608 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1609 const Length numberOfLines = mVisualModel->mLines.Count();
1610 if( ( 0 == numberOfGlyphs ) ||
1611 ( 0 == numberOfLines ) )
1613 // Nothing to do if there is no text.
1617 // Find which word was selected
1618 CharacterIndex selectionStart( 0 );
1619 CharacterIndex selectionEnd( 0 );
1620 FindSelectionIndices( mVisualModel,
1627 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1629 if( selectionStart == selectionEnd )
1631 ChangeState( EventData::EDITING );
1632 // Nothing to select. i.e. a white space, out of bounds
1636 mEventData->mLeftSelectionPosition = selectionStart;
1637 mEventData->mRightSelectionPosition = selectionEnd;
1640 void Controller::Impl::SetPopupButtons()
1643 * Sets the Popup buttons to be shown depending on State.
1645 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1647 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1650 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1652 if( EventData::SELECTING == mEventData->mState )
1654 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1656 if( !IsClipboardEmpty() )
1658 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1659 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1662 if( !mEventData->mAllTextSelected )
1664 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1667 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1669 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1671 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1674 if( !IsClipboardEmpty() )
1676 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1677 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1680 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1682 if ( !IsClipboardEmpty() )
1684 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1685 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1689 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1692 void Controller::Impl::ChangeState( EventData::State newState )
1694 if( NULL == mEventData )
1696 // Nothing to do if there is no text input.
1700 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1702 if( mEventData->mState != newState )
1704 mEventData->mState = newState;
1706 if( EventData::INACTIVE == mEventData->mState )
1708 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1709 mEventData->mDecorator->StopCursorBlink();
1710 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1711 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1712 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1713 mEventData->mDecorator->SetPopupActive( false );
1714 mEventData->mDecoratorUpdated = true;
1717 else if( EventData::INTERRUPTED == mEventData->mState)
1719 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1720 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1721 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1722 mEventData->mDecorator->SetPopupActive( false );
1723 mEventData->mDecoratorUpdated = true;
1726 else if( EventData::SELECTING == mEventData->mState )
1728 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1729 mEventData->mDecorator->StopCursorBlink();
1730 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1731 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1732 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1733 if( mEventData->mGrabHandlePopupEnabled )
1736 mEventData->mDecorator->SetPopupActive( true );
1738 mEventData->mDecoratorUpdated = true;
1740 else if( EventData::EDITING == mEventData->mState )
1742 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1743 if( mEventData->mCursorBlinkEnabled )
1745 mEventData->mDecorator->StartCursorBlink();
1747 // Grab handle is not shown until a tap is received whilst EDITING
1748 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1749 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1750 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1751 if( mEventData->mGrabHandlePopupEnabled )
1753 mEventData->mDecorator->SetPopupActive( false );
1755 mEventData->mDecoratorUpdated = true;
1758 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1760 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1762 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1763 if( mEventData->mCursorBlinkEnabled )
1765 mEventData->mDecorator->StartCursorBlink();
1767 if( mEventData->mSelectionEnabled )
1769 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1770 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1774 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1776 if( mEventData->mGrabHandlePopupEnabled )
1779 mEventData->mDecorator->SetPopupActive( true );
1782 mEventData->mDecoratorUpdated = true;
1784 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1786 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1788 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1789 if( mEventData->mCursorBlinkEnabled )
1791 mEventData->mDecorator->StartCursorBlink();
1793 // Grab handle is not shown until a tap is received whilst EDITING
1794 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1795 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1796 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1797 if( mEventData->mGrabHandlePopupEnabled )
1799 mEventData->mDecorator->SetPopupActive( false );
1801 mEventData->mDecoratorUpdated = true;
1804 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1806 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1807 mEventData->mDecorator->StopCursorBlink();
1808 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1809 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1810 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1811 if( mEventData->mGrabHandlePopupEnabled )
1813 mEventData->mDecorator->SetPopupActive( false );
1815 mEventData->mDecoratorUpdated = true;
1817 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1819 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1821 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1822 if( mEventData->mCursorBlinkEnabled )
1824 mEventData->mDecorator->StartCursorBlink();
1826 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1827 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1828 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1829 if( mEventData->mGrabHandlePopupEnabled )
1831 mEventData->mDecorator->SetPopupActive( false );
1833 mEventData->mDecoratorUpdated = true;
1835 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1837 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1839 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1840 if( mEventData->mCursorBlinkEnabled )
1842 mEventData->mDecorator->StartCursorBlink();
1845 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1846 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1847 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1849 if( mEventData->mGrabHandlePopupEnabled )
1852 mEventData->mDecorator->SetPopupActive( true );
1855 mEventData->mDecoratorUpdated = true;
1860 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1861 CursorInfo& cursorInfo )
1863 if( !IsShowingRealText() )
1865 // Do not want to use the place-holder text to set the cursor position.
1867 // Use the line's height of the font's family set to set the cursor's size.
1868 // If there is no font's family set, use the default font.
1869 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1871 cursorInfo.lineOffset = 0.f;
1872 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1873 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1875 switch( mLayoutEngine.GetHorizontalAlignment() )
1877 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1879 cursorInfo.primaryPosition.x = 0.f;
1882 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1884 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
1887 case LayoutEngine::HORIZONTAL_ALIGN_END:
1889 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1894 // Nothing else to do.
1898 Text::GetCursorPosition( mVisualModel,
1904 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
1906 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1908 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1909 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1911 if( 0.f > cursorInfo.primaryPosition.x )
1913 cursorInfo.primaryPosition.x = 0.f;
1916 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
1917 if( cursorInfo.primaryPosition.x > edgeWidth )
1919 cursorInfo.primaryPosition.x = edgeWidth;
1924 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1926 if( NULL == mEventData )
1928 // Nothing to do if there is no text input.
1932 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1934 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1935 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1937 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1938 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1940 if( numberOfCharacters > 1u )
1942 const Script script = mLogicalModel->GetScript( index );
1943 if( HasLigatureMustBreak( script ) )
1945 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
1946 numberOfCharacters = 1u;
1951 while( 0u == numberOfCharacters )
1954 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1958 if( index < mEventData->mPrimaryCursorPosition )
1960 cursorIndex -= numberOfCharacters;
1964 cursorIndex += numberOfCharacters;
1970 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1972 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1973 if( NULL == mEventData )
1975 // Nothing to do if there is no text input.
1976 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1980 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
1982 // Sets the cursor position.
1983 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1986 cursorInfo.primaryCursorHeight,
1987 cursorInfo.lineHeight );
1988 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1990 // Sets the grab handle position.
1991 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1993 cursorInfo.lineOffset + mScrollPosition.y,
1994 cursorInfo.lineHeight );
1996 if( cursorInfo.isSecondaryCursor )
1998 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1999 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2000 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2001 cursorInfo.secondaryCursorHeight,
2002 cursorInfo.lineHeight );
2003 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2006 // Set which cursors are active according the state.
2007 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2009 if( cursorInfo.isSecondaryCursor )
2011 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2015 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2020 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2023 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2026 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2027 const CursorInfo& cursorInfo )
2029 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2030 ( RIGHT_SELECTION_HANDLE != handleType ) )
2035 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2037 // Sets the handle's position.
2038 mEventData->mDecorator->SetPosition( handleType,
2040 cursorInfo.lineOffset + mScrollPosition.y,
2041 cursorInfo.lineHeight );
2043 // If selection handle at start of the text and other at end of the text then all text is selected.
2044 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2045 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2046 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2049 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2051 // Clamp between -space & 0.
2053 if( actualSize.width > mVisualModel->mControlSize.width )
2055 const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2056 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2057 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2059 mEventData->mDecoratorUpdated = true;
2063 mScrollPosition.x = 0.f;
2067 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2069 // Clamp between -space & 0.
2070 if( actualSize.height > mVisualModel->mControlSize.height )
2072 const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2073 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2074 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2076 mEventData->mDecoratorUpdated = true;
2080 mScrollPosition.y = 0.f;
2084 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2086 const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2088 // position is in actor's coords.
2089 const float positionEnd = position.x + cursorWidth;
2091 // Transform the position to decorator coords.
2092 const float decoratorPositionBegin = position.x + mScrollPosition.x;
2093 const float decoratorPositionEnd = positionEnd + mScrollPosition.x;
2095 if( decoratorPositionBegin < 0.f )
2097 mScrollPosition.x = -position.x;
2099 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2101 mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd;
2105 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2107 // Get the current cursor position in decorator coords.
2108 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2110 // Calculate the offset to match the cursor position before the character was deleted.
2111 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2113 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2115 // Makes the new cursor position visible if needed.
2116 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2119 void Controller::Impl::RequestRelayout()
2121 mControlInterface.RequestTextRelayout();
2126 } // namespace Toolkit