2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-run-container.h>
40 * @brief Struct used to calculate the selection box.
42 struct SelectionBoxInfo
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
54 const float MAX_FLOAT = std::numeric_limits<float>::max();
55 const float MIN_FLOAT = std::numeric_limits<float>::min();
56 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
69 EventData::EventData( DecoratorPtr decorator )
70 : mDecorator( decorator ),
72 mPlaceholderTextActive(),
73 mPlaceholderTextInactive(),
74 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77 mPrimaryCursorPosition( 0u ),
78 mLeftSelectionPosition( 0u ),
79 mRightSelectionPosition( 0u ),
80 mPreEditStartPosition( 0u ),
82 mIsShowingPlaceholderText( false ),
83 mPreEditFlag( false ),
84 mDecoratorUpdated( false ),
85 mCursorBlinkEnabled( true ),
86 mGrabHandleEnabled( true ),
87 mGrabHandlePopupEnabled( true ),
88 mSelectionEnabled( true ),
89 mHorizontalScrollingEnabled( true ),
90 mVerticalScrollingEnabled( false ),
91 mUpdateCursorPosition( false ),
92 mUpdateLeftSelectionPosition( false ),
93 mUpdateRightSelectionPosition( false ),
94 mScrollAfterUpdatePosition( false ),
95 mScrollAfterDelete( false ),
96 mAllTextSelected( false ),
97 mUpdateInputStyle( false )
99 mImfManager = ImfManager::Get();
102 EventData::~EventData()
105 bool Controller::Impl::ProcessInputEvents()
107 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
108 if( NULL == mEventData )
110 // Nothing to do if there is no text input.
111 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
115 if( mEventData->mDecorator )
117 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
118 iter != mEventData->mEventQueue.end();
123 case Event::CURSOR_KEY_EVENT:
125 OnCursorKeyEvent( *iter );
128 case Event::TAP_EVENT:
133 case Event::LONG_PRESS_EVENT:
135 OnLongPressEvent( *iter );
138 case Event::PAN_EVENT:
143 case Event::GRAB_HANDLE_EVENT:
144 case Event::LEFT_SELECTION_HANDLE_EVENT:
145 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
147 OnHandleEvent( *iter );
152 OnSelectEvent( *iter );
155 case Event::SELECT_ALL:
164 // The cursor must also be repositioned after inserts into the model
165 if( mEventData->mUpdateCursorPosition )
167 // Updates the cursor position and scrolls the text to make it visible.
168 CursorInfo cursorInfo;
169 GetCursorPosition( mEventData->mPrimaryCursorPosition,
172 // Scroll first the text after delete ...
173 if( mEventData->mScrollAfterDelete )
175 ScrollTextToMatchCursor( cursorInfo );
178 // ... then, text can be scrolled to make the cursor visible.
179 if( mEventData->mScrollAfterUpdatePosition )
181 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
183 mEventData->mScrollAfterUpdatePosition = false;
184 mEventData->mScrollAfterDelete = false;
186 UpdateCursorPosition( cursorInfo );
188 mEventData->mDecoratorUpdated = true;
189 mEventData->mUpdateCursorPosition = false;
193 bool leftScroll = false;
194 bool rightScroll = false;
196 CursorInfo leftHandleInfo;
197 CursorInfo rightHandleInfo;
199 if( mEventData->mUpdateLeftSelectionPosition )
201 GetCursorPosition( mEventData->mLeftSelectionPosition,
204 if( mEventData->mScrollAfterUpdatePosition )
206 ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
211 if( mEventData->mUpdateRightSelectionPosition )
213 GetCursorPosition( mEventData->mRightSelectionPosition,
216 if( mEventData->mScrollAfterUpdatePosition )
218 ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
223 if( mEventData->mUpdateLeftSelectionPosition )
225 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
229 mEventData->mDecoratorUpdated = true;
232 if( mEventData->mUpdateRightSelectionPosition )
234 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
238 mEventData->mDecoratorUpdated = true;
241 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
243 RepositionSelectionHandles();
245 mEventData->mUpdateLeftSelectionPosition = false;
246 mEventData->mUpdateRightSelectionPosition = false;
249 if( leftScroll || rightScroll )
251 mEventData->mScrollAfterUpdatePosition = false;
255 if( mEventData->mUpdateInputStyle )
257 // Set the default style first.
258 RetrieveDefaultInputStyle( mEventData->mInputStyle );
260 // Get the character index from the cursor index.
261 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
263 // Retrieve the style from the style runs stored in the logical model.
264 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
266 mEventData->mUpdateInputStyle = false;
269 mEventData->mEventQueue.clear();
271 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
273 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
274 mEventData->mDecoratorUpdated = false;
276 return decoratorUpdated;
279 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
281 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
282 mTextUpdateInfo.mStartGlyphIndex = 0u;
283 mTextUpdateInfo.mStartLineIndex = 0u;
284 numberOfCharacters = 0u;
286 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
287 if( 0u == numberOfParagraphs )
289 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
290 numberOfCharacters = 0u;
292 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
294 // Nothing else to do if there are no paragraphs.
298 // Find the paragraphs to be updated.
299 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
300 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
302 // Text is being added at the end of the current text.
303 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
305 // Text is being added in a new paragraph after the last character of the text.
306 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
307 numberOfCharacters = 0u;
308 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
310 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
311 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
313 // Nothing else to do;
317 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
321 Length numberOfCharactersToUpdate = 0u;
322 if( mTextUpdateInfo.mFullRelayoutNeeded )
324 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
328 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
330 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
331 numberOfCharactersToUpdate,
332 paragraphsToBeUpdated );
335 if( 0u != paragraphsToBeUpdated.Count() )
337 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
338 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
339 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
341 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
342 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
344 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
345 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
346 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
347 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
349 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
350 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
352 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
356 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
360 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
361 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
364 void Controller::Impl::ClearFullModelData( OperationsMask operations )
366 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
368 mLogicalModel->mLineBreakInfo.Clear();
369 mLogicalModel->mParagraphInfo.Clear();
372 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
374 mLogicalModel->mLineBreakInfo.Clear();
377 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
379 mLogicalModel->mScriptRuns.Clear();
382 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
384 mLogicalModel->mFontRuns.Clear();
387 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
389 if( NO_OPERATION != ( BIDI_INFO & operations ) )
391 mLogicalModel->mBidirectionalParagraphInfo.Clear();
392 mLogicalModel->mCharacterDirections.Clear();
395 if( NO_OPERATION != ( REORDER & operations ) )
397 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
398 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
399 endIt = mLogicalModel->mBidirectionalLineInfo.End();
403 BidirectionalLineInfoRun& bidiLineInfo = *it;
405 free( bidiLineInfo.visualToLogicalMap );
406 bidiLineInfo.visualToLogicalMap = NULL;
408 mLogicalModel->mBidirectionalLineInfo.Clear();
412 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
414 mVisualModel->mGlyphs.Clear();
415 mVisualModel->mGlyphsToCharacters.Clear();
416 mVisualModel->mCharactersToGlyph.Clear();
417 mVisualModel->mCharactersPerGlyph.Clear();
418 mVisualModel->mGlyphsPerCharacter.Clear();
419 mVisualModel->mGlyphPositions.Clear();
422 if( NO_OPERATION != ( LAYOUT & operations ) )
424 mVisualModel->mLines.Clear();
427 if( NO_OPERATION != ( COLOR & operations ) )
429 mVisualModel->mColorIndices.Clear();
433 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
435 const CharacterIndex endIndexPlusOne = endIndex + 1u;
437 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
439 // Clear the line break info.
440 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
442 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
443 lineBreakInfoBuffer + endIndexPlusOne );
445 // Clear the paragraphs.
446 ClearCharacterRuns( startIndex,
448 mLogicalModel->mParagraphInfo );
451 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
453 // Clear the word break info.
454 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
456 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
457 wordBreakInfoBuffer + endIndexPlusOne );
460 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
462 // Clear the scripts.
463 ClearCharacterRuns( startIndex,
465 mLogicalModel->mScriptRuns );
468 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
471 ClearCharacterRuns( startIndex,
473 mLogicalModel->mFontRuns );
476 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
478 if( NO_OPERATION != ( BIDI_INFO & operations ) )
480 // Clear the bidirectional paragraph info.
481 ClearCharacterRuns( startIndex,
483 mLogicalModel->mBidirectionalParagraphInfo );
485 // Clear the character's directions.
486 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
488 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
489 characterDirectionsBuffer + endIndexPlusOne );
492 if( NO_OPERATION != ( REORDER & operations ) )
494 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
495 uint32_t endRemoveIndex = startRemoveIndex;
496 ClearCharacterRuns( startIndex,
498 mLogicalModel->mBidirectionalLineInfo,
502 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
504 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
505 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
506 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
510 BidirectionalLineInfoRun& bidiLineInfo = *it;
512 free( bidiLineInfo.visualToLogicalMap );
513 bidiLineInfo.visualToLogicalMap = NULL;
516 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
517 bidirectionalLineInfoBuffer + endRemoveIndex );
522 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
524 const CharacterIndex endIndexPlusOne = endIndex + 1u;
525 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
527 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
528 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
529 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
531 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
532 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
534 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
536 // Update the character to glyph indices.
537 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
538 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
542 CharacterIndex& index = *it;
543 index -= numberOfGlyphsRemoved;
546 // Clear the character to glyph conversion table.
547 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
548 charactersToGlyphBuffer + endIndexPlusOne );
550 // Clear the glyphs per character table.
551 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
552 glyphsPerCharacterBuffer + endIndexPlusOne );
554 // Clear the glyphs buffer.
555 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
556 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
557 glyphsBuffer + endGlyphIndexPlusOne );
559 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
561 // Update the glyph to character indices.
562 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
563 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
567 CharacterIndex& index = *it;
568 index -= numberOfCharactersRemoved;
571 // Clear the glyphs to characters buffer.
572 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
573 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
575 // Clear the characters per glyph buffer.
576 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
577 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
578 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
580 // Clear the positions buffer.
581 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
582 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
583 positionsBuffer + endGlyphIndexPlusOne );
586 if( NO_OPERATION != ( LAYOUT & operations ) )
589 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
590 uint32_t endRemoveIndex = startRemoveIndex;
591 ClearCharacterRuns( startIndex,
593 mVisualModel->mLines,
597 // Will update the glyph runs.
598 startRemoveIndex = mVisualModel->mLines.Count();
599 endRemoveIndex = startRemoveIndex;
600 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
601 endGlyphIndexPlusOne - 1u,
602 mVisualModel->mLines,
606 // Set the line index from where to insert the new laid-out lines.
607 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
609 LineRun* linesBuffer = mVisualModel->mLines.Begin();
610 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
611 linesBuffer + endRemoveIndex );
614 if( NO_OPERATION != ( COLOR & operations ) )
616 if( 0u != mVisualModel->mColorIndices.Count() )
618 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
619 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
620 colorIndexBuffer + endGlyphIndexPlusOne );
625 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
627 if( mTextUpdateInfo.mClearAll ||
628 ( ( 0u == startIndex ) &&
629 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
631 ClearFullModelData( operations );
635 // Clear the model data related with characters.
636 ClearCharacterModelData( startIndex, endIndex, operations );
638 // Clear the model data related with glyphs.
639 ClearGlyphModelData( startIndex, endIndex, operations );
642 // The estimated number of lines. Used to avoid reallocations when layouting.
643 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
645 mVisualModel->ClearCaches();
648 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
650 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
652 // Calculate the operations to be done.
653 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
655 if( NO_OPERATION == operations )
657 // Nothing to do if no operations are pending and required.
661 Vector<Character>& utf32Characters = mLogicalModel->mText;
663 const Length numberOfCharacters = utf32Characters.Count();
665 // Index to the first character of the first paragraph to be updated.
666 CharacterIndex startIndex = 0u;
667 // Number of characters of the paragraphs to be removed.
668 Length paragraphCharacters = 0u;
670 CalculateTextUpdateIndices( paragraphCharacters );
671 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
673 if( mTextUpdateInfo.mClearAll ||
674 ( 0u != paragraphCharacters ) )
676 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
679 mTextUpdateInfo.mClearAll = false;
681 // Whether the model is updated.
682 bool updated = false;
684 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
685 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
687 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
689 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
690 // calculate the bidirectional info for each 'paragraph'.
691 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
692 // is not shaped together).
693 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
695 SetLineBreakInfo( utf32Characters,
697 requestedNumberOfCharacters,
700 // Create the paragraph info.
701 mLogicalModel->CreateParagraphInfo( startIndex,
702 requestedNumberOfCharacters );
706 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
707 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
709 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
710 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
712 SetWordBreakInfo( utf32Characters,
714 requestedNumberOfCharacters,
719 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
720 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
722 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
723 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
725 if( getScripts || validateFonts )
727 // Validates the fonts assigned by the application or assigns default ones.
728 // It makes sure all the characters are going to be rendered by the correct font.
729 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
733 // Retrieves the scripts used in the text.
734 multilanguageSupport.SetScripts( utf32Characters,
736 requestedNumberOfCharacters,
742 // Validate the fonts set through the mark-up string.
743 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
745 // Get the default font id.
746 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
748 // Validates the fonts. If there is a character with no assigned font it sets a default one.
749 // After this call, fonts are validated.
750 multilanguageSupport.ValidateFonts( utf32Characters,
755 requestedNumberOfCharacters,
761 Vector<Character> mirroredUtf32Characters;
762 bool textMirrored = false;
763 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
764 if( NO_OPERATION != ( BIDI_INFO & operations ) )
766 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
767 bidirectionalInfo.Reserve( numberOfParagraphs );
769 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
770 SetBidirectionalInfo( utf32Characters,
774 requestedNumberOfCharacters,
777 if( 0u != bidirectionalInfo.Count() )
779 // Only set the character directions if there is right to left characters.
780 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
781 GetCharactersDirection( bidirectionalInfo,
784 requestedNumberOfCharacters,
787 // This paragraph has right to left text. Some characters may need to be mirrored.
788 // TODO: consider if the mirrored string can be stored as well.
790 textMirrored = GetMirroredText( utf32Characters,
794 requestedNumberOfCharacters,
795 mirroredUtf32Characters );
799 // There is no right to left characters. Clear the directions vector.
800 mLogicalModel->mCharacterDirections.Clear();
805 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
806 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
807 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
808 Vector<GlyphIndex> newParagraphGlyphs;
809 newParagraphGlyphs.Reserve( numberOfParagraphs );
811 const Length currentNumberOfGlyphs = glyphs.Count();
812 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
814 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
816 ShapeText( textToShape,
821 mTextUpdateInfo.mStartGlyphIndex,
822 requestedNumberOfCharacters,
824 glyphsToCharactersMap,
826 newParagraphGlyphs );
828 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
829 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
830 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
834 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
836 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
838 GlyphInfo* glyphsBuffer = glyphs.Begin();
839 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
841 // Update the width and advance of all new paragraph characters.
842 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
844 const GlyphIndex index = *it;
845 GlyphInfo& glyph = *( glyphsBuffer + index );
847 glyph.xBearing = 0.f;
854 if( NO_OPERATION != ( COLOR & operations ) )
856 // Set the color runs in glyphs.
857 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
858 mVisualModel->mCharactersToGlyph,
859 mVisualModel->mGlyphsPerCharacter,
861 mTextUpdateInfo.mStartGlyphIndex,
862 requestedNumberOfCharacters,
863 mVisualModel->mColors,
864 mVisualModel->mColorIndices );
869 if( ( NULL != mEventData ) &&
870 mEventData->mPreEditFlag &&
871 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
873 // Add the underline for the pre-edit text.
874 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
875 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
877 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
878 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
879 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
880 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
882 GlyphRun underlineRun;
883 underlineRun.glyphIndex = glyphStart;
884 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
886 // TODO: At the moment the underline runs are only for pre-edit.
887 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
890 // The estimated number of lines. Used to avoid reallocations when layouting.
891 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
893 // Set the previous number of characters for the next time the text is updated.
894 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
899 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
901 // Sets the default text's color.
902 inputStyle.textColor = mTextColor;
903 inputStyle.isDefaultColor = true;
905 inputStyle.familyName.clear();
906 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
907 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
908 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
909 inputStyle.size = 0.f;
911 inputStyle.familyDefined = false;
912 inputStyle.weightDefined = false;
913 inputStyle.widthDefined = false;
914 inputStyle.slantDefined = false;
915 inputStyle.sizeDefined = false;
917 // Sets the default font's family name, weight, width, slant and size.
920 if( mFontDefaults->familyDefined )
922 inputStyle.familyName = mFontDefaults->mFontDescription.family;
923 inputStyle.familyDefined = true;
926 if( mFontDefaults->weightDefined )
928 inputStyle.weight = mFontDefaults->mFontDescription.weight;
929 inputStyle.weightDefined = true;
932 if( mFontDefaults->widthDefined )
934 inputStyle.width = mFontDefaults->mFontDescription.width;
935 inputStyle.widthDefined = true;
938 if( mFontDefaults->slantDefined )
940 inputStyle.slant = mFontDefaults->mFontDescription.slant;
941 inputStyle.slantDefined = true;
944 if( mFontDefaults->sizeDefined )
946 inputStyle.size = mFontDefaults->mDefaultPointSize;
947 inputStyle.sizeDefined = true;
952 float Controller::Impl::GetDefaultFontLineHeight()
954 FontId defaultFontId = 0u;
955 if( NULL == mFontDefaults )
957 TextAbstraction::FontDescription fontDescription;
958 defaultFontId = mFontClient.GetFontId( fontDescription );
962 defaultFontId = mFontDefaults->GetFontId( mFontClient );
965 Text::FontMetrics fontMetrics;
966 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
968 return( fontMetrics.ascender - fontMetrics.descender );
971 void Controller::Impl::OnCursorKeyEvent( const Event& event )
973 if( NULL == mEventData )
975 // Nothing to do if there is no text input.
979 int keyCode = event.p1.mInt;
981 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
983 if( mEventData->mPrimaryCursorPosition > 0u )
985 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
988 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
990 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
992 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
995 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
999 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1004 mEventData->mUpdateCursorPosition = true;
1005 mEventData->mUpdateInputStyle = true;
1006 mEventData->mScrollAfterUpdatePosition = true;
1009 void Controller::Impl::OnTapEvent( const Event& event )
1011 if( NULL != mEventData )
1013 const unsigned int tapCount = event.p1.mUint;
1015 if( 1u == tapCount )
1017 if( IsShowingRealText() )
1019 // Convert from control's coords to text's coords.
1020 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1021 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1023 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1029 // When the cursor position is changing, delay cursor blinking
1030 mEventData->mDecorator->DelayCursorBlink();
1034 mEventData->mPrimaryCursorPosition = 0u;
1037 mEventData->mUpdateCursorPosition = true;
1038 mEventData->mScrollAfterUpdatePosition = true;
1039 mEventData->mUpdateInputStyle = true;
1041 // Notify the cursor position to the imf manager.
1042 if( mEventData->mImfManager )
1044 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1045 mEventData->mImfManager.NotifyCursorPosition();
1051 void Controller::Impl::OnPanEvent( const Event& event )
1053 if( NULL == mEventData )
1055 // Nothing to do if there is no text input.
1059 int state = event.p1.mInt;
1061 if( ( Gesture::Started == state ) ||
1062 ( Gesture::Continuing == state ) )
1064 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1065 const Vector2 currentScroll = mScrollPosition;
1067 if( mEventData->mHorizontalScrollingEnabled )
1069 const float displacementX = event.p2.mFloat;
1070 mScrollPosition.x += displacementX;
1072 ClampHorizontalScroll( actualSize );
1075 if( mEventData->mVerticalScrollingEnabled )
1077 const float displacementY = event.p3.mFloat;
1078 mScrollPosition.y += displacementY;
1080 ClampVerticalScroll( actualSize );
1083 if( mEventData->mDecorator )
1085 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1090 void Controller::Impl::OnLongPressEvent( const Event& event )
1092 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1094 if( EventData::EDITING == mEventData->mState )
1096 ChangeState ( EventData::EDITING_WITH_POPUP );
1097 mEventData->mDecoratorUpdated = true;
1101 void Controller::Impl::OnHandleEvent( const Event& event )
1103 if( NULL == mEventData )
1105 // Nothing to do if there is no text input.
1109 const unsigned int state = event.p1.mUint;
1110 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1112 if( HANDLE_PRESSED == state )
1114 // Convert from decorator's coords to text's coords.
1115 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1116 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1118 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1124 if( Event::GRAB_HANDLE_EVENT == event.type )
1126 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1128 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1130 mEventData->mPrimaryCursorPosition = handleNewPosition;
1131 mEventData->mUpdateCursorPosition = true;
1134 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1136 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1138 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1139 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1141 mEventData->mLeftSelectionPosition = handleNewPosition;
1143 mEventData->mUpdateLeftSelectionPosition = true;
1146 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1148 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1150 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1151 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1153 mEventData->mRightSelectionPosition = handleNewPosition;
1155 mEventData->mUpdateRightSelectionPosition = true;
1158 } // end ( HANDLE_PRESSED == state )
1159 else if( ( HANDLE_RELEASED == state ) ||
1160 handleStopScrolling )
1162 CharacterIndex handlePosition = 0u;
1163 if( handleStopScrolling )
1165 // Convert from decorator's coords to text's coords.
1166 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1167 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1169 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1176 if( Event::GRAB_HANDLE_EVENT == event.type )
1178 mEventData->mUpdateCursorPosition = true;
1179 mEventData->mUpdateInputStyle = true;
1181 if( !IsClipboardEmpty() )
1183 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1186 if( handleStopScrolling )
1188 mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1189 mEventData->mPrimaryCursorPosition = handlePosition;
1192 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1194 ChangeState( EventData::SELECTING );
1196 if( handleStopScrolling )
1198 mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1199 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1201 if( mEventData->mUpdateLeftSelectionPosition )
1203 mEventData->mLeftSelectionPosition = handlePosition;
1207 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1209 ChangeState( EventData::SELECTING );
1211 if( handleStopScrolling )
1213 mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1214 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1215 if( mEventData->mUpdateRightSelectionPosition )
1217 mEventData->mRightSelectionPosition = handlePosition;
1222 mEventData->mDecoratorUpdated = true;
1223 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1224 else if( HANDLE_SCROLLING == state )
1226 const float xSpeed = event.p2.mFloat;
1227 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1228 const Vector2 currentScrollPosition = mScrollPosition;
1230 mScrollPosition.x += xSpeed;
1232 ClampHorizontalScroll( actualSize );
1234 bool endOfScroll = false;
1235 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1237 // Notify the decorator there is no more text to scroll.
1238 // The decorator won't send more scroll events.
1239 mEventData->mDecorator->NotifyEndOfScroll();
1240 // Still need to set the position of the handle.
1244 // Set the position of the handle.
1245 const bool scrollRightDirection = xSpeed > 0.f;
1246 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1247 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1249 if( Event::GRAB_HANDLE_EVENT == event.type )
1251 ChangeState( EventData::GRAB_HANDLE_PANNING );
1253 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1255 // Position the grag handle close to either the left or right edge.
1256 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1258 // Get the new handle position.
1259 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1260 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1263 position.x - mScrollPosition.x,
1264 position.y - mScrollPosition.y );
1266 mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1267 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1268 mEventData->mPrimaryCursorPosition = handlePosition;
1269 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1271 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1273 // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1274 // Think if something can be done to save power.
1276 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1278 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1280 // Position the selection handle close to either the left or right edge.
1281 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1283 // Get the new handle position.
1284 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1285 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1288 position.x - mScrollPosition.x,
1289 position.y - mScrollPosition.y );
1291 if( leftSelectionHandleEvent )
1293 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1294 mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1295 if( differentHandles )
1297 mEventData->mLeftSelectionPosition = handlePosition;
1302 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1303 mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1304 if( differentHandles )
1306 mEventData->mRightSelectionPosition = handlePosition;
1310 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1312 RepositionSelectionHandles();
1314 mEventData->mScrollAfterUpdatePosition = true;
1317 mEventData->mDecoratorUpdated = true;
1318 } // end ( HANDLE_SCROLLING == state )
1321 void Controller::Impl::OnSelectEvent( const Event& event )
1323 if( NULL == mEventData )
1325 // Nothing to do if there is no text.
1329 if( mEventData->mSelectionEnabled )
1331 // Convert from control's coords to text's coords.
1332 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1333 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1335 // Calculates the logical position from the x,y coords.
1336 RepositionSelectionHandles( xPosition,
1339 mEventData->mUpdateLeftSelectionPosition = true;
1340 mEventData->mUpdateRightSelectionPosition = true;
1341 mEventData->mUpdateCursorPosition = false;
1343 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1347 void Controller::Impl::OnSelectAllEvent()
1349 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1351 if( NULL == mEventData )
1353 // Nothing to do if there is no text.
1357 if( mEventData->mSelectionEnabled )
1359 mEventData->mLeftSelectionPosition = 0u;
1360 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1362 mEventData->mScrollAfterUpdatePosition = true;
1363 mEventData->mUpdateLeftSelectionPosition = true;
1364 mEventData->mUpdateRightSelectionPosition = true;
1368 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1370 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1372 // Nothing to select if handles are in the same place.
1373 selectedText.clear();
1377 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1379 //Get start and end position of selection
1380 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1381 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1383 Vector<Character>& utf32Characters = mLogicalModel->mText;
1384 const Length numberOfCharacters = utf32Characters.Count();
1386 // Validate the start and end selection points
1387 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1389 //Get text as a UTF8 string
1390 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1392 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1394 // Set as input style the style of the first deleted character.
1395 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1397 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1399 // Mark the paragraphs to be updated.
1400 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1401 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1403 // Delete text between handles
1404 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1405 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1406 utf32Characters.Erase( first, last );
1408 // Scroll after delete.
1409 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1411 mEventData->mDecoratorUpdated = true;
1415 void Controller::Impl::ShowClipboard()
1419 mClipboard.ShowClipboard();
1423 void Controller::Impl::HideClipboard()
1425 if( mClipboard && mClipboardHideEnabled )
1427 mClipboard.HideClipboard();
1431 void Controller::Impl::SetClipboardHideEnable(bool enable)
1433 mClipboardHideEnabled = enable;
1436 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1438 //Send string to clipboard
1439 return ( mClipboard && mClipboard.SetItem( source ) );
1442 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1444 std::string selectedText;
1445 RetrieveSelection( selectedText, deleteAfterSending );
1446 CopyStringToClipboard( selectedText );
1447 ChangeState( EventData::EDITING );
1450 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1454 retrievedString = mClipboard.GetItem( itemIndex );
1458 void Controller::Impl::RepositionSelectionHandles()
1460 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1461 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1463 if( selectionStart == selectionEnd )
1465 // Nothing to select if handles are in the same place.
1469 mEventData->mDecorator->ClearHighlights();
1471 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1472 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1473 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1474 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1475 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1476 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1477 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1479 // TODO: Better algorithm to create the highlight box.
1481 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1482 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1483 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1485 // Swap the indices if the start is greater than the end.
1486 const bool indicesSwapped = selectionStart > selectionEnd;
1488 // Tell the decorator to flip the selection handles if needed.
1489 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1491 if( indicesSwapped )
1493 std::swap( selectionStart, selectionEnd );
1496 // Get the indices to the first and last selected glyphs.
1497 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1498 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1499 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1500 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1502 // Get the lines where the glyphs are laid-out.
1503 const LineRun* lineRun = mVisualModel->mLines.Begin();
1505 LineIndex lineIndex = 0u;
1506 Length numberOfLines = 0u;
1507 mVisualModel->GetNumberOfLines( glyphStart,
1508 1u + glyphEnd - glyphStart,
1511 const LineIndex firstLineIndex = lineIndex;
1513 // Create the structure to store some selection box info.
1514 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1515 selectionBoxLinesInfo.Resize( numberOfLines );
1517 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1518 selectionBoxInfo->minX = MAX_FLOAT;
1519 selectionBoxInfo->maxX = MIN_FLOAT;
1521 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1523 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1524 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1526 lineRun += firstLineIndex;
1528 // The line height is the addition of the line ascender and the line descender.
1529 // However, the line descender has a negative value, hence the subtraction.
1530 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1532 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1534 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1535 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1536 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1538 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1539 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1540 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1542 // Traverse the glyphs.
1543 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1545 const GlyphInfo& glyph = *( glyphsBuffer + index );
1546 const Vector2& position = *( positionsBuffer + index );
1548 if( splitStartGlyph )
1550 // 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.
1552 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1553 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1554 // Get the direction of the character.
1555 CharacterDirection isCurrentRightToLeft = false;
1556 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1558 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1561 // The end point could be in the middle of the ligature.
1562 // Calculate the number of characters selected.
1563 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1565 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1566 const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1567 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1569 // Store the min and max 'x' for each line.
1570 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1571 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1573 mEventData->mDecorator->AddHighlight( xPosition,
1576 yPosition + selectionBoxInfo->lineHeight );
1578 splitStartGlyph = false;
1582 if( splitEndGlyph && ( index == glyphEnd ) )
1584 // 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.
1586 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1587 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1588 // Get the direction of the character.
1589 CharacterDirection isCurrentRightToLeft = false;
1590 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1592 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1595 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1597 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1598 const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1599 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1601 // Store the min and max 'x' for each line.
1602 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1603 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1605 mEventData->mDecorator->AddHighlight( xPosition,
1608 yPosition + selectionBoxInfo->lineHeight );
1610 splitEndGlyph = false;
1614 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1615 const float xPositionAdvance = xPosition + glyph.advance;
1616 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1618 // Store the min and max 'x' for each line.
1619 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1620 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1622 mEventData->mDecorator->AddHighlight( xPosition,
1625 yPosition + selectionBoxInfo->lineHeight );
1627 // Whether to retrieve the next line.
1628 if( index == lastGlyphOfLine )
1630 // Retrieve the next line.
1633 // Get the last glyph of the new line.
1634 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1637 if( lineIndex < firstLineIndex + numberOfLines )
1639 // Get the selection box info for the next line.
1640 const float currentLineOffset = selectionBoxInfo->lineOffset;
1643 selectionBoxInfo->minX = MAX_FLOAT;
1644 selectionBoxInfo->maxX = MIN_FLOAT;
1646 // The line height is the addition of the line ascender and the line descender.
1647 // However, the line descender has a negative value, hence the subtraction.
1648 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1650 // Update the line's vertical offset.
1651 selectionBoxInfo->lineOffset = currentLineOffset + selectionBoxInfo->lineHeight;
1656 // Add extra geometry to 'boxify' the selection.
1658 if( 1u < numberOfLines )
1660 // Boxify the first line.
1661 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1662 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1664 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1665 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1669 // Boxify at the beginning of the line.
1670 mEventData->mDecorator->AddHighlight( 0.f,
1671 firstSelectionBoxLineInfo.lineOffset,
1672 firstSelectionBoxLineInfo.minX,
1673 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1678 // Boxify at the end of the line.
1679 mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1680 firstSelectionBoxLineInfo.lineOffset,
1681 mVisualModel->mControlSize.width,
1682 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1685 // Boxify the central lines.
1686 if( 2u < numberOfLines )
1688 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1689 endIt = selectionBoxLinesInfo.End() - 1u;
1693 const SelectionBoxInfo& info = *it;
1695 mEventData->mDecorator->AddHighlight( 0.f,
1698 info.lineOffset + info.lineHeight );
1700 mEventData->mDecorator->AddHighlight( info.maxX,
1702 mVisualModel->mControlSize.width,
1703 info.lineOffset + info.lineHeight );
1707 // Boxify the last line.
1708 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1709 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1711 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1712 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1716 // Boxify at the beginning of the line.
1717 mEventData->mDecorator->AddHighlight( 0.f,
1718 lastSelectionBoxLineInfo.lineOffset,
1719 lastSelectionBoxLineInfo.minX,
1720 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1725 // Boxify at the end of the line.
1726 mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1727 lastSelectionBoxLineInfo.lineOffset,
1728 mVisualModel->mControlSize.width,
1729 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1734 CursorInfo primaryCursorInfo;
1735 GetCursorPosition( mEventData->mLeftSelectionPosition,
1736 primaryCursorInfo );
1738 CursorInfo secondaryCursorInfo;
1739 GetCursorPosition( mEventData->mRightSelectionPosition,
1740 secondaryCursorInfo );
1742 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1743 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1745 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1747 primaryCursorInfo.lineOffset + mScrollPosition.y,
1748 primaryCursorInfo.lineHeight );
1750 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1751 secondaryPosition.x,
1752 secondaryCursorInfo.lineOffset + mScrollPosition.y,
1753 secondaryCursorInfo.lineHeight );
1755 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1756 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1758 // Set the flag to update the decorator.
1759 mEventData->mDecoratorUpdated = true;
1762 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1764 if( NULL == mEventData )
1766 // Nothing to do if there is no text input.
1770 if( IsShowingPlaceholderText() )
1772 // Nothing to do if there is the place-holder text.
1776 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1777 const Length numberOfLines = mVisualModel->mLines.Count();
1778 if( ( 0 == numberOfGlyphs ) ||
1779 ( 0 == numberOfLines ) )
1781 // Nothing to do if there is no text.
1785 // Find which word was selected
1786 CharacterIndex selectionStart( 0 );
1787 CharacterIndex selectionEnd( 0 );
1788 FindSelectionIndices( mVisualModel,
1795 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1797 if( selectionStart == selectionEnd )
1799 ChangeState( EventData::EDITING );
1800 // Nothing to select. i.e. a white space, out of bounds
1804 mEventData->mLeftSelectionPosition = selectionStart;
1805 mEventData->mRightSelectionPosition = selectionEnd;
1808 void Controller::Impl::SetPopupButtons()
1811 * Sets the Popup buttons to be shown depending on State.
1813 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1815 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1818 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1820 if( EventData::SELECTING == mEventData->mState )
1822 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1824 if( !IsClipboardEmpty() )
1826 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1827 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1830 if( !mEventData->mAllTextSelected )
1832 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1835 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1837 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1839 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1842 if( !IsClipboardEmpty() )
1844 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1845 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1848 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1850 if ( !IsClipboardEmpty() )
1852 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1853 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1857 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1860 void Controller::Impl::ChangeState( EventData::State newState )
1862 if( NULL == mEventData )
1864 // Nothing to do if there is no text input.
1868 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1870 if( mEventData->mState != newState )
1872 mEventData->mState = newState;
1874 if( EventData::INACTIVE == mEventData->mState )
1876 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1877 mEventData->mDecorator->StopCursorBlink();
1878 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1879 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1880 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1881 mEventData->mDecorator->SetPopupActive( false );
1882 mEventData->mDecoratorUpdated = true;
1885 else if( EventData::INTERRUPTED == mEventData->mState)
1887 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1888 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1889 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1890 mEventData->mDecorator->SetPopupActive( false );
1891 mEventData->mDecoratorUpdated = true;
1894 else if( EventData::SELECTING == mEventData->mState )
1896 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1897 mEventData->mDecorator->StopCursorBlink();
1898 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1899 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1900 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1901 if( mEventData->mGrabHandlePopupEnabled )
1904 mEventData->mDecorator->SetPopupActive( true );
1906 mEventData->mDecoratorUpdated = true;
1908 else if( EventData::EDITING == mEventData->mState )
1910 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1911 if( mEventData->mCursorBlinkEnabled )
1913 mEventData->mDecorator->StartCursorBlink();
1915 // Grab handle is not shown until a tap is received whilst EDITING
1916 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1917 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1918 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1919 if( mEventData->mGrabHandlePopupEnabled )
1921 mEventData->mDecorator->SetPopupActive( false );
1923 mEventData->mDecoratorUpdated = true;
1926 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1928 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
1930 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1931 if( mEventData->mCursorBlinkEnabled )
1933 mEventData->mDecorator->StartCursorBlink();
1935 if( mEventData->mSelectionEnabled )
1937 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1938 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1942 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1944 if( mEventData->mGrabHandlePopupEnabled )
1947 mEventData->mDecorator->SetPopupActive( true );
1950 mEventData->mDecoratorUpdated = true;
1952 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1954 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
1956 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1957 if( mEventData->mCursorBlinkEnabled )
1959 mEventData->mDecorator->StartCursorBlink();
1961 // Grab handle is not shown until a tap is received whilst EDITING
1962 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1963 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1964 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1965 if( mEventData->mGrabHandlePopupEnabled )
1967 mEventData->mDecorator->SetPopupActive( false );
1969 mEventData->mDecoratorUpdated = true;
1972 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1974 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1975 mEventData->mDecorator->StopCursorBlink();
1976 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1977 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1978 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1979 if( mEventData->mGrabHandlePopupEnabled )
1981 mEventData->mDecorator->SetPopupActive( false );
1983 mEventData->mDecoratorUpdated = true;
1985 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1987 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
1989 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1990 if( mEventData->mCursorBlinkEnabled )
1992 mEventData->mDecorator->StartCursorBlink();
1994 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1995 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1996 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1997 if( mEventData->mGrabHandlePopupEnabled )
1999 mEventData->mDecorator->SetPopupActive( false );
2001 mEventData->mDecoratorUpdated = true;
2003 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2005 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2007 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2008 if( mEventData->mCursorBlinkEnabled )
2010 mEventData->mDecorator->StartCursorBlink();
2013 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2014 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2015 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2017 if( mEventData->mGrabHandlePopupEnabled )
2020 mEventData->mDecorator->SetPopupActive( true );
2023 mEventData->mDecoratorUpdated = true;
2028 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2029 CursorInfo& cursorInfo )
2031 if( !IsShowingRealText() )
2033 // Do not want to use the place-holder text to set the cursor position.
2035 // Use the line's height of the font's family set to set the cursor's size.
2036 // If there is no font's family set, use the default font.
2037 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2039 cursorInfo.lineOffset = 0.f;
2040 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2041 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2043 switch( mLayoutEngine.GetHorizontalAlignment() )
2045 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2047 cursorInfo.primaryPosition.x = 0.f;
2050 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2052 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2055 case LayoutEngine::HORIZONTAL_ALIGN_END:
2057 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2062 // Nothing else to do.
2066 Text::GetCursorPosition( mVisualModel,
2072 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2074 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2076 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2077 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2079 if( 0.f > cursorInfo.primaryPosition.x )
2081 cursorInfo.primaryPosition.x = 0.f;
2084 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2085 if( cursorInfo.primaryPosition.x > edgeWidth )
2087 cursorInfo.primaryPosition.x = edgeWidth;
2092 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2094 if( NULL == mEventData )
2096 // Nothing to do if there is no text input.
2100 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2102 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2103 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2105 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2106 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2108 if( numberOfCharacters > 1u )
2110 const Script script = mLogicalModel->GetScript( index );
2111 if( HasLigatureMustBreak( script ) )
2113 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2114 numberOfCharacters = 1u;
2119 while( 0u == numberOfCharacters )
2122 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2126 if( index < mEventData->mPrimaryCursorPosition )
2128 cursorIndex -= numberOfCharacters;
2132 cursorIndex += numberOfCharacters;
2138 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2140 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2141 if( NULL == mEventData )
2143 // Nothing to do if there is no text input.
2144 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2148 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2150 // Sets the cursor position.
2151 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2154 cursorInfo.primaryCursorHeight,
2155 cursorInfo.lineHeight );
2156 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2158 // Sets the grab handle position.
2159 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2161 cursorInfo.lineOffset + mScrollPosition.y,
2162 cursorInfo.lineHeight );
2164 if( cursorInfo.isSecondaryCursor )
2166 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2167 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2168 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2169 cursorInfo.secondaryCursorHeight,
2170 cursorInfo.lineHeight );
2171 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2174 // Set which cursors are active according the state.
2175 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2177 if( cursorInfo.isSecondaryCursor )
2179 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2183 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2188 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2191 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2194 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2195 const CursorInfo& cursorInfo )
2197 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2198 ( RIGHT_SELECTION_HANDLE != handleType ) )
2203 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2205 // Sets the handle's position.
2206 mEventData->mDecorator->SetPosition( handleType,
2208 cursorInfo.lineOffset + mScrollPosition.y,
2209 cursorInfo.lineHeight );
2211 // If selection handle at start of the text and other at end of the text then all text is selected.
2212 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2213 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2214 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2217 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2219 // Clamp between -space & 0.
2221 if( actualSize.width > mVisualModel->mControlSize.width )
2223 const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2224 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2225 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2227 mEventData->mDecoratorUpdated = true;
2231 mScrollPosition.x = 0.f;
2235 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2237 // Clamp between -space & 0.
2238 if( actualSize.height > mVisualModel->mControlSize.height )
2240 const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2241 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2242 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2244 mEventData->mDecoratorUpdated = true;
2248 mScrollPosition.y = 0.f;
2252 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2254 const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2256 // position is in actor's coords.
2257 const float positionEnd = position.x + cursorWidth;
2259 // Transform the position to decorator coords.
2260 const float decoratorPositionBegin = position.x + mScrollPosition.x;
2261 const float decoratorPositionEnd = positionEnd + mScrollPosition.x;
2263 if( decoratorPositionBegin < 0.f )
2265 mScrollPosition.x = -position.x;
2267 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2269 mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd;
2273 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2275 // Get the current cursor position in decorator coords.
2276 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2278 // Calculate the offset to match the cursor position before the character was deleted.
2279 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2281 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2283 // Makes the new cursor position visible if needed.
2284 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2287 void Controller::Impl::RequestRelayout()
2289 mControlInterface.RequestTextRelayout();
2294 } // namespace Toolkit