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 if( mEventData->mUpdateCursorPosition ||
165 mEventData->mUpdateLeftSelectionPosition ||
166 mEventData->mUpdateRightSelectionPosition )
171 // The cursor must also be repositioned after inserts into the model
172 if( mEventData->mUpdateCursorPosition )
174 // Updates the cursor position and scrolls the text to make it visible.
175 CursorInfo cursorInfo;
176 GetCursorPosition( mEventData->mPrimaryCursorPosition,
179 // Scroll first the text after delete ...
180 if( mEventData->mScrollAfterDelete )
182 ScrollTextToMatchCursor( cursorInfo );
185 // ... then, text can be scrolled to make the cursor visible.
186 if( mEventData->mScrollAfterUpdatePosition )
188 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
190 mEventData->mScrollAfterUpdatePosition = false;
191 mEventData->mScrollAfterDelete = false;
193 UpdateCursorPosition( cursorInfo );
195 mEventData->mDecoratorUpdated = true;
196 mEventData->mUpdateCursorPosition = false;
200 bool leftScroll = false;
201 bool rightScroll = false;
203 CursorInfo leftHandleInfo;
204 CursorInfo rightHandleInfo;
206 if( mEventData->mUpdateLeftSelectionPosition )
208 GetCursorPosition( mEventData->mLeftSelectionPosition,
211 if( mEventData->mScrollAfterUpdatePosition )
213 ScrollToMakePositionVisible( leftHandleInfo.primaryPosition );
218 if( mEventData->mUpdateRightSelectionPosition )
220 GetCursorPosition( mEventData->mRightSelectionPosition,
223 if( mEventData->mScrollAfterUpdatePosition )
225 ScrollToMakePositionVisible( rightHandleInfo.primaryPosition );
230 if( mEventData->mUpdateLeftSelectionPosition )
232 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
236 mEventData->mDecoratorUpdated = true;
239 if( mEventData->mUpdateRightSelectionPosition )
241 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
245 mEventData->mDecoratorUpdated = true;
248 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
250 RepositionSelectionHandles();
252 mEventData->mUpdateLeftSelectionPosition = false;
253 mEventData->mUpdateRightSelectionPosition = false;
256 if( leftScroll || rightScroll )
258 mEventData->mScrollAfterUpdatePosition = false;
262 if( mEventData->mUpdateInputStyle )
264 // Set the default style first.
265 RetrieveDefaultInputStyle( mEventData->mInputStyle );
267 // Get the character index from the cursor index.
268 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
270 // Retrieve the style from the style runs stored in the logical model.
271 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
273 mEventData->mUpdateInputStyle = false;
276 mEventData->mEventQueue.clear();
278 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
280 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
281 mEventData->mDecoratorUpdated = false;
283 return decoratorUpdated;
286 void Controller::Impl::NotifyImfManager()
288 if( mEventData && mEventData->mImfManager )
290 CharacterIndex cursorPosition = GetLogicalCursorPosition();
292 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
294 // Update the cursor position by removing the initial white spaces.
295 if( cursorPosition < numberOfWhiteSpaces )
301 cursorPosition -= numberOfWhiteSpaces;
304 mEventData->mImfManager.SetCursorPosition( cursorPosition );
305 mEventData->mImfManager.NotifyCursorPosition();
309 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
311 CharacterIndex cursorPosition = 0u;
315 if( ( EventData::SELECTING == mEventData->mState ) ||
316 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
318 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
322 cursorPosition = mEventData->mPrimaryCursorPosition;
326 return cursorPosition;
329 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
331 Length numberOfWhiteSpaces = 0u;
333 // Get the buffer to the text.
334 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
336 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
337 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
339 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
345 return numberOfWhiteSpaces;
348 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
350 // Get the total number of characters.
351 Length numberOfCharacters = mLogicalModel->mText.Count();
353 // Retrieve the text.
354 if( 0u != numberOfCharacters )
356 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
360 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
362 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
363 mTextUpdateInfo.mStartGlyphIndex = 0u;
364 mTextUpdateInfo.mStartLineIndex = 0u;
365 numberOfCharacters = 0u;
367 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
368 if( 0u == numberOfParagraphs )
370 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
371 numberOfCharacters = 0u;
373 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
375 // Nothing else to do if there are no paragraphs.
379 // Find the paragraphs to be updated.
380 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
381 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
383 // Text is being added at the end of the current text.
384 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
386 // Text is being added in a new paragraph after the last character of the text.
387 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
388 numberOfCharacters = 0u;
389 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
391 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
392 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
394 // Nothing else to do;
398 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
402 Length numberOfCharactersToUpdate = 0u;
403 if( mTextUpdateInfo.mFullRelayoutNeeded )
405 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
409 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
411 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
412 numberOfCharactersToUpdate,
413 paragraphsToBeUpdated );
416 if( 0u != paragraphsToBeUpdated.Count() )
418 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
419 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
420 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
422 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
423 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
425 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
426 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
427 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
428 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
430 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
431 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
433 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
437 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
441 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
442 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
445 void Controller::Impl::ClearFullModelData( OperationsMask operations )
447 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
449 mLogicalModel->mLineBreakInfo.Clear();
450 mLogicalModel->mParagraphInfo.Clear();
453 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
455 mLogicalModel->mLineBreakInfo.Clear();
458 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
460 mLogicalModel->mScriptRuns.Clear();
463 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
465 mLogicalModel->mFontRuns.Clear();
468 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
470 if( NO_OPERATION != ( BIDI_INFO & operations ) )
472 mLogicalModel->mBidirectionalParagraphInfo.Clear();
473 mLogicalModel->mCharacterDirections.Clear();
476 if( NO_OPERATION != ( REORDER & operations ) )
478 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
479 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
480 endIt = mLogicalModel->mBidirectionalLineInfo.End();
484 BidirectionalLineInfoRun& bidiLineInfo = *it;
486 free( bidiLineInfo.visualToLogicalMap );
487 bidiLineInfo.visualToLogicalMap = NULL;
489 mLogicalModel->mBidirectionalLineInfo.Clear();
493 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
495 mVisualModel->mGlyphs.Clear();
496 mVisualModel->mGlyphsToCharacters.Clear();
497 mVisualModel->mCharactersToGlyph.Clear();
498 mVisualModel->mCharactersPerGlyph.Clear();
499 mVisualModel->mGlyphsPerCharacter.Clear();
500 mVisualModel->mGlyphPositions.Clear();
503 if( NO_OPERATION != ( LAYOUT & operations ) )
505 mVisualModel->mLines.Clear();
508 if( NO_OPERATION != ( COLOR & operations ) )
510 mVisualModel->mColorIndices.Clear();
514 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
516 const CharacterIndex endIndexPlusOne = endIndex + 1u;
518 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
520 // Clear the line break info.
521 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
523 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
524 lineBreakInfoBuffer + endIndexPlusOne );
526 // Clear the paragraphs.
527 ClearCharacterRuns( startIndex,
529 mLogicalModel->mParagraphInfo );
532 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
534 // Clear the word break info.
535 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
537 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
538 wordBreakInfoBuffer + endIndexPlusOne );
541 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
543 // Clear the scripts.
544 ClearCharacterRuns( startIndex,
546 mLogicalModel->mScriptRuns );
549 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
552 ClearCharacterRuns( startIndex,
554 mLogicalModel->mFontRuns );
557 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
559 if( NO_OPERATION != ( BIDI_INFO & operations ) )
561 // Clear the bidirectional paragraph info.
562 ClearCharacterRuns( startIndex,
564 mLogicalModel->mBidirectionalParagraphInfo );
566 // Clear the character's directions.
567 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
569 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
570 characterDirectionsBuffer + endIndexPlusOne );
573 if( NO_OPERATION != ( REORDER & operations ) )
575 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
576 uint32_t endRemoveIndex = startRemoveIndex;
577 ClearCharacterRuns( startIndex,
579 mLogicalModel->mBidirectionalLineInfo,
583 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
585 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
586 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
587 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
591 BidirectionalLineInfoRun& bidiLineInfo = *it;
593 free( bidiLineInfo.visualToLogicalMap );
594 bidiLineInfo.visualToLogicalMap = NULL;
597 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
598 bidirectionalLineInfoBuffer + endRemoveIndex );
603 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
605 const CharacterIndex endIndexPlusOne = endIndex + 1u;
606 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
608 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
609 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
610 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
612 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
613 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
615 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
617 // Update the character to glyph indices.
618 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
619 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
623 CharacterIndex& index = *it;
624 index -= numberOfGlyphsRemoved;
627 // Clear the character to glyph conversion table.
628 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
629 charactersToGlyphBuffer + endIndexPlusOne );
631 // Clear the glyphs per character table.
632 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
633 glyphsPerCharacterBuffer + endIndexPlusOne );
635 // Clear the glyphs buffer.
636 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
637 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
638 glyphsBuffer + endGlyphIndexPlusOne );
640 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
642 // Update the glyph to character indices.
643 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
644 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
648 CharacterIndex& index = *it;
649 index -= numberOfCharactersRemoved;
652 // Clear the glyphs to characters buffer.
653 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
654 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
656 // Clear the characters per glyph buffer.
657 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
658 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
659 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
661 // Clear the positions buffer.
662 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
663 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
664 positionsBuffer + endGlyphIndexPlusOne );
667 if( NO_OPERATION != ( LAYOUT & operations ) )
670 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
671 uint32_t endRemoveIndex = startRemoveIndex;
672 ClearCharacterRuns( startIndex,
674 mVisualModel->mLines,
678 // Will update the glyph runs.
679 startRemoveIndex = mVisualModel->mLines.Count();
680 endRemoveIndex = startRemoveIndex;
681 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
682 endGlyphIndexPlusOne - 1u,
683 mVisualModel->mLines,
687 // Set the line index from where to insert the new laid-out lines.
688 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
690 LineRun* linesBuffer = mVisualModel->mLines.Begin();
691 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
692 linesBuffer + endRemoveIndex );
695 if( NO_OPERATION != ( COLOR & operations ) )
697 if( 0u != mVisualModel->mColorIndices.Count() )
699 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
700 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
701 colorIndexBuffer + endGlyphIndexPlusOne );
706 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
708 if( mTextUpdateInfo.mClearAll ||
709 ( ( 0u == startIndex ) &&
710 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
712 ClearFullModelData( operations );
716 // Clear the model data related with characters.
717 ClearCharacterModelData( startIndex, endIndex, operations );
719 // Clear the model data related with glyphs.
720 ClearGlyphModelData( startIndex, endIndex, operations );
723 // The estimated number of lines. Used to avoid reallocations when layouting.
724 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
726 mVisualModel->ClearCaches();
729 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
731 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
733 // Calculate the operations to be done.
734 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
736 if( NO_OPERATION == operations )
738 // Nothing to do if no operations are pending and required.
742 Vector<Character>& utf32Characters = mLogicalModel->mText;
744 const Length numberOfCharacters = utf32Characters.Count();
746 // Index to the first character of the first paragraph to be updated.
747 CharacterIndex startIndex = 0u;
748 // Number of characters of the paragraphs to be removed.
749 Length paragraphCharacters = 0u;
751 CalculateTextUpdateIndices( paragraphCharacters );
752 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
754 if( mTextUpdateInfo.mClearAll ||
755 ( 0u != paragraphCharacters ) )
757 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
760 mTextUpdateInfo.mClearAll = false;
762 // Whether the model is updated.
763 bool updated = false;
765 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
766 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
768 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
770 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
771 // calculate the bidirectional info for each 'paragraph'.
772 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
773 // is not shaped together).
774 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
776 SetLineBreakInfo( utf32Characters,
778 requestedNumberOfCharacters,
781 // Create the paragraph info.
782 mLogicalModel->CreateParagraphInfo( startIndex,
783 requestedNumberOfCharacters );
787 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
788 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
790 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
791 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
793 SetWordBreakInfo( utf32Characters,
795 requestedNumberOfCharacters,
800 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
801 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
803 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
804 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
806 if( getScripts || validateFonts )
808 // Validates the fonts assigned by the application or assigns default ones.
809 // It makes sure all the characters are going to be rendered by the correct font.
810 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
814 // Retrieves the scripts used in the text.
815 multilanguageSupport.SetScripts( utf32Characters,
817 requestedNumberOfCharacters,
823 // Validate the fonts set through the mark-up string.
824 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
826 // Get the default font id.
827 const FontId defaultFontId = ( NULL == mFontDefaults ) ? 0u : mFontDefaults->GetFontId( mFontClient );
829 // Validates the fonts. If there is a character with no assigned font it sets a default one.
830 // After this call, fonts are validated.
831 multilanguageSupport.ValidateFonts( utf32Characters,
836 requestedNumberOfCharacters,
842 Vector<Character> mirroredUtf32Characters;
843 bool textMirrored = false;
844 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
845 if( NO_OPERATION != ( BIDI_INFO & operations ) )
847 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
848 bidirectionalInfo.Reserve( numberOfParagraphs );
850 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
851 SetBidirectionalInfo( utf32Characters,
855 requestedNumberOfCharacters,
858 if( 0u != bidirectionalInfo.Count() )
860 // Only set the character directions if there is right to left characters.
861 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
862 GetCharactersDirection( bidirectionalInfo,
865 requestedNumberOfCharacters,
868 // This paragraph has right to left text. Some characters may need to be mirrored.
869 // TODO: consider if the mirrored string can be stored as well.
871 textMirrored = GetMirroredText( utf32Characters,
875 requestedNumberOfCharacters,
876 mirroredUtf32Characters );
880 // There is no right to left characters. Clear the directions vector.
881 mLogicalModel->mCharacterDirections.Clear();
886 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
887 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
888 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
889 Vector<GlyphIndex> newParagraphGlyphs;
890 newParagraphGlyphs.Reserve( numberOfParagraphs );
892 const Length currentNumberOfGlyphs = glyphs.Count();
893 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
895 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
897 ShapeText( textToShape,
902 mTextUpdateInfo.mStartGlyphIndex,
903 requestedNumberOfCharacters,
905 glyphsToCharactersMap,
907 newParagraphGlyphs );
909 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
910 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
911 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
915 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
917 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
919 GlyphInfo* glyphsBuffer = glyphs.Begin();
920 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
922 // Update the width and advance of all new paragraph characters.
923 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
925 const GlyphIndex index = *it;
926 GlyphInfo& glyph = *( glyphsBuffer + index );
928 glyph.xBearing = 0.f;
935 if( NO_OPERATION != ( COLOR & operations ) )
937 // Set the color runs in glyphs.
938 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
939 mVisualModel->mCharactersToGlyph,
940 mVisualModel->mGlyphsPerCharacter,
942 mTextUpdateInfo.mStartGlyphIndex,
943 requestedNumberOfCharacters,
944 mVisualModel->mColors,
945 mVisualModel->mColorIndices );
950 if( ( NULL != mEventData ) &&
951 mEventData->mPreEditFlag &&
952 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
954 // Add the underline for the pre-edit text.
955 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
956 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
958 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
959 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
960 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
961 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
963 GlyphRun underlineRun;
964 underlineRun.glyphIndex = glyphStart;
965 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
967 // TODO: At the moment the underline runs are only for pre-edit.
968 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
971 // The estimated number of lines. Used to avoid reallocations when layouting.
972 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
974 // Set the previous number of characters for the next time the text is updated.
975 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
980 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
982 // Sets the default text's color.
983 inputStyle.textColor = mTextColor;
984 inputStyle.isDefaultColor = true;
986 inputStyle.familyName.clear();
987 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
988 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
989 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
990 inputStyle.size = 0.f;
992 inputStyle.familyDefined = false;
993 inputStyle.weightDefined = false;
994 inputStyle.widthDefined = false;
995 inputStyle.slantDefined = false;
996 inputStyle.sizeDefined = false;
998 // Sets the default font's family name, weight, width, slant and size.
1001 if( mFontDefaults->familyDefined )
1003 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1004 inputStyle.familyDefined = true;
1007 if( mFontDefaults->weightDefined )
1009 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1010 inputStyle.weightDefined = true;
1013 if( mFontDefaults->widthDefined )
1015 inputStyle.width = mFontDefaults->mFontDescription.width;
1016 inputStyle.widthDefined = true;
1019 if( mFontDefaults->slantDefined )
1021 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1022 inputStyle.slantDefined = true;
1025 if( mFontDefaults->sizeDefined )
1027 inputStyle.size = mFontDefaults->mDefaultPointSize;
1028 inputStyle.sizeDefined = true;
1033 float Controller::Impl::GetDefaultFontLineHeight()
1035 FontId defaultFontId = 0u;
1036 if( NULL == mFontDefaults )
1038 TextAbstraction::FontDescription fontDescription;
1039 defaultFontId = mFontClient.GetFontId( fontDescription );
1043 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1046 Text::FontMetrics fontMetrics;
1047 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1049 return( fontMetrics.ascender - fontMetrics.descender );
1052 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1054 if( NULL == mEventData )
1056 // Nothing to do if there is no text input.
1060 int keyCode = event.p1.mInt;
1062 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1064 if( mEventData->mPrimaryCursorPosition > 0u )
1066 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1069 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1071 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1073 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1076 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1080 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1085 mEventData->mUpdateCursorPosition = true;
1086 mEventData->mUpdateInputStyle = true;
1087 mEventData->mScrollAfterUpdatePosition = true;
1090 void Controller::Impl::OnTapEvent( const Event& event )
1092 if( NULL != mEventData )
1094 const unsigned int tapCount = event.p1.mUint;
1096 if( 1u == tapCount )
1098 if( IsShowingRealText() )
1100 // Convert from control's coords to text's coords.
1101 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1102 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1104 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1110 // When the cursor position is changing, delay cursor blinking
1111 mEventData->mDecorator->DelayCursorBlink();
1115 mEventData->mPrimaryCursorPosition = 0u;
1118 mEventData->mUpdateCursorPosition = true;
1119 mEventData->mScrollAfterUpdatePosition = true;
1120 mEventData->mUpdateInputStyle = true;
1122 // Notify the cursor position to the imf manager.
1123 if( mEventData->mImfManager )
1125 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1126 mEventData->mImfManager.NotifyCursorPosition();
1132 void Controller::Impl::OnPanEvent( const Event& event )
1134 if( NULL == mEventData )
1136 // Nothing to do if there is no text input.
1140 int state = event.p1.mInt;
1142 if( ( Gesture::Started == state ) ||
1143 ( Gesture::Continuing == state ) )
1145 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1146 const Vector2 currentScroll = mScrollPosition;
1148 if( mEventData->mHorizontalScrollingEnabled )
1150 const float displacementX = event.p2.mFloat;
1151 mScrollPosition.x += displacementX;
1153 ClampHorizontalScroll( actualSize );
1156 if( mEventData->mVerticalScrollingEnabled )
1158 const float displacementY = event.p3.mFloat;
1159 mScrollPosition.y += displacementY;
1161 ClampVerticalScroll( actualSize );
1164 if( mEventData->mDecorator )
1166 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1171 void Controller::Impl::OnLongPressEvent( const Event& event )
1173 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1175 if( EventData::EDITING == mEventData->mState )
1177 ChangeState ( EventData::EDITING_WITH_POPUP );
1178 mEventData->mDecoratorUpdated = true;
1182 void Controller::Impl::OnHandleEvent( const Event& event )
1184 if( NULL == mEventData )
1186 // Nothing to do if there is no text input.
1190 const unsigned int state = event.p1.mUint;
1191 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1193 if( HANDLE_PRESSED == state )
1195 // Convert from decorator's coords to text's coords.
1196 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1197 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1199 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1205 if( Event::GRAB_HANDLE_EVENT == event.type )
1207 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1209 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1211 mEventData->mPrimaryCursorPosition = handleNewPosition;
1212 mEventData->mUpdateCursorPosition = true;
1215 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1217 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1219 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1220 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1222 mEventData->mLeftSelectionPosition = handleNewPosition;
1224 mEventData->mUpdateLeftSelectionPosition = true;
1227 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1229 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1231 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1232 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1234 mEventData->mRightSelectionPosition = handleNewPosition;
1236 mEventData->mUpdateRightSelectionPosition = true;
1239 } // end ( HANDLE_PRESSED == state )
1240 else if( ( HANDLE_RELEASED == state ) ||
1241 handleStopScrolling )
1243 CharacterIndex handlePosition = 0u;
1244 if( handleStopScrolling )
1246 // Convert from decorator's coords to text's coords.
1247 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1248 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1250 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1257 if( Event::GRAB_HANDLE_EVENT == event.type )
1259 mEventData->mUpdateCursorPosition = true;
1260 mEventData->mUpdateInputStyle = true;
1262 if( !IsClipboardEmpty() )
1264 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1267 if( handleStopScrolling )
1269 mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
1270 mEventData->mPrimaryCursorPosition = handlePosition;
1273 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1275 ChangeState( EventData::SELECTING );
1277 if( handleStopScrolling )
1279 mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
1280 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
1282 if( mEventData->mUpdateLeftSelectionPosition )
1284 mEventData->mLeftSelectionPosition = handlePosition;
1288 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1290 ChangeState( EventData::SELECTING );
1292 if( handleStopScrolling )
1294 mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
1295 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
1296 if( mEventData->mUpdateRightSelectionPosition )
1298 mEventData->mRightSelectionPosition = handlePosition;
1303 mEventData->mDecoratorUpdated = true;
1304 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1305 else if( HANDLE_SCROLLING == state )
1307 const float xSpeed = event.p2.mFloat;
1308 const Vector2& actualSize = mVisualModel->GetLayoutSize();
1309 const Vector2 currentScrollPosition = mScrollPosition;
1311 mScrollPosition.x += xSpeed;
1313 ClampHorizontalScroll( actualSize );
1315 bool endOfScroll = false;
1316 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1318 // Notify the decorator there is no more text to scroll.
1319 // The decorator won't send more scroll events.
1320 mEventData->mDecorator->NotifyEndOfScroll();
1321 // Still need to set the position of the handle.
1325 // Set the position of the handle.
1326 const bool scrollRightDirection = xSpeed > 0.f;
1327 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1328 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1330 if( Event::GRAB_HANDLE_EVENT == event.type )
1332 ChangeState( EventData::GRAB_HANDLE_PANNING );
1334 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1336 // Position the grag handle close to either the left or right edge.
1337 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1339 // Get the new handle position.
1340 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1341 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1344 position.x - mScrollPosition.x,
1345 position.y - mScrollPosition.y );
1347 mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
1348 mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
1349 mEventData->mPrimaryCursorPosition = handlePosition;
1350 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1352 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1354 // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
1355 // Think if something can be done to save power.
1357 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1359 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1361 // Position the selection handle close to either the left or right edge.
1362 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1364 // Get the new handle position.
1365 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1366 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1369 position.x - mScrollPosition.x,
1370 position.y - mScrollPosition.y );
1372 if( leftSelectionHandleEvent )
1374 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1375 mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
1376 if( differentHandles )
1378 mEventData->mLeftSelectionPosition = handlePosition;
1383 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1384 mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
1385 if( differentHandles )
1387 mEventData->mRightSelectionPosition = handlePosition;
1391 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1393 RepositionSelectionHandles();
1395 mEventData->mScrollAfterUpdatePosition = true;
1398 mEventData->mDecoratorUpdated = true;
1399 } // end ( HANDLE_SCROLLING == state )
1402 void Controller::Impl::OnSelectEvent( const Event& event )
1404 if( NULL == mEventData )
1406 // Nothing to do if there is no text.
1410 if( mEventData->mSelectionEnabled )
1412 // Convert from control's coords to text's coords.
1413 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1414 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1416 // Calculates the logical position from the x,y coords.
1417 RepositionSelectionHandles( xPosition,
1420 mEventData->mUpdateLeftSelectionPosition = true;
1421 mEventData->mUpdateRightSelectionPosition = true;
1422 mEventData->mUpdateCursorPosition = false;
1424 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
1428 void Controller::Impl::OnSelectAllEvent()
1430 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1432 if( NULL == mEventData )
1434 // Nothing to do if there is no text.
1438 if( mEventData->mSelectionEnabled )
1440 mEventData->mLeftSelectionPosition = 0u;
1441 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1443 mEventData->mScrollAfterUpdatePosition = true;
1444 mEventData->mUpdateLeftSelectionPosition = true;
1445 mEventData->mUpdateRightSelectionPosition = true;
1449 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1451 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1453 // Nothing to select if handles are in the same place.
1454 selectedText.clear();
1458 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1460 //Get start and end position of selection
1461 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1462 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1464 Vector<Character>& utf32Characters = mLogicalModel->mText;
1465 const Length numberOfCharacters = utf32Characters.Count();
1467 // Validate the start and end selection points
1468 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1470 //Get text as a UTF8 string
1471 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1473 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1475 // Set as input style the style of the first deleted character.
1476 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1478 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1480 // Mark the paragraphs to be updated.
1481 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1482 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1484 // Delete text between handles
1485 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1486 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1487 utf32Characters.Erase( first, last );
1489 // Will show the cursor at the first character of the selection.
1490 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1494 // Will show the cursor at the last character of the selection.
1495 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1498 mEventData->mDecoratorUpdated = true;
1502 void Controller::Impl::ShowClipboard()
1506 mClipboard.ShowClipboard();
1510 void Controller::Impl::HideClipboard()
1512 if( mClipboard && mClipboardHideEnabled )
1514 mClipboard.HideClipboard();
1518 void Controller::Impl::SetClipboardHideEnable(bool enable)
1520 mClipboardHideEnabled = enable;
1523 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1525 //Send string to clipboard
1526 return ( mClipboard && mClipboard.SetItem( source ) );
1529 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1531 std::string selectedText;
1532 RetrieveSelection( selectedText, deleteAfterSending );
1533 CopyStringToClipboard( selectedText );
1534 ChangeState( EventData::EDITING );
1537 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1541 retrievedString = mClipboard.GetItem( itemIndex );
1545 void Controller::Impl::RepositionSelectionHandles()
1547 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1548 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1550 if( selectionStart == selectionEnd )
1552 // Nothing to select if handles are in the same place.
1556 mEventData->mDecorator->ClearHighlights();
1558 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1559 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1560 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1561 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1562 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1563 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1564 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1566 // TODO: Better algorithm to create the highlight box.
1568 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1569 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1570 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1572 // Swap the indices if the start is greater than the end.
1573 const bool indicesSwapped = selectionStart > selectionEnd;
1575 // Tell the decorator to flip the selection handles if needed.
1576 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1578 if( indicesSwapped )
1580 std::swap( selectionStart, selectionEnd );
1583 // Get the indices to the first and last selected glyphs.
1584 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1585 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1586 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1587 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1589 // Get the lines where the glyphs are laid-out.
1590 const LineRun* lineRun = mVisualModel->mLines.Begin();
1592 LineIndex lineIndex = 0u;
1593 Length numberOfLines = 0u;
1594 mVisualModel->GetNumberOfLines( glyphStart,
1595 1u + glyphEnd - glyphStart,
1598 const LineIndex firstLineIndex = lineIndex;
1600 // Create the structure to store some selection box info.
1601 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1602 selectionBoxLinesInfo.Resize( numberOfLines );
1604 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1605 selectionBoxInfo->minX = MAX_FLOAT;
1606 selectionBoxInfo->maxX = MIN_FLOAT;
1608 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1610 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1611 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1613 lineRun += firstLineIndex;
1615 // The line height is the addition of the line ascender and the line descender.
1616 // However, the line descender has a negative value, hence the subtraction.
1617 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1619 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1621 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1622 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1623 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1625 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1626 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1627 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1629 // Traverse the glyphs.
1630 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1632 const GlyphInfo& glyph = *( glyphsBuffer + index );
1633 const Vector2& position = *( positionsBuffer + index );
1635 if( splitStartGlyph )
1637 // 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.
1639 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1640 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1641 // Get the direction of the character.
1642 CharacterDirection isCurrentRightToLeft = false;
1643 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1645 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1648 // The end point could be in the middle of the ligature.
1649 // Calculate the number of characters selected.
1650 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1652 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1653 const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1654 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1656 // Store the min and max 'x' for each line.
1657 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1658 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1660 mEventData->mDecorator->AddHighlight( xPosition,
1663 yPosition + selectionBoxInfo->lineHeight );
1665 splitStartGlyph = false;
1669 if( splitEndGlyph && ( index == glyphEnd ) )
1671 // 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.
1673 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1674 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1675 // Get the direction of the character.
1676 CharacterDirection isCurrentRightToLeft = false;
1677 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1679 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1682 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1684 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1685 const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1686 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1688 // Store the min and max 'x' for each line.
1689 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1690 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1692 mEventData->mDecorator->AddHighlight( xPosition,
1695 yPosition + selectionBoxInfo->lineHeight );
1697 splitEndGlyph = false;
1701 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1702 const float xPositionAdvance = xPosition + glyph.advance;
1703 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1705 // Store the min and max 'x' for each line.
1706 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1707 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1709 mEventData->mDecorator->AddHighlight( xPosition,
1712 yPosition + selectionBoxInfo->lineHeight );
1714 // Whether to retrieve the next line.
1715 if( index == lastGlyphOfLine )
1717 // Retrieve the next line.
1720 // Get the last glyph of the new line.
1721 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1724 if( lineIndex < firstLineIndex + numberOfLines )
1726 // Get the selection box info for the next line.
1727 const float currentLineOffset = selectionBoxInfo->lineOffset;
1730 selectionBoxInfo->minX = MAX_FLOAT;
1731 selectionBoxInfo->maxX = MIN_FLOAT;
1733 // The line height is the addition of the line ascender and the line descender.
1734 // However, the line descender has a negative value, hence the subtraction.
1735 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1737 // Update the line's vertical offset.
1738 selectionBoxInfo->lineOffset = currentLineOffset + selectionBoxInfo->lineHeight;
1743 // Add extra geometry to 'boxify' the selection.
1745 if( 1u < numberOfLines )
1747 // Boxify the first line.
1748 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1749 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1751 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1752 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1756 // Boxify at the beginning of the line.
1757 mEventData->mDecorator->AddHighlight( 0.f,
1758 firstSelectionBoxLineInfo.lineOffset,
1759 firstSelectionBoxLineInfo.minX,
1760 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1765 // Boxify at the end of the line.
1766 mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1767 firstSelectionBoxLineInfo.lineOffset,
1768 mVisualModel->mControlSize.width,
1769 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1772 // Boxify the central lines.
1773 if( 2u < numberOfLines )
1775 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1776 endIt = selectionBoxLinesInfo.End() - 1u;
1780 const SelectionBoxInfo& info = *it;
1782 mEventData->mDecorator->AddHighlight( 0.f,
1785 info.lineOffset + info.lineHeight );
1787 mEventData->mDecorator->AddHighlight( info.maxX,
1789 mVisualModel->mControlSize.width,
1790 info.lineOffset + info.lineHeight );
1794 // Boxify the last line.
1795 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1796 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1798 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1799 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1803 // Boxify at the beginning of the line.
1804 mEventData->mDecorator->AddHighlight( 0.f,
1805 lastSelectionBoxLineInfo.lineOffset,
1806 lastSelectionBoxLineInfo.minX,
1807 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1812 // Boxify at the end of the line.
1813 mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1814 lastSelectionBoxLineInfo.lineOffset,
1815 mVisualModel->mControlSize.width,
1816 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1821 CursorInfo primaryCursorInfo;
1822 GetCursorPosition( mEventData->mLeftSelectionPosition,
1823 primaryCursorInfo );
1825 CursorInfo secondaryCursorInfo;
1826 GetCursorPosition( mEventData->mRightSelectionPosition,
1827 secondaryCursorInfo );
1829 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1830 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1832 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1834 primaryCursorInfo.lineOffset + mScrollPosition.y,
1835 primaryCursorInfo.lineHeight );
1837 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1838 secondaryPosition.x,
1839 secondaryCursorInfo.lineOffset + mScrollPosition.y,
1840 secondaryCursorInfo.lineHeight );
1842 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1843 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1845 // Set the flag to update the decorator.
1846 mEventData->mDecoratorUpdated = true;
1849 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1851 if( NULL == mEventData )
1853 // Nothing to do if there is no text input.
1857 if( IsShowingPlaceholderText() )
1859 // Nothing to do if there is the place-holder text.
1863 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1864 const Length numberOfLines = mVisualModel->mLines.Count();
1865 if( ( 0 == numberOfGlyphs ) ||
1866 ( 0 == numberOfLines ) )
1868 // Nothing to do if there is no text.
1872 // Find which word was selected
1873 CharacterIndex selectionStart( 0 );
1874 CharacterIndex selectionEnd( 0 );
1875 FindSelectionIndices( mVisualModel,
1882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1884 if( selectionStart == selectionEnd )
1886 ChangeState( EventData::EDITING );
1887 // Nothing to select. i.e. a white space, out of bounds
1891 mEventData->mLeftSelectionPosition = selectionStart;
1892 mEventData->mRightSelectionPosition = selectionEnd;
1895 void Controller::Impl::SetPopupButtons()
1898 * Sets the Popup buttons to be shown depending on State.
1900 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1902 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1905 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1907 if( EventData::SELECTING == mEventData->mState )
1909 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1911 if( !IsClipboardEmpty() )
1913 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1914 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1917 if( !mEventData->mAllTextSelected )
1919 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1922 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1924 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1926 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1929 if( !IsClipboardEmpty() )
1931 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1932 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1935 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1937 if ( !IsClipboardEmpty() )
1939 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1940 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1944 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1947 void Controller::Impl::ChangeState( EventData::State newState )
1949 if( NULL == mEventData )
1951 // Nothing to do if there is no text input.
1955 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1957 if( mEventData->mState != newState )
1959 mEventData->mState = newState;
1961 if( EventData::INACTIVE == mEventData->mState )
1963 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1964 mEventData->mDecorator->StopCursorBlink();
1965 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1966 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1967 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1968 mEventData->mDecorator->SetPopupActive( false );
1969 mEventData->mDecoratorUpdated = true;
1972 else if( EventData::INTERRUPTED == mEventData->mState)
1974 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1975 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1976 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1977 mEventData->mDecorator->SetPopupActive( false );
1978 mEventData->mDecoratorUpdated = true;
1981 else if( EventData::SELECTING == mEventData->mState )
1983 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1984 mEventData->mDecorator->StopCursorBlink();
1985 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1986 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1987 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1988 if( mEventData->mGrabHandlePopupEnabled )
1991 mEventData->mDecorator->SetPopupActive( true );
1993 mEventData->mDecoratorUpdated = true;
1995 else if( EventData::EDITING == mEventData->mState )
1997 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1998 if( mEventData->mCursorBlinkEnabled )
2000 mEventData->mDecorator->StartCursorBlink();
2002 // Grab handle is not shown until a tap is received whilst EDITING
2003 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2004 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2005 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2006 if( mEventData->mGrabHandlePopupEnabled )
2008 mEventData->mDecorator->SetPopupActive( false );
2010 mEventData->mDecoratorUpdated = true;
2013 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2015 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2017 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2018 if( mEventData->mCursorBlinkEnabled )
2020 mEventData->mDecorator->StartCursorBlink();
2022 if( mEventData->mSelectionEnabled )
2024 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2025 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2029 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2031 if( mEventData->mGrabHandlePopupEnabled )
2034 mEventData->mDecorator->SetPopupActive( true );
2037 mEventData->mDecoratorUpdated = true;
2039 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
2041 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2043 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2044 if( mEventData->mCursorBlinkEnabled )
2046 mEventData->mDecorator->StartCursorBlink();
2048 // Grab handle is not shown until a tap is received whilst EDITING
2049 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2050 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2051 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2052 if( mEventData->mGrabHandlePopupEnabled )
2054 mEventData->mDecorator->SetPopupActive( false );
2056 mEventData->mDecoratorUpdated = true;
2059 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
2061 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2062 mEventData->mDecorator->StopCursorBlink();
2063 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2064 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2065 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2066 if( mEventData->mGrabHandlePopupEnabled )
2068 mEventData->mDecorator->SetPopupActive( false );
2070 mEventData->mDecoratorUpdated = true;
2072 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
2074 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2076 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2077 if( mEventData->mCursorBlinkEnabled )
2079 mEventData->mDecorator->StartCursorBlink();
2081 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2082 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2083 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2084 if( mEventData->mGrabHandlePopupEnabled )
2086 mEventData->mDecorator->SetPopupActive( false );
2088 mEventData->mDecoratorUpdated = true;
2090 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2092 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2094 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2095 if( mEventData->mCursorBlinkEnabled )
2097 mEventData->mDecorator->StartCursorBlink();
2100 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2101 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2102 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2104 if( mEventData->mGrabHandlePopupEnabled )
2107 mEventData->mDecorator->SetPopupActive( true );
2110 mEventData->mDecoratorUpdated = true;
2115 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2116 CursorInfo& cursorInfo )
2118 if( !IsShowingRealText() )
2120 // Do not want to use the place-holder text to set the cursor position.
2122 // Use the line's height of the font's family set to set the cursor's size.
2123 // If there is no font's family set, use the default font.
2124 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2126 cursorInfo.lineOffset = 0.f;
2127 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2128 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2130 switch( mLayoutEngine.GetHorizontalAlignment() )
2132 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2134 cursorInfo.primaryPosition.x = 0.f;
2137 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2139 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2142 case LayoutEngine::HORIZONTAL_ALIGN_END:
2144 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2149 // Nothing else to do.
2153 Text::GetCursorPosition( mVisualModel,
2159 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2161 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2163 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2164 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2166 if( 0.f > cursorInfo.primaryPosition.x )
2168 cursorInfo.primaryPosition.x = 0.f;
2171 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2172 if( cursorInfo.primaryPosition.x > edgeWidth )
2174 cursorInfo.primaryPosition.x = edgeWidth;
2179 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2181 if( NULL == mEventData )
2183 // Nothing to do if there is no text input.
2187 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2189 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2190 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2192 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2193 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2195 if( numberOfCharacters > 1u )
2197 const Script script = mLogicalModel->GetScript( index );
2198 if( HasLigatureMustBreak( script ) )
2200 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2201 numberOfCharacters = 1u;
2206 while( 0u == numberOfCharacters )
2209 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2213 if( index < mEventData->mPrimaryCursorPosition )
2215 cursorIndex -= numberOfCharacters;
2219 cursorIndex += numberOfCharacters;
2225 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2227 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2228 if( NULL == mEventData )
2230 // Nothing to do if there is no text input.
2231 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2235 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2237 // Sets the cursor position.
2238 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2241 cursorInfo.primaryCursorHeight,
2242 cursorInfo.lineHeight );
2243 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2245 // Sets the grab handle position.
2246 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2248 cursorInfo.lineOffset + mScrollPosition.y,
2249 cursorInfo.lineHeight );
2251 if( cursorInfo.isSecondaryCursor )
2253 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2254 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2255 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2256 cursorInfo.secondaryCursorHeight,
2257 cursorInfo.lineHeight );
2258 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2261 // Set which cursors are active according the state.
2262 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2264 if( cursorInfo.isSecondaryCursor )
2266 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2270 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2275 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2278 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2281 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2282 const CursorInfo& cursorInfo )
2284 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2285 ( RIGHT_SELECTION_HANDLE != handleType ) )
2290 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2292 // Sets the handle's position.
2293 mEventData->mDecorator->SetPosition( handleType,
2295 cursorInfo.lineOffset + mScrollPosition.y,
2296 cursorInfo.lineHeight );
2298 // If selection handle at start of the text and other at end of the text then all text is selected.
2299 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2300 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2301 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2304 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2306 // Clamp between -space & 0.
2308 if( actualSize.width > mVisualModel->mControlSize.width )
2310 const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2311 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2312 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2314 mEventData->mDecoratorUpdated = true;
2318 mScrollPosition.x = 0.f;
2322 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2324 // Clamp between -space & 0.
2325 if( actualSize.height > mVisualModel->mControlSize.height )
2327 const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2328 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2329 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2331 mEventData->mDecoratorUpdated = true;
2335 mScrollPosition.y = 0.f;
2339 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2341 const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2343 // position is in actor's coords.
2344 const float positionEnd = position.x + cursorWidth;
2346 // Transform the position to decorator coords.
2347 const float decoratorPositionBegin = position.x + mScrollPosition.x;
2348 const float decoratorPositionEnd = positionEnd + mScrollPosition.x;
2350 if( decoratorPositionBegin < 0.f )
2352 mScrollPosition.x = -position.x;
2354 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2356 mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd;
2360 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2362 // Get the current cursor position in decorator coords.
2363 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2365 // Calculate the offset to match the cursor position before the character was deleted.
2366 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2368 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2370 // Makes the new cursor position visible if needed.
2371 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2374 void Controller::Impl::RequestRelayout()
2376 mControlInterface.RequestTextRelayout();
2381 } // namespace Toolkit