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 ),
62 mPrimaryCursorPosition( 0u ),
63 mLeftSelectionPosition( 0u ),
64 mRightSelectionPosition( 0u ),
65 mPreEditStartPosition( 0u ),
67 mIsShowingPlaceholderText( false ),
68 mPreEditFlag( false ),
69 mDecoratorUpdated( false ),
70 mCursorBlinkEnabled( true ),
71 mGrabHandleEnabled( true ),
72 mGrabHandlePopupEnabled( true ),
73 mSelectionEnabled( true ),
74 mHorizontalScrollingEnabled( true ),
75 mVerticalScrollingEnabled( false ),
76 mUpdateCursorPosition( false ),
77 mUpdateLeftSelectionPosition( false ),
78 mUpdateRightSelectionPosition( false ),
79 mScrollAfterUpdatePosition( false ),
80 mScrollAfterDelete( false ),
81 mAllTextSelected( false ),
82 mUpdateInputStyle( false )
84 mImfManager = ImfManager::Get();
87 EventData::~EventData()
90 bool Controller::Impl::ProcessInputEvents()
92 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
93 if( NULL == mEventData )
95 // Nothing to do if there is no text input.
96 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
100 if( mEventData->mDecorator )
102 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
103 iter != mEventData->mEventQueue.end();
108 case Event::CURSOR_KEY_EVENT:
110 OnCursorKeyEvent( *iter );
113 case Event::TAP_EVENT:
118 case Event::LONG_PRESS_EVENT:
120 OnLongPressEvent( *iter );
123 case Event::PAN_EVENT:
128 case Event::GRAB_HANDLE_EVENT:
129 case Event::LEFT_SELECTION_HANDLE_EVENT:
130 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
132 OnHandleEvent( *iter );
137 OnSelectEvent( *iter );
140 case Event::SELECT_ALL:
149 // The cursor must also be repositioned after inserts into the model
150 if( mEventData->mUpdateCursorPosition )
152 // Updates the cursor position and scrolls the text to make it visible.
153 CursorInfo cursorInfo;
154 GetCursorPosition( mEventData->mPrimaryCursorPosition,
157 // Scroll first the text after delete ...
158 if( mEventData->mScrollAfterDelete )
160 ScrollTextToMatchCursor( cursorInfo );
163 // ... then, text can be scrolled to make the cursor visible.
164 if( mEventData->mScrollAfterUpdatePosition )
166 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
168 mEventData->mScrollAfterUpdatePosition = false;
169 mEventData->mScrollAfterDelete = false;
171 UpdateCursorPosition( cursorInfo );
173 mEventData->mDecoratorUpdated = true;
174 mEventData->mUpdateCursorPosition = false;
178 bool leftScroll = false;
179 bool rightScroll = false;
181 CursorInfo leftHandleInfo;
182 CursorInfo rightHandleInfo;
184 if( mEventData->mUpdateLeftSelectionPosition )
186 GetCursorPosition( mEventData->mLeftSelectionPosition,
189 if( mEventData->mScrollAfterUpdatePosition )
191 ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
196 if( mEventData->mUpdateRightSelectionPosition )
198 GetCursorPosition( mEventData->mRightSelectionPosition,
201 if( mEventData->mScrollAfterUpdatePosition )
203 ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
208 if( mEventData->mUpdateLeftSelectionPosition )
210 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
214 mEventData->mDecoratorUpdated = true;
217 if( mEventData->mUpdateRightSelectionPosition )
219 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
223 mEventData->mDecoratorUpdated = true;
226 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
228 RepositionSelectionHandles();
230 mEventData->mUpdateLeftSelectionPosition = false;
231 mEventData->mUpdateRightSelectionPosition = false;
234 if( leftScroll || rightScroll )
236 mEventData->mScrollAfterUpdatePosition = false;
240 if( mEventData->mUpdateInputStyle )
242 // Set the default style first.
243 RetrieveDefaultInputStyle( mEventData->mInputStyle );
245 // Get the character index from the cursor index.
246 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
248 // Retrieve the style from the style runs stored in the logical model.
249 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
251 mEventData->mUpdateInputStyle = false;
254 mEventData->mEventQueue.clear();
256 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
258 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
259 mEventData->mDecoratorUpdated = false;
261 return decoratorUpdated;
264 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
266 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
267 mTextUpdateInfo.mStartGlyphIndex = 0u;
268 mTextUpdateInfo.mStartLineIndex = 0u;
269 numberOfCharacters = 0u;
271 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
272 if( 0u == numberOfParagraphs )
274 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
275 numberOfCharacters = 0u;
277 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
279 // Nothing else to do if there are no paragraphs.
283 // Find the paragraphs to be updated.
284 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
285 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
287 // Text is being added at the end of the current text.
288 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
290 // Text is being added in a new paragraph after the last character of the text.
291 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
292 numberOfCharacters = 0u;
293 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
295 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
296 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
298 // Nothing else to do;
302 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
306 Length numberOfCharactersToUpdate = 0u;
307 if( mTextUpdateInfo.mFullRelayoutNeeded )
309 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
313 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
315 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
316 numberOfCharactersToUpdate,
317 paragraphsToBeUpdated );
320 if( 0u != paragraphsToBeUpdated.Count() )
322 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
323 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
324 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
326 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
327 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
329 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
330 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
331 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
332 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
334 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
335 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
337 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
341 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
345 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
346 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
349 void Controller::Impl::ClearFullModelData( OperationsMask operations )
351 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
353 mLogicalModel->mLineBreakInfo.Clear();
354 mLogicalModel->mParagraphInfo.Clear();
357 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
359 mLogicalModel->mLineBreakInfo.Clear();
362 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
364 mLogicalModel->mScriptRuns.Clear();
367 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
369 mLogicalModel->mFontRuns.Clear();
372 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
374 if( NO_OPERATION != ( BIDI_INFO & operations ) )
376 mLogicalModel->mBidirectionalParagraphInfo.Clear();
377 mLogicalModel->mCharacterDirections.Clear();
380 if( NO_OPERATION != ( REORDER & operations ) )
382 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
383 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
384 endIt = mLogicalModel->mBidirectionalLineInfo.End();
388 BidirectionalLineInfoRun& bidiLineInfo = *it;
390 free( bidiLineInfo.visualToLogicalMap );
391 bidiLineInfo.visualToLogicalMap = NULL;
393 mLogicalModel->mBidirectionalLineInfo.Clear();
397 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
399 mVisualModel->mGlyphs.Clear();
400 mVisualModel->mGlyphsToCharacters.Clear();
401 mVisualModel->mCharactersToGlyph.Clear();
402 mVisualModel->mCharactersPerGlyph.Clear();
403 mVisualModel->mGlyphsPerCharacter.Clear();
404 mVisualModel->mGlyphPositions.Clear();
407 if( NO_OPERATION != ( LAYOUT & operations ) )
409 mVisualModel->mLines.Clear();
412 if( NO_OPERATION != ( COLOR & operations ) )
414 mVisualModel->mColorIndices.Clear();
418 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
420 const CharacterIndex endIndexPlusOne = endIndex + 1u;
422 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
424 // Clear the line break info.
425 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
427 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
428 lineBreakInfoBuffer + endIndexPlusOne );
430 // Clear the paragraphs.
431 ClearCharacterRuns( startIndex,
433 mLogicalModel->mParagraphInfo );
436 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
438 // Clear the word break info.
439 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
441 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
442 wordBreakInfoBuffer + endIndexPlusOne );
445 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
447 // Clear the scripts.
448 ClearCharacterRuns( startIndex,
450 mLogicalModel->mScriptRuns );
453 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
456 ClearCharacterRuns( startIndex,
458 mLogicalModel->mFontRuns );
461 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
463 if( NO_OPERATION != ( BIDI_INFO & operations ) )
465 // Clear the bidirectional paragraph info.
466 ClearCharacterRuns( startIndex,
468 mLogicalModel->mBidirectionalParagraphInfo );
470 // Clear the character's directions.
471 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
473 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
474 characterDirectionsBuffer + endIndexPlusOne );
477 if( NO_OPERATION != ( REORDER & operations ) )
479 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
480 uint32_t endRemoveIndex = startRemoveIndex;
481 ClearCharacterRuns( startIndex,
483 mLogicalModel->mBidirectionalLineInfo,
487 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
489 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
490 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
491 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
495 BidirectionalLineInfoRun& bidiLineInfo = *it;
497 free( bidiLineInfo.visualToLogicalMap );
498 bidiLineInfo.visualToLogicalMap = NULL;
501 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
502 bidirectionalLineInfoBuffer + endRemoveIndex );
507 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
509 const CharacterIndex endIndexPlusOne = endIndex + 1u;
510 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
512 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
513 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
514 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
516 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
517 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
519 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
521 // Update the character to glyph indices.
522 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
523 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
527 CharacterIndex& index = *it;
528 index -= numberOfGlyphsRemoved;
531 // Clear the character to glyph conversion table.
532 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
533 charactersToGlyphBuffer + endIndexPlusOne );
535 // Clear the glyphs per character table.
536 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
537 glyphsPerCharacterBuffer + endIndexPlusOne );
539 // Clear the glyphs buffer.
540 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
541 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
542 glyphsBuffer + endGlyphIndexPlusOne );
544 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
546 // Update the glyph to character indices.
547 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
548 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
552 CharacterIndex& index = *it;
553 index -= numberOfCharactersRemoved;
556 // Clear the glyphs to characters buffer.
557 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
558 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
560 // Clear the characters per glyph buffer.
561 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
562 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
563 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
565 // Clear the positions buffer.
566 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
567 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
568 positionsBuffer + endGlyphIndexPlusOne );
571 if( NO_OPERATION != ( LAYOUT & operations ) )
574 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
575 uint32_t endRemoveIndex = startRemoveIndex;
576 ClearCharacterRuns( startIndex,
578 mVisualModel->mLines,
582 // Will update the glyph runs.
583 startRemoveIndex = mVisualModel->mLines.Count();
584 endRemoveIndex = startRemoveIndex;
585 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
586 endGlyphIndexPlusOne - 1u,
587 mVisualModel->mLines,
591 // Set the line index from where to insert the new laid-out lines.
592 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
594 LineRun* linesBuffer = mVisualModel->mLines.Begin();
595 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
596 linesBuffer + endRemoveIndex );
599 if( NO_OPERATION != ( COLOR & operations ) )
601 if( 0u != mVisualModel->mColorIndices.Count() )
603 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
604 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
605 colorIndexBuffer + endGlyphIndexPlusOne );
610 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
612 if( mTextUpdateInfo.mClearAll ||
613 ( ( 0u == startIndex ) &&
614 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
616 ClearFullModelData( operations );
620 // Clear the model data related with characters.
621 ClearCharacterModelData( startIndex, endIndex, operations );
623 // Clear the model data related with glyphs.
624 ClearGlyphModelData( startIndex, endIndex, operations );
627 // The estimated number of lines. Used to avoid reallocations when layouting.
628 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
630 mVisualModel->ClearCaches();
633 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
635 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
637 // Calculate the operations to be done.
638 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
640 if( NO_OPERATION == operations )
642 // Nothing to do if no operations are pending and required.
646 Vector<Character>& utf32Characters = mLogicalModel->mText;
648 const Length numberOfCharacters = utf32Characters.Count();
650 // Index to the first character of the first paragraph to be updated.
651 CharacterIndex startIndex = 0u;
652 // Number of characters of the paragraphs to be removed.
653 Length paragraphCharacters = 0u;
655 CalculateTextUpdateIndices( paragraphCharacters );
656 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
658 if( mTextUpdateInfo.mClearAll ||
659 ( 0u != paragraphCharacters ) )
661 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
664 mTextUpdateInfo.mClearAll = false;
666 // Whether the model is updated.
667 bool updated = false;
669 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
670 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
672 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
674 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
675 // calculate the bidirectional info for each 'paragraph'.
676 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
677 // is not shaped together).
678 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
680 SetLineBreakInfo( utf32Characters,
682 requestedNumberOfCharacters,
685 // Create the paragraph info.
686 mLogicalModel->CreateParagraphInfo( startIndex,
687 requestedNumberOfCharacters );
691 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
692 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
694 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
695 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
697 SetWordBreakInfo( utf32Characters,
699 requestedNumberOfCharacters,
704 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
705 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
707 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
708 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
710 if( getScripts || validateFonts )
712 // Validates the fonts assigned by the application or assigns default ones.
713 // It makes sure all the characters are going to be rendered by the correct font.
714 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
718 // Retrieves the scripts used in the text.
719 multilanguageSupport.SetScripts( utf32Characters,
721 requestedNumberOfCharacters,
727 // Validate the fonts set through the mark-up string.
728 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
730 // Get the default font id.
731 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
733 // Validates the fonts. If there is a character with no assigned font it sets a default one.
734 // After this call, fonts are validated.
735 multilanguageSupport.ValidateFonts( utf32Characters,
740 requestedNumberOfCharacters,
746 Vector<Character> mirroredUtf32Characters;
747 bool textMirrored = false;
748 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
749 if( NO_OPERATION != ( BIDI_INFO & operations ) )
751 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
752 bidirectionalInfo.Reserve( numberOfParagraphs );
754 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
755 SetBidirectionalInfo( utf32Characters,
759 requestedNumberOfCharacters,
762 if( 0u != bidirectionalInfo.Count() )
764 // Only set the character directions if there is right to left characters.
765 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
766 GetCharactersDirection( bidirectionalInfo,
769 requestedNumberOfCharacters,
772 // This paragraph has right to left text. Some characters may need to be mirrored.
773 // TODO: consider if the mirrored string can be stored as well.
775 textMirrored = GetMirroredText( utf32Characters,
779 requestedNumberOfCharacters,
780 mirroredUtf32Characters );
784 // There is no right to left characters. Clear the directions vector.
785 mLogicalModel->mCharacterDirections.Clear();
790 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
791 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
792 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
793 Vector<GlyphIndex> newParagraphGlyphs;
794 newParagraphGlyphs.Reserve( numberOfParagraphs );
796 const Length currentNumberOfGlyphs = glyphs.Count();
797 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
799 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
801 ShapeText( textToShape,
806 mTextUpdateInfo.mStartGlyphIndex,
807 requestedNumberOfCharacters,
809 glyphsToCharactersMap,
811 newParagraphGlyphs );
813 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
814 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
815 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
819 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
821 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
823 GlyphInfo* glyphsBuffer = glyphs.Begin();
824 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
826 // Update the width and advance of all new paragraph characters.
827 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
829 const GlyphIndex index = *it;
830 GlyphInfo& glyph = *( glyphsBuffer + index );
832 glyph.xBearing = 0.f;
839 if( NO_OPERATION != ( COLOR & operations ) )
841 // Set the color runs in glyphs.
842 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
843 mVisualModel->mCharactersToGlyph,
844 mVisualModel->mGlyphsPerCharacter,
846 mTextUpdateInfo.mStartGlyphIndex,
847 requestedNumberOfCharacters,
848 mVisualModel->mColors,
849 mVisualModel->mColorIndices );
854 if( ( NULL != mEventData ) &&
855 mEventData->mPreEditFlag &&
856 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
858 // Add the underline for the pre-edit text.
859 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
860 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
862 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
863 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
864 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
865 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
867 GlyphRun underlineRun;
868 underlineRun.glyphIndex = glyphStart;
869 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
871 // TODO: At the moment the underline runs are only for pre-edit.
872 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
875 // The estimated number of lines. Used to avoid reallocations when layouting.
876 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
878 // Set the previous number of characters for the next time the text is updated.
879 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
884 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
886 // Sets the default text's color.
887 inputStyle.textColor = mTextColor;
888 inputStyle.isDefaultColor = true;
890 inputStyle.familyName.clear();
891 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
892 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
893 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
894 inputStyle.size = 0.f;
896 inputStyle.familyDefined = false;
897 inputStyle.weightDefined = false;
898 inputStyle.widthDefined = false;
899 inputStyle.slantDefined = false;
900 inputStyle.sizeDefined = false;
902 // Sets the default font's family name, weight, width, slant and size.
905 if( mFontDefaults->familyDefined )
907 inputStyle.familyName = mFontDefaults->mFontDescription.family;
908 inputStyle.familyDefined = true;
911 if( mFontDefaults->weightDefined )
913 inputStyle.weight = mFontDefaults->mFontDescription.weight;
914 inputStyle.weightDefined = true;
917 if( mFontDefaults->widthDefined )
919 inputStyle.width = mFontDefaults->mFontDescription.width;
920 inputStyle.widthDefined = true;
923 if( mFontDefaults->slantDefined )
925 inputStyle.slant = mFontDefaults->mFontDescription.slant;
926 inputStyle.slantDefined = true;
929 if( mFontDefaults->sizeDefined )
931 inputStyle.size = mFontDefaults->mDefaultPointSize;
932 inputStyle.sizeDefined = true;
937 float Controller::Impl::GetDefaultFontLineHeight()
939 FontId defaultFontId = 0u;
940 if( NULL == mFontDefaults )
942 TextAbstraction::FontDescription fontDescription;
943 defaultFontId = mFontClient.GetFontId( fontDescription );
947 defaultFontId = mFontDefaults->GetFontId( mFontClient );
950 Text::FontMetrics fontMetrics;
951 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
953 return( fontMetrics.ascender - fontMetrics.descender );
956 void Controller::Impl::OnCursorKeyEvent( const Event& event )
958 if( NULL == mEventData )
960 // Nothing to do if there is no text input.
964 int keyCode = event.p1.mInt;
966 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
968 if( mEventData->mPrimaryCursorPosition > 0u )
970 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
973 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
975 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
977 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
980 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
984 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
989 mEventData->mUpdateCursorPosition = true;
990 mEventData->mUpdateInputStyle = true;
991 mEventData->mScrollAfterUpdatePosition = true;
994 void Controller::Impl::OnTapEvent( const Event& event )
996 if( NULL != mEventData )
998 const unsigned int tapCount = event.p1.mUint;
1000 if( 1u == tapCount )
1002 if( IsShowingRealText() )
1004 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1005 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.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 = mEventData->mScrollPosition;
1051 if( mEventData->mHorizontalScrollingEnabled )
1053 const float displacementX = event.p2.mFloat;
1054 mEventData->mScrollPosition.x += displacementX;
1056 ClampHorizontalScroll( actualSize );
1059 if( mEventData->mVerticalScrollingEnabled )
1061 const float displacementY = event.p3.mFloat;
1062 mEventData->mScrollPosition.y += displacementY;
1064 ClampVerticalScroll( actualSize );
1067 if( mEventData->mDecorator )
1069 mEventData->mDecorator->UpdatePositions( mEventData->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 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1099 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1100 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.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 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1150 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1151 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.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 = mEventData->mScrollPosition;
1214 mEventData->mScrollPosition.x += xSpeed;
1216 ClampHorizontalScroll( actualSize );
1218 bool endOfScroll = false;
1219 if( Vector2::ZERO == ( currentScrollPosition - mEventData->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 coords. Need to transforms to text coords.
1244 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1247 position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1248 position.y - mEventData->mScrollPosition.y - mAlignmentOffset.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 coords. Need to transforms to text coords.
1269 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1272 position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1273 position.y - mEventData->mScrollPosition.y - mAlignmentOffset.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 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1316 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1317 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.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 const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1502 // Traverse the glyphs.
1503 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1505 const GlyphInfo& glyph = *( glyphsBuffer + index );
1506 const Vector2& position = *( positionsBuffer + index );
1508 if( splitStartGlyph )
1510 // 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.
1512 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1513 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1514 // Get the direction of the character.
1515 CharacterDirection isCurrentRightToLeft = false;
1516 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1518 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1521 // The end point could be in the middle of the ligature.
1522 // Calculate the number of characters selected.
1523 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1525 const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1527 mEventData->mDecorator->AddHighlight( xPosition,
1529 xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1530 offset.y + height );
1532 splitStartGlyph = false;
1536 if( splitEndGlyph && ( index == glyphEnd ) )
1538 // 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.
1540 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1541 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1542 // Get the direction of the character.
1543 CharacterDirection isCurrentRightToLeft = false;
1544 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1546 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1549 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1551 const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1552 mEventData->mDecorator->AddHighlight( xPosition,
1554 xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1555 offset.y + height );
1557 splitEndGlyph = false;
1561 const float xPosition = position.x - glyph.xBearing + offset.x;
1562 mEventData->mDecorator->AddHighlight( xPosition,
1564 xPosition + glyph.advance,
1565 offset.y + height );
1568 CursorInfo primaryCursorInfo;
1569 GetCursorPosition( mEventData->mLeftSelectionPosition,
1570 primaryCursorInfo );
1572 CursorInfo secondaryCursorInfo;
1573 GetCursorPosition( mEventData->mRightSelectionPosition,
1574 secondaryCursorInfo );
1576 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1577 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1579 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1581 primaryCursorInfo.lineOffset + offset.y,
1582 primaryCursorInfo.lineHeight );
1584 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1585 secondaryPosition.x,
1586 secondaryCursorInfo.lineOffset + offset.y,
1587 secondaryCursorInfo.lineHeight );
1589 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1590 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1592 // Set the flag to update the decorator.
1593 mEventData->mDecoratorUpdated = true;
1596 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1598 if( NULL == mEventData )
1600 // Nothing to do if there is no text input.
1604 if( IsShowingPlaceholderText() )
1606 // Nothing to do if there is the place-holder text.
1610 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1611 const Length numberOfLines = mVisualModel->mLines.Count();
1612 if( ( 0 == numberOfGlyphs ) ||
1613 ( 0 == numberOfLines ) )
1615 // Nothing to do if there is no text.
1619 // Find which word was selected
1620 CharacterIndex selectionStart( 0 );
1621 CharacterIndex selectionEnd( 0 );
1622 FindSelectionIndices( mVisualModel,
1629 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1631 if( selectionStart == selectionEnd )
1633 ChangeState( EventData::EDITING );
1634 // Nothing to select. i.e. a white space, out of bounds
1638 mEventData->mLeftSelectionPosition = selectionStart;
1639 mEventData->mRightSelectionPosition = selectionEnd;
1642 void Controller::Impl::SetPopupButtons()
1645 * Sets the Popup buttons to be shown depending on State.
1647 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1649 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1652 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1654 if( EventData::SELECTING == mEventData->mState )
1656 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1658 if( !IsClipboardEmpty() )
1660 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1661 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1664 if( !mEventData->mAllTextSelected )
1666 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1669 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1671 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1673 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1676 if( !IsClipboardEmpty() )
1678 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1679 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1682 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1684 if ( !IsClipboardEmpty() )
1686 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1687 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1691 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1694 void Controller::Impl::ChangeState( EventData::State newState )
1696 if( NULL == mEventData )
1698 // Nothing to do if there is no text input.
1702 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1704 if( mEventData->mState != newState )
1706 mEventData->mState = newState;
1708 if( EventData::INACTIVE == mEventData->mState )
1710 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1711 mEventData->mDecorator->StopCursorBlink();
1712 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1713 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1714 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1715 mEventData->mDecorator->SetPopupActive( false );
1716 mEventData->mDecoratorUpdated = true;
1719 else if( EventData::INTERRUPTED == mEventData->mState)
1721 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1722 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1723 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1724 mEventData->mDecorator->SetPopupActive( false );
1725 mEventData->mDecoratorUpdated = true;
1728 else if( EventData::SELECTING == mEventData->mState )
1730 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1731 mEventData->mDecorator->StopCursorBlink();
1732 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1733 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1734 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1735 if( mEventData->mGrabHandlePopupEnabled )
1738 mEventData->mDecorator->SetPopupActive( true );
1740 mEventData->mDecoratorUpdated = true;
1742 else if( EventData::EDITING == mEventData->mState )
1744 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1745 if( mEventData->mCursorBlinkEnabled )
1747 mEventData->mDecorator->StartCursorBlink();
1749 // Grab handle is not shown until a tap is received whilst EDITING
1750 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1751 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1752 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1753 if( mEventData->mGrabHandlePopupEnabled )
1755 mEventData->mDecorator->SetPopupActive( false );
1757 mEventData->mDecoratorUpdated = true;
1760 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1762 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1764 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1765 if( mEventData->mCursorBlinkEnabled )
1767 mEventData->mDecorator->StartCursorBlink();
1769 if( mEventData->mSelectionEnabled )
1771 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1772 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1776 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1778 if( mEventData->mGrabHandlePopupEnabled )
1781 mEventData->mDecorator->SetPopupActive( true );
1784 mEventData->mDecoratorUpdated = true;
1786 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1788 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1790 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1791 if( mEventData->mCursorBlinkEnabled )
1793 mEventData->mDecorator->StartCursorBlink();
1795 // Grab handle is not shown until a tap is received whilst EDITING
1796 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1797 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1798 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1799 if( mEventData->mGrabHandlePopupEnabled )
1801 mEventData->mDecorator->SetPopupActive( false );
1803 mEventData->mDecoratorUpdated = true;
1806 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1808 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1809 mEventData->mDecorator->StopCursorBlink();
1810 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1811 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1812 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1813 if( mEventData->mGrabHandlePopupEnabled )
1815 mEventData->mDecorator->SetPopupActive( false );
1817 mEventData->mDecoratorUpdated = true;
1819 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1821 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1823 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1824 if( mEventData->mCursorBlinkEnabled )
1826 mEventData->mDecorator->StartCursorBlink();
1828 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1829 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1830 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1831 if( mEventData->mGrabHandlePopupEnabled )
1833 mEventData->mDecorator->SetPopupActive( false );
1835 mEventData->mDecoratorUpdated = true;
1837 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1839 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1841 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1842 if( mEventData->mCursorBlinkEnabled )
1844 mEventData->mDecorator->StartCursorBlink();
1847 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1848 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1849 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1851 if( mEventData->mGrabHandlePopupEnabled )
1854 mEventData->mDecorator->SetPopupActive( true );
1857 mEventData->mDecoratorUpdated = true;
1862 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1863 CursorInfo& cursorInfo )
1865 if( !IsShowingRealText() )
1867 // Do not want to use the place-holder text to set the cursor position.
1869 // Use the line's height of the font's family set to set the cursor's size.
1870 // If there is no font's family set, use the default font.
1871 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1873 cursorInfo.lineOffset = 0.f;
1874 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1875 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1877 switch( mLayoutEngine.GetHorizontalAlignment() )
1879 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1881 cursorInfo.primaryPosition.x = 0.f;
1884 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1886 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
1889 case LayoutEngine::HORIZONTAL_ALIGN_END:
1891 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1896 switch( mLayoutEngine.GetVerticalAlignment() )
1898 case LayoutEngine::VERTICAL_ALIGN_TOP:
1900 cursorInfo.primaryPosition.y = 0.f;
1903 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1905 cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
1908 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1910 cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
1915 // Nothing else to do.
1919 Text::GetCursorPosition( mVisualModel,
1925 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
1927 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1929 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1930 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1932 if( 0.f > cursorInfo.primaryPosition.x )
1934 cursorInfo.primaryPosition.x = 0.f;
1937 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
1938 if( cursorInfo.primaryPosition.x > edgeWidth )
1940 cursorInfo.primaryPosition.x = edgeWidth;
1945 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1947 if( NULL == mEventData )
1949 // Nothing to do if there is no text input.
1953 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1955 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1956 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1958 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1959 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1961 if( numberOfCharacters > 1u )
1963 const Script script = mLogicalModel->GetScript( index );
1964 if( HasLigatureMustBreak( script ) )
1966 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
1967 numberOfCharacters = 1u;
1972 while( 0u == numberOfCharacters )
1975 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1979 if( index < mEventData->mPrimaryCursorPosition )
1981 cursorIndex -= numberOfCharacters;
1985 cursorIndex += numberOfCharacters;
1991 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1993 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1994 if( NULL == mEventData )
1996 // Nothing to do if there is no text input.
1997 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2001 const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
2002 const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2004 // Sets the cursor position.
2005 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2008 cursorInfo.primaryCursorHeight,
2009 cursorInfo.lineHeight );
2010 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2012 // Sets the grab handle position.
2013 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2015 cursorInfo.lineOffset + offset.y,
2016 cursorInfo.lineHeight );
2018 if( cursorInfo.isSecondaryCursor )
2020 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2021 cursorInfo.secondaryPosition.x + offset.x,
2022 cursorInfo.secondaryPosition.y + offset.y,
2023 cursorInfo.secondaryCursorHeight,
2024 cursorInfo.lineHeight );
2025 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2028 // Set which cursors are active according the state.
2029 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2031 if( cursorInfo.isSecondaryCursor )
2033 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2037 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2042 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2045 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2048 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2049 const CursorInfo& cursorInfo )
2051 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2052 ( RIGHT_SELECTION_HANDLE != handleType ) )
2057 const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
2058 const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2060 // Sets the handle's position.
2061 mEventData->mDecorator->SetPosition( handleType,
2063 cursorInfo.lineOffset + offset.y,
2064 cursorInfo.lineHeight );
2066 // If selection handle at start of the text and other at end of the text then all text is selected.
2067 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2068 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2069 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2072 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2074 // Clamp between -space & 0 (and the text alignment).
2076 if( actualSize.width > mVisualModel->mControlSize.width )
2078 const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
2079 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
2080 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
2082 mEventData->mDecoratorUpdated = true;
2086 mEventData->mScrollPosition.x = 0.f;
2090 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2092 // Clamp between -space & 0 (and the text alignment).
2093 if( actualSize.height > mVisualModel->mControlSize.height )
2095 const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
2096 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
2097 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
2099 mEventData->mDecoratorUpdated = true;
2103 mEventData->mScrollPosition.y = 0.f;
2107 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2109 const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2111 // position is in actor's coords.
2112 const float positionEnd = position.x + cursorWidth;
2114 // Transform the position to decorator coords.
2115 const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
2116 const float offset = mEventData->mScrollPosition.x + alignment;
2117 const float decoratorPositionBegin = position.x + offset;
2118 const float decoratorPositionEnd = positionEnd + offset;
2120 if( decoratorPositionBegin < 0.f )
2122 mEventData->mScrollPosition.x = -position.x - alignment;
2124 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2126 mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
2130 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2132 // Get the current cursor position in decorator coords.
2133 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2135 // Calculate the offset to match the cursor position before the character was deleted.
2136 mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
2138 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2140 // Makes the new cursor position visible if needed.
2141 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2144 void Controller::Impl::RequestRelayout()
2146 mControlInterface.RequestTextRelayout();
2151 } // namespace Toolkit