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 if( mEventData->mScrollAfterUpdatePosition )
159 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
160 mEventData->mScrollAfterUpdatePosition = false;
162 else if( mEventData->mScrollAfterDelete )
164 ScrollTextToMatchCursor( cursorInfo );
165 mEventData->mScrollAfterDelete = false;
168 UpdateCursorPosition( cursorInfo );
170 mEventData->mDecoratorUpdated = true;
171 mEventData->mUpdateCursorPosition = false;
175 bool leftScroll = false;
176 bool rightScroll = false;
178 CursorInfo leftHandleInfo;
179 CursorInfo rightHandleInfo;
181 if( mEventData->mUpdateLeftSelectionPosition )
183 GetCursorPosition( mEventData->mLeftSelectionPosition,
186 if( mEventData->mScrollAfterUpdatePosition )
188 ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
193 if( mEventData->mUpdateRightSelectionPosition )
195 GetCursorPosition( mEventData->mRightSelectionPosition,
198 if( mEventData->mScrollAfterUpdatePosition )
200 ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
205 if( mEventData->mUpdateLeftSelectionPosition )
207 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
211 mEventData->mDecoratorUpdated = true;
214 if( mEventData->mUpdateRightSelectionPosition )
216 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
220 mEventData->mDecoratorUpdated = true;
223 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
225 RepositionSelectionHandles();
227 mEventData->mUpdateLeftSelectionPosition = false;
228 mEventData->mUpdateRightSelectionPosition = false;
231 if( leftScroll || rightScroll )
233 mEventData->mScrollAfterUpdatePosition = false;
237 if( mEventData->mUpdateInputStyle )
239 // Set the default style first.
240 RetrieveDefaultInputStyle( mEventData->mInputStyle );
242 // Get the character index from the cursor index.
243 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
245 // Retrieve the style from the style runs stored in the logical model.
246 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
248 mEventData->mUpdateInputStyle = false;
251 mEventData->mEventQueue.clear();
253 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
255 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
256 mEventData->mDecoratorUpdated = false;
258 return decoratorUpdated;
261 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
263 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
264 mTextUpdateInfo.mStartGlyphIndex = 0u;
265 mTextUpdateInfo.mStartLineIndex = 0u;
266 numberOfCharacters = 0u;
268 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
269 if( 0u == numberOfParagraphs )
271 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
272 numberOfCharacters = 0u;
274 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
276 // Nothing else to do if there are no paragraphs.
280 // Find the paragraphs to be updated.
281 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
282 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
284 // Text is being added at the end of the current text.
285 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
287 // Text is being added in a new paragraph after the last character of the text.
288 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
289 numberOfCharacters = 0u;
290 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
292 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
293 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
295 // Nothing else to do;
299 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
303 Length numberOfCharactersToUpdate = 0u;
304 if( mTextUpdateInfo.mFullRelayoutNeeded )
306 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
310 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
312 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
313 numberOfCharactersToUpdate,
314 paragraphsToBeUpdated );
317 if( 0u != paragraphsToBeUpdated.Count() )
319 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
320 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
321 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
323 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
324 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
326 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
327 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
328 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
329 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
331 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
332 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
334 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
338 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
342 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
343 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
346 void Controller::Impl::ClearFullModelData( OperationsMask operations )
348 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
350 mLogicalModel->mLineBreakInfo.Clear();
351 mLogicalModel->mParagraphInfo.Clear();
354 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
356 mLogicalModel->mLineBreakInfo.Clear();
359 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
361 mLogicalModel->mScriptRuns.Clear();
364 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
366 mLogicalModel->mFontRuns.Clear();
369 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
371 if( NO_OPERATION != ( BIDI_INFO & operations ) )
373 mLogicalModel->mBidirectionalParagraphInfo.Clear();
374 mLogicalModel->mCharacterDirections.Clear();
377 if( NO_OPERATION != ( REORDER & operations ) )
379 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
380 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
381 endIt = mLogicalModel->mBidirectionalLineInfo.End();
385 BidirectionalLineInfoRun& bidiLineInfo = *it;
387 free( bidiLineInfo.visualToLogicalMap );
388 bidiLineInfo.visualToLogicalMap = NULL;
390 mLogicalModel->mBidirectionalLineInfo.Clear();
394 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
396 mVisualModel->mGlyphs.Clear();
397 mVisualModel->mGlyphsToCharacters.Clear();
398 mVisualModel->mCharactersToGlyph.Clear();
399 mVisualModel->mCharactersPerGlyph.Clear();
400 mVisualModel->mGlyphsPerCharacter.Clear();
401 mVisualModel->mGlyphPositions.Clear();
404 if( NO_OPERATION != ( LAYOUT & operations ) )
406 mVisualModel->mLines.Clear();
409 if( NO_OPERATION != ( COLOR & operations ) )
411 mVisualModel->mColorIndices.Clear();
415 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
417 const CharacterIndex endIndexPlusOne = endIndex + 1u;
419 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
421 // Clear the line break info.
422 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
424 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
425 lineBreakInfoBuffer + endIndexPlusOne );
427 // Clear the paragraphs.
428 ClearCharacterRuns( startIndex,
430 mLogicalModel->mParagraphInfo );
433 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
435 // Clear the word break info.
436 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
438 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
439 wordBreakInfoBuffer + endIndexPlusOne );
442 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
444 // Clear the scripts.
445 ClearCharacterRuns( startIndex,
447 mLogicalModel->mScriptRuns );
450 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
453 ClearCharacterRuns( startIndex,
455 mLogicalModel->mFontRuns );
458 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
460 if( NO_OPERATION != ( BIDI_INFO & operations ) )
462 // Clear the bidirectional paragraph info.
463 ClearCharacterRuns( startIndex,
465 mLogicalModel->mBidirectionalParagraphInfo );
467 // Clear the character's directions.
468 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
470 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
471 characterDirectionsBuffer + endIndexPlusOne );
474 if( NO_OPERATION != ( REORDER & operations ) )
476 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
477 uint32_t endRemoveIndex = startRemoveIndex;
478 ClearCharacterRuns( startIndex,
480 mLogicalModel->mBidirectionalLineInfo,
484 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
486 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
487 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
488 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
492 BidirectionalLineInfoRun& bidiLineInfo = *it;
494 free( bidiLineInfo.visualToLogicalMap );
495 bidiLineInfo.visualToLogicalMap = NULL;
498 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
499 bidirectionalLineInfoBuffer + endRemoveIndex );
504 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
506 const CharacterIndex endIndexPlusOne = endIndex + 1u;
507 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
509 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
510 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
511 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
513 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
514 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
516 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
518 // Update the character to glyph indices.
519 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
520 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
524 CharacterIndex& index = *it;
525 index -= numberOfGlyphsRemoved;
528 // Clear the character to glyph conversion table.
529 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
530 charactersToGlyphBuffer + endIndexPlusOne );
532 // Clear the glyphs per character table.
533 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
534 glyphsPerCharacterBuffer + endIndexPlusOne );
536 // Clear the glyphs buffer.
537 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
538 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
539 glyphsBuffer + endGlyphIndexPlusOne );
541 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
543 // Update the glyph to character indices.
544 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
545 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
549 CharacterIndex& index = *it;
550 index -= numberOfCharactersRemoved;
553 // Clear the glyphs to characters buffer.
554 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
555 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
557 // Clear the characters per glyph buffer.
558 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
559 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
560 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
562 // Clear the positions buffer.
563 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
564 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
565 positionsBuffer + endGlyphIndexPlusOne );
568 if( NO_OPERATION != ( LAYOUT & operations ) )
571 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
572 uint32_t endRemoveIndex = startRemoveIndex;
573 ClearCharacterRuns( startIndex,
575 mVisualModel->mLines,
579 // Will update the glyph runs.
580 startRemoveIndex = mVisualModel->mLines.Count();
581 endRemoveIndex = startRemoveIndex;
582 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
583 endGlyphIndexPlusOne - 1u,
584 mVisualModel->mLines,
588 // Set the line index from where to insert the new laid-out lines.
589 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
591 LineRun* linesBuffer = mVisualModel->mLines.Begin();
592 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
593 linesBuffer + endRemoveIndex );
596 if( NO_OPERATION != ( COLOR & operations ) )
598 if( 0u != mVisualModel->mColorIndices.Count() )
600 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
601 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
602 colorIndexBuffer + endGlyphIndexPlusOne );
607 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
609 if( mTextUpdateInfo.mClearAll ||
610 ( ( 0u == startIndex ) &&
611 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
613 ClearFullModelData( operations );
617 // Clear the model data related with characters.
618 ClearCharacterModelData( startIndex, endIndex, operations );
620 // Clear the model data related with glyphs.
621 ClearGlyphModelData( startIndex, endIndex, operations );
624 // The estimated number of lines. Used to avoid reallocations when layouting.
625 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
627 mVisualModel->ClearCaches();
630 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
632 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
634 // Calculate the operations to be done.
635 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
637 if( NO_OPERATION == operations )
639 // Nothing to do if no operations are pending and required.
643 Vector<Character>& utf32Characters = mLogicalModel->mText;
645 const Length numberOfCharacters = utf32Characters.Count();
647 // Index to the first character of the first paragraph to be updated.
648 CharacterIndex startIndex = 0u;
649 // Number of characters of the paragraphs to be removed.
650 Length paragraphCharacters = 0u;
652 CalculateTextUpdateIndices( paragraphCharacters );
653 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
655 if( mTextUpdateInfo.mClearAll ||
656 ( 0u != paragraphCharacters ) )
658 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
661 mTextUpdateInfo.mClearAll = false;
663 // Whether the model is updated.
664 bool updated = false;
666 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
667 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
669 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
671 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
672 // calculate the bidirectional info for each 'paragraph'.
673 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
674 // is not shaped together).
675 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
677 SetLineBreakInfo( utf32Characters,
679 requestedNumberOfCharacters,
682 // Create the paragraph info.
683 mLogicalModel->CreateParagraphInfo( startIndex,
684 requestedNumberOfCharacters );
688 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
689 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
691 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
692 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
694 SetWordBreakInfo( utf32Characters,
696 requestedNumberOfCharacters,
701 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
702 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
704 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
705 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
707 if( getScripts || validateFonts )
709 // Validates the fonts assigned by the application or assigns default ones.
710 // It makes sure all the characters are going to be rendered by the correct font.
711 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
715 // Retrieves the scripts used in the text.
716 multilanguageSupport.SetScripts( utf32Characters,
718 requestedNumberOfCharacters,
724 // Validate the fonts set through the mark-up string.
725 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
727 // Get the default font id.
728 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
730 // Validates the fonts. If there is a character with no assigned font it sets a default one.
731 // After this call, fonts are validated.
732 multilanguageSupport.ValidateFonts( utf32Characters,
737 requestedNumberOfCharacters,
743 Vector<Character> mirroredUtf32Characters;
744 bool textMirrored = false;
745 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
746 if( NO_OPERATION != ( BIDI_INFO & operations ) )
748 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
749 bidirectionalInfo.Reserve( numberOfParagraphs );
751 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
752 SetBidirectionalInfo( utf32Characters,
756 requestedNumberOfCharacters,
759 if( 0u != bidirectionalInfo.Count() )
761 // Only set the character directions if there is right to left characters.
762 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
763 GetCharactersDirection( bidirectionalInfo,
766 requestedNumberOfCharacters,
769 // This paragraph has right to left text. Some characters may need to be mirrored.
770 // TODO: consider if the mirrored string can be stored as well.
772 textMirrored = GetMirroredText( utf32Characters,
776 requestedNumberOfCharacters,
777 mirroredUtf32Characters );
781 // There is no right to left characters. Clear the directions vector.
782 mLogicalModel->mCharacterDirections.Clear();
787 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
788 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
789 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
790 Vector<GlyphIndex> newParagraphGlyphs;
791 newParagraphGlyphs.Reserve( numberOfParagraphs );
793 const Length currentNumberOfGlyphs = glyphs.Count();
794 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
796 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
798 ShapeText( textToShape,
803 mTextUpdateInfo.mStartGlyphIndex,
804 requestedNumberOfCharacters,
806 glyphsToCharactersMap,
808 newParagraphGlyphs );
810 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
811 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
812 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
816 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
818 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
820 GlyphInfo* glyphsBuffer = glyphs.Begin();
821 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
823 // Update the width and advance of all new paragraph characters.
824 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
826 const GlyphIndex index = *it;
827 GlyphInfo& glyph = *( glyphsBuffer + index );
829 glyph.xBearing = 0.f;
836 if( NO_OPERATION != ( COLOR & operations ) )
838 // Set the color runs in glyphs.
839 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
840 mVisualModel->mCharactersToGlyph,
841 mVisualModel->mGlyphsPerCharacter,
843 mTextUpdateInfo.mStartGlyphIndex,
844 requestedNumberOfCharacters,
845 mVisualModel->mColors,
846 mVisualModel->mColorIndices );
851 if( ( NULL != mEventData ) &&
852 mEventData->mPreEditFlag &&
853 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
855 // Add the underline for the pre-edit text.
856 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
857 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
859 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
860 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
861 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
862 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
864 GlyphRun underlineRun;
865 underlineRun.glyphIndex = glyphStart;
866 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
868 // TODO: At the moment the underline runs are only for pre-edit.
869 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
872 // The estimated number of lines. Used to avoid reallocations when layouting.
873 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
875 // Set the previous number of characters for the next time the text is updated.
876 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
881 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
883 // Sets the default text's color.
884 inputStyle.textColor = mTextColor;
885 inputStyle.isDefaultColor = true;
887 inputStyle.familyName.clear();
888 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
889 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
890 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
891 inputStyle.size = 0.f;
893 inputStyle.familyDefined = false;
894 inputStyle.weightDefined = false;
895 inputStyle.widthDefined = false;
896 inputStyle.slantDefined = false;
897 inputStyle.sizeDefined = false;
899 // Sets the default font's family name, weight, width, slant and size.
902 if( mFontDefaults->familyDefined )
904 inputStyle.familyName = mFontDefaults->mFontDescription.family;
905 inputStyle.familyDefined = true;
908 if( mFontDefaults->weightDefined )
910 inputStyle.weight = mFontDefaults->mFontDescription.weight;
911 inputStyle.weightDefined = true;
914 if( mFontDefaults->widthDefined )
916 inputStyle.width = mFontDefaults->mFontDescription.width;
917 inputStyle.widthDefined = true;
920 if( mFontDefaults->slantDefined )
922 inputStyle.slant = mFontDefaults->mFontDescription.slant;
923 inputStyle.slantDefined = true;
926 if( mFontDefaults->sizeDefined )
928 inputStyle.size = mFontDefaults->mDefaultPointSize;
929 inputStyle.sizeDefined = true;
934 float Controller::Impl::GetDefaultFontLineHeight()
936 FontId defaultFontId = 0u;
937 if( NULL == mFontDefaults )
939 TextAbstraction::FontDescription fontDescription;
940 defaultFontId = mFontClient.GetFontId( fontDescription );
944 defaultFontId = mFontDefaults->GetFontId( mFontClient );
947 Text::FontMetrics fontMetrics;
948 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
950 return( fontMetrics.ascender - fontMetrics.descender );
953 void Controller::Impl::OnCursorKeyEvent( const Event& event )
955 if( NULL == mEventData )
957 // Nothing to do if there is no text input.
961 int keyCode = event.p1.mInt;
963 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
965 if( mEventData->mPrimaryCursorPosition > 0u )
967 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
970 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
972 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
974 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
977 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
981 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
986 mEventData->mUpdateCursorPosition = true;
987 mEventData->mUpdateInputStyle = true;
988 mEventData->mScrollAfterUpdatePosition = true;
991 void Controller::Impl::OnTapEvent( const Event& event )
993 if( NULL != mEventData )
995 const unsigned int tapCount = event.p1.mUint;
999 if( IsShowingRealText() )
1001 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1002 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1004 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1010 // When the cursor position is changing, delay cursor blinking
1011 mEventData->mDecorator->DelayCursorBlink();
1015 mEventData->mPrimaryCursorPosition = 0u;
1018 mEventData->mUpdateCursorPosition = true;
1019 mEventData->mScrollAfterUpdatePosition = true;
1020 mEventData->mUpdateInputStyle = true;
1022 // Notify the cursor position to the imf manager.
1023 if( mEventData->mImfManager )
1025 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1026 mEventData->mImfManager.NotifyCursorPosition();
1032 void Controller::Impl::OnPanEvent( const Event& event )
1034 if( NULL == mEventData )
1036 // Nothing to do if there is no text input.
1040 int state = event.p1.mInt;
1042 if( Gesture::Started == state ||
1043 Gesture::Continuing == state )
1045 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1046 const Vector2 currentScroll = mEventData->mScrollPosition;
1048 if( mEventData->mHorizontalScrollingEnabled )
1050 const float displacementX = event.p2.mFloat;
1051 mEventData->mScrollPosition.x += displacementX;
1053 ClampHorizontalScroll( actualSize );
1056 if( mEventData->mVerticalScrollingEnabled )
1058 const float displacementY = event.p3.mFloat;
1059 mEventData->mScrollPosition.y += displacementY;
1061 ClampVerticalScroll( actualSize );
1064 if( mEventData->mDecorator )
1066 mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
1071 void Controller::Impl::OnLongPressEvent( const Event& event )
1073 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1075 if( EventData::EDITING == mEventData->mState )
1077 ChangeState ( EventData::EDITING_WITH_POPUP );
1078 mEventData->mDecoratorUpdated = true;
1082 void Controller::Impl::OnHandleEvent( const Event& event )
1084 if( NULL == mEventData )
1086 // Nothing to do if there is no text input.
1090 const unsigned int state = event.p1.mUint;
1091 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1093 if( HANDLE_PRESSED == state )
1095 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1096 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1097 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1099 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1105 if( Event::GRAB_HANDLE_EVENT == event.type )
1107 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1109 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1111 mEventData->mPrimaryCursorPosition = handleNewPosition;
1112 mEventData->mUpdateCursorPosition = true;
1115 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1117 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1119 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1120 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1122 mEventData->mLeftSelectionPosition = handleNewPosition;
1124 mEventData->mUpdateLeftSelectionPosition = true;
1127 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1129 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1131 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1132 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1134 mEventData->mRightSelectionPosition = handleNewPosition;
1136 mEventData->mUpdateRightSelectionPosition = true;
1139 } // end ( HANDLE_PRESSED == state )
1140 else if( ( HANDLE_RELEASED == state ) ||
1141 handleStopScrolling )
1143 CharacterIndex handlePosition = 0u;
1144 if( handleStopScrolling )
1146 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1147 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1148 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1150 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1157 if( Event::GRAB_HANDLE_EVENT == event.type )
1159 mEventData->mUpdateCursorPosition = true;
1160 mEventData->mUpdateInputStyle = true;
1162 if( !IsClipboardEmpty() )
1164 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1167 if( handleStopScrolling )
1169 mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1170 mEventData->mPrimaryCursorPosition = handlePosition;
1173 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1175 ChangeState( EventData::SELECTING );
1177 if( handleStopScrolling )
1179 mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1180 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1182 if( mEventData->mUpdateLeftSelectionPosition )
1184 mEventData->mLeftSelectionPosition = handlePosition;
1188 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1190 ChangeState( EventData::SELECTING );
1192 if( handleStopScrolling )
1194 mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1195 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1196 if( mEventData->mUpdateRightSelectionPosition )
1198 mEventData->mRightSelectionPosition = handlePosition;
1203 mEventData->mDecoratorUpdated = true;
1204 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1205 else if( HANDLE_SCROLLING == state )
1207 const float xSpeed = event.p2.mFloat;
1208 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1209 const Vector2 currentScrollPosition = mEventData->mScrollPosition;
1211 mEventData->mScrollPosition.x += xSpeed;
1213 ClampHorizontalScroll( actualSize );
1215 bool endOfScroll = false;
1216 if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
1218 // Notify the decorator there is no more text to scroll.
1219 // The decorator won't send more scroll events.
1220 mEventData->mDecorator->NotifyEndOfScroll();
1221 // Still need to set the position of the handle.
1225 // Set the position of the handle.
1226 const bool scrollRightDirection = xSpeed > 0.f;
1227 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1228 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1230 if( Event::GRAB_HANDLE_EVENT == event.type )
1232 ChangeState( EventData::GRAB_HANDLE_PANNING );
1234 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1236 // Position the grag handle close to either the left or right edge.
1237 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1239 // Get the new handle position.
1240 // The grab handle's position is in decorator coords. Need to transforms to text coords.
1241 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1244 position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1245 position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
1247 mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1248 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1249 mEventData->mPrimaryCursorPosition = handlePosition;
1250 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1252 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1254 // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1255 // Think if something can be done to save power.
1257 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1259 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1261 // Position the selection handle close to either the left or right edge.
1262 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1264 // Get the new handle position.
1265 // The selection handle's position is in decorator coords. Need to transforms to text coords.
1266 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1269 position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
1270 position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
1272 if( leftSelectionHandleEvent )
1274 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1275 mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1276 if( differentHandles )
1278 mEventData->mLeftSelectionPosition = handlePosition;
1283 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1284 mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1285 if( differentHandles )
1287 mEventData->mRightSelectionPosition = handlePosition;
1291 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1293 RepositionSelectionHandles();
1295 mEventData->mScrollAfterUpdatePosition = true;
1298 mEventData->mDecoratorUpdated = true;
1299 } // end ( HANDLE_SCROLLING == state )
1302 void Controller::Impl::OnSelectEvent( const Event& event )
1304 if( NULL == mEventData )
1306 // Nothing to do if there is no text.
1310 if( mEventData->mSelectionEnabled )
1312 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
1313 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
1314 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
1316 // Calculates the logical position from the x,y coords.
1317 RepositionSelectionHandles( xPosition,
1320 mEventData->mUpdateLeftSelectionPosition = true;
1321 mEventData->mUpdateRightSelectionPosition = true;
1323 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1327 void Controller::Impl::OnSelectAllEvent()
1329 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1331 if( NULL == mEventData )
1333 // Nothing to do if there is no text.
1337 if( mEventData->mSelectionEnabled )
1339 mEventData->mLeftSelectionPosition = 0u;
1340 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1342 mEventData->mScrollAfterUpdatePosition = true;
1343 mEventData->mUpdateLeftSelectionPosition = true;
1344 mEventData->mUpdateRightSelectionPosition = true;
1348 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1350 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1352 // Nothing to select if handles are in the same place.
1353 selectedText.clear();
1357 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1359 //Get start and end position of selection
1360 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1361 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1363 Vector<Character>& utf32Characters = mLogicalModel->mText;
1364 const Length numberOfCharacters = utf32Characters.Count();
1366 // Validate the start and end selection points
1367 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1369 //Get text as a UTF8 string
1370 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1372 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1374 // Set as input style the style of the first deleted character.
1375 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1377 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1379 // Mark the paragraphs to be updated.
1380 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1381 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1383 // Delete text between handles
1384 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1385 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1386 utf32Characters.Erase( first, last );
1388 // Scroll after delete.
1389 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1390 mEventData->mScrollAfterDelete = true;
1392 // Udpade the cursor position and the decorator.
1393 // Scroll after the position is updated if is not scrolling after delete.
1394 mEventData->mUpdateCursorPosition = true;
1395 mEventData->mScrollAfterUpdatePosition = !mEventData->mScrollAfterDelete;
1396 mEventData->mDecoratorUpdated = true;
1400 void Controller::Impl::ShowClipboard()
1404 mClipboard.ShowClipboard();
1408 void Controller::Impl::HideClipboard()
1410 if( mClipboard && mClipboardHideEnabled )
1412 mClipboard.HideClipboard();
1416 void Controller::Impl::SetClipboardHideEnable(bool enable)
1418 mClipboardHideEnabled = enable;
1421 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1423 //Send string to clipboard
1424 return ( mClipboard && mClipboard.SetItem( source ) );
1427 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1429 std::string selectedText;
1430 RetrieveSelection( selectedText, deleteAfterSending );
1431 CopyStringToClipboard( selectedText );
1432 ChangeState( EventData::EDITING );
1435 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1439 retrievedString = mClipboard.GetItem( itemIndex );
1443 void Controller::Impl::RepositionSelectionHandles()
1445 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1446 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1448 if( selectionStart == selectionEnd )
1450 // Nothing to select if handles are in the same place.
1454 mEventData->mDecorator->ClearHighlights();
1456 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1457 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1458 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1459 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1460 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1461 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1462 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1464 // TODO: Better algorithm to create the highlight box.
1465 // TODO: Multi-line.
1467 // Get the height of the line.
1468 const Vector<LineRun>& lines = mVisualModel->mLines;
1469 const LineRun& firstLine = *lines.Begin();
1470 const float height = firstLine.ascender + -firstLine.descender;
1472 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1473 const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1474 const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1476 // Swap the indices if the start is greater than the end.
1477 const bool indicesSwapped = selectionStart > selectionEnd;
1479 // Tell the decorator to flip the selection handles if needed.
1480 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1482 if( indicesSwapped )
1484 std::swap( selectionStart, selectionEnd );
1487 // Get the indices to the first and last selected glyphs.
1488 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1489 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1490 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1491 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1493 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1494 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1495 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1497 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1498 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1499 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1501 const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1503 // Traverse the glyphs.
1504 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1506 const GlyphInfo& glyph = *( glyphsBuffer + index );
1507 const Vector2& position = *( positionsBuffer + index );
1509 if( splitStartGlyph )
1511 // 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.
1513 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1514 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1515 // Get the direction of the character.
1516 CharacterDirection isCurrentRightToLeft = false;
1517 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1519 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1522 // The end point could be in the middle of the ligature.
1523 // Calculate the number of characters selected.
1524 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1526 const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1528 mEventData->mDecorator->AddHighlight( xPosition,
1530 xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
1531 offset.y + height );
1533 splitStartGlyph = false;
1537 if( splitEndGlyph && ( index == glyphEnd ) )
1539 // 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.
1541 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1542 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1543 // Get the direction of the character.
1544 CharacterDirection isCurrentRightToLeft = false;
1545 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1547 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1550 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1552 const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1553 mEventData->mDecorator->AddHighlight( xPosition,
1555 xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
1556 offset.y + height );
1558 splitEndGlyph = false;
1562 const float xPosition = position.x - glyph.xBearing + offset.x;
1563 mEventData->mDecorator->AddHighlight( xPosition,
1565 xPosition + glyph.advance,
1566 offset.y + height );
1569 CursorInfo primaryCursorInfo;
1570 GetCursorPosition( mEventData->mLeftSelectionPosition,
1571 primaryCursorInfo );
1573 CursorInfo secondaryCursorInfo;
1574 GetCursorPosition( mEventData->mRightSelectionPosition,
1575 secondaryCursorInfo );
1577 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
1578 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
1580 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1582 primaryCursorInfo.lineOffset + offset.y,
1583 primaryCursorInfo.lineHeight );
1585 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1586 secondaryPosition.x,
1587 secondaryCursorInfo.lineOffset + offset.y,
1588 secondaryCursorInfo.lineHeight );
1590 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1591 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1593 // Set the flag to update the decorator.
1594 mEventData->mDecoratorUpdated = true;
1597 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1599 if( NULL == mEventData )
1601 // Nothing to do if there is no text input.
1605 if( IsShowingPlaceholderText() )
1607 // Nothing to do if there is the place-holder text.
1611 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1612 const Length numberOfLines = mVisualModel->mLines.Count();
1613 if( ( 0 == numberOfGlyphs ) ||
1614 ( 0 == numberOfLines ) )
1616 // Nothing to do if there is no text.
1620 // Find which word was selected
1621 CharacterIndex selectionStart( 0 );
1622 CharacterIndex selectionEnd( 0 );
1623 FindSelectionIndices( mVisualModel,
1630 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1632 if( selectionStart == selectionEnd )
1634 ChangeState( EventData::EDITING );
1635 // Nothing to select. i.e. a white space, out of bounds
1639 mEventData->mLeftSelectionPosition = selectionStart;
1640 mEventData->mRightSelectionPosition = selectionEnd;
1643 void Controller::Impl::SetPopupButtons()
1646 * Sets the Popup buttons to be shown depending on State.
1648 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1650 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1653 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1655 if( EventData::SELECTING == mEventData->mState )
1657 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1659 if( !IsClipboardEmpty() )
1661 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1662 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1665 if( !mEventData->mAllTextSelected )
1667 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1670 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1672 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1674 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1677 if( !IsClipboardEmpty() )
1679 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1680 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1683 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1685 if ( !IsClipboardEmpty() )
1687 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1688 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1692 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1695 void Controller::Impl::ChangeState( EventData::State newState )
1697 if( NULL == mEventData )
1699 // Nothing to do if there is no text input.
1703 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1705 if( mEventData->mState != newState )
1707 mEventData->mState = newState;
1709 if( EventData::INACTIVE == mEventData->mState )
1711 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1712 mEventData->mDecorator->StopCursorBlink();
1713 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1714 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1715 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1716 mEventData->mDecorator->SetPopupActive( false );
1717 mEventData->mDecoratorUpdated = true;
1720 else if( EventData::INTERRUPTED == mEventData->mState)
1722 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1723 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1724 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1725 mEventData->mDecorator->SetPopupActive( false );
1726 mEventData->mDecoratorUpdated = true;
1729 else if( EventData::SELECTING == mEventData->mState )
1731 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1732 mEventData->mDecorator->StopCursorBlink();
1733 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1734 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1735 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1736 if( mEventData->mGrabHandlePopupEnabled )
1739 mEventData->mDecorator->SetPopupActive( true );
1741 mEventData->mDecoratorUpdated = true;
1743 else if( EventData::EDITING == mEventData->mState )
1745 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1746 if( mEventData->mCursorBlinkEnabled )
1748 mEventData->mDecorator->StartCursorBlink();
1750 // Grab handle is not shown until a tap is received whilst EDITING
1751 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1752 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1753 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1754 if( mEventData->mGrabHandlePopupEnabled )
1756 mEventData->mDecorator->SetPopupActive( false );
1758 mEventData->mDecoratorUpdated = true;
1761 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1763 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1765 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1766 if( mEventData->mCursorBlinkEnabled )
1768 mEventData->mDecorator->StartCursorBlink();
1770 if( mEventData->mSelectionEnabled )
1772 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1773 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1777 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1779 if( mEventData->mGrabHandlePopupEnabled )
1782 mEventData->mDecorator->SetPopupActive( true );
1785 mEventData->mDecoratorUpdated = true;
1787 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1789 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1791 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1792 if( mEventData->mCursorBlinkEnabled )
1794 mEventData->mDecorator->StartCursorBlink();
1796 // Grab handle is not shown until a tap is received whilst EDITING
1797 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1798 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1799 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1800 if( mEventData->mGrabHandlePopupEnabled )
1802 mEventData->mDecorator->SetPopupActive( false );
1804 mEventData->mDecoratorUpdated = true;
1807 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1809 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1810 mEventData->mDecorator->StopCursorBlink();
1811 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1812 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1813 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1814 if( mEventData->mGrabHandlePopupEnabled )
1816 mEventData->mDecorator->SetPopupActive( false );
1818 mEventData->mDecoratorUpdated = true;
1820 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1822 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1824 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1825 if( mEventData->mCursorBlinkEnabled )
1827 mEventData->mDecorator->StartCursorBlink();
1829 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1830 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1831 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1832 if( mEventData->mGrabHandlePopupEnabled )
1834 mEventData->mDecorator->SetPopupActive( false );
1836 mEventData->mDecoratorUpdated = true;
1838 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1840 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
1842 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1843 if( mEventData->mCursorBlinkEnabled )
1845 mEventData->mDecorator->StartCursorBlink();
1848 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1849 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1850 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1852 if( mEventData->mGrabHandlePopupEnabled )
1855 mEventData->mDecorator->SetPopupActive( true );
1858 mEventData->mDecoratorUpdated = true;
1863 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1864 CursorInfo& cursorInfo )
1866 if( !IsShowingRealText() )
1868 // Do not want to use the place-holder text to set the cursor position.
1870 // Use the line's height of the font's family set to set the cursor's size.
1871 // If there is no font's family set, use the default font.
1872 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1874 cursorInfo.lineOffset = 0.f;
1875 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1876 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1878 switch( mLayoutEngine.GetHorizontalAlignment() )
1880 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1882 cursorInfo.primaryPosition.x = 0.f;
1885 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1887 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
1890 case LayoutEngine::HORIZONTAL_ALIGN_END:
1892 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1897 switch( mLayoutEngine.GetVerticalAlignment() )
1899 case LayoutEngine::VERTICAL_ALIGN_TOP:
1901 cursorInfo.primaryPosition.y = 0.f;
1904 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1906 cursorInfo.primaryPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - cursorInfo.lineHeight ) );
1909 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1911 cursorInfo.primaryPosition.y = mVisualModel->mControlSize.height - cursorInfo.lineHeight;
1916 // Nothing else to do.
1920 Text::GetCursorPosition( mVisualModel,
1926 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
1928 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1930 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1931 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1933 if( 0.f > cursorInfo.primaryPosition.x )
1935 cursorInfo.primaryPosition.x = 0.f;
1938 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
1939 if( cursorInfo.primaryPosition.x > edgeWidth )
1941 cursorInfo.primaryPosition.x = edgeWidth;
1946 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1948 if( NULL == mEventData )
1950 // Nothing to do if there is no text input.
1954 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1956 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1957 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1959 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1960 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1962 if( numberOfCharacters > 1u )
1964 const Script script = mLogicalModel->GetScript( index );
1965 if( HasLigatureMustBreak( script ) )
1967 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
1968 numberOfCharacters = 1u;
1973 while( 0u == numberOfCharacters )
1976 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1980 if( index < mEventData->mPrimaryCursorPosition )
1982 cursorIndex -= numberOfCharacters;
1986 cursorIndex += numberOfCharacters;
1992 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
1994 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1995 if( NULL == mEventData )
1997 // Nothing to do if there is no text input.
1998 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2002 const Vector2 offset = mEventData->mScrollPosition + ( IsShowingRealText() ? mAlignmentOffset : Vector2::ZERO );
2003 const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2005 // Sets the cursor position.
2006 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2009 cursorInfo.primaryCursorHeight,
2010 cursorInfo.lineHeight );
2011 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2013 // Sets the grab handle position.
2014 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2016 cursorInfo.lineOffset + offset.y,
2017 cursorInfo.lineHeight );
2019 if( cursorInfo.isSecondaryCursor )
2021 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2022 cursorInfo.secondaryPosition.x + offset.x,
2023 cursorInfo.secondaryPosition.y + offset.y,
2024 cursorInfo.secondaryCursorHeight,
2025 cursorInfo.lineHeight );
2026 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
2029 // Set which cursors are active according the state.
2030 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2032 if( cursorInfo.isSecondaryCursor )
2034 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2038 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2043 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2046 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2049 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2050 const CursorInfo& cursorInfo )
2052 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2053 ( RIGHT_SELECTION_HANDLE != handleType ) )
2058 const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
2059 const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
2061 // Sets the handle's position.
2062 mEventData->mDecorator->SetPosition( handleType,
2064 cursorInfo.lineOffset + offset.y,
2065 cursorInfo.lineHeight );
2067 // If selection handle at start of the text and other at end of the text then all text is selected.
2068 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2069 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2070 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2073 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2075 // Clamp between -space & 0 (and the text alignment).
2077 if( actualSize.width > mVisualModel->mControlSize.width )
2079 const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
2080 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
2081 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
2083 mEventData->mDecoratorUpdated = true;
2087 mEventData->mScrollPosition.x = 0.f;
2091 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2093 // Clamp between -space & 0 (and the text alignment).
2094 if( actualSize.height > mVisualModel->mControlSize.height )
2096 const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
2097 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
2098 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
2100 mEventData->mDecoratorUpdated = true;
2104 mEventData->mScrollPosition.y = 0.f;
2108 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2110 // position is in actor's coords.
2111 const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
2113 // Transform the position to decorator coords.
2114 const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
2115 const float offset = mEventData->mScrollPosition.x + alignment;
2116 const float decoratorPositionBegin = position.x + offset;
2117 const float decoratorPositionEnd = positionEnd + offset;
2119 if( decoratorPositionBegin < 0.f )
2121 mEventData->mScrollPosition.x = -position.x - alignment;
2123 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2125 mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
2129 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2131 // Get the current cursor position in decorator coords.
2132 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2134 // Calculate the offset to match the cursor position before the character was deleted.
2135 mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
2137 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2140 void Controller::Impl::RequestRelayout()
2142 mControlInterface.RequestTextRelayout();
2147 } // namespace Toolkit