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 // Scroll after delete.
1490 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1492 mEventData->mDecoratorUpdated = true;
1496 void Controller::Impl::ShowClipboard()
1500 mClipboard.ShowClipboard();
1504 void Controller::Impl::HideClipboard()
1506 if( mClipboard && mClipboardHideEnabled )
1508 mClipboard.HideClipboard();
1512 void Controller::Impl::SetClipboardHideEnable(bool enable)
1514 mClipboardHideEnabled = enable;
1517 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1519 //Send string to clipboard
1520 return ( mClipboard && mClipboard.SetItem( source ) );
1523 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1525 std::string selectedText;
1526 RetrieveSelection( selectedText, deleteAfterSending );
1527 CopyStringToClipboard( selectedText );
1528 ChangeState( EventData::EDITING );
1531 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1535 retrievedString = mClipboard.GetItem( itemIndex );
1539 void Controller::Impl::RepositionSelectionHandles()
1541 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1542 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1544 if( selectionStart == selectionEnd )
1546 // Nothing to select if handles are in the same place.
1550 mEventData->mDecorator->ClearHighlights();
1552 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1553 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1554 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1555 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1556 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1557 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1558 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1560 // TODO: Better algorithm to create the highlight box.
1562 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1563 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1564 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1566 // Swap the indices if the start is greater than the end.
1567 const bool indicesSwapped = selectionStart > selectionEnd;
1569 // Tell the decorator to flip the selection handles if needed.
1570 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1572 if( indicesSwapped )
1574 std::swap( selectionStart, selectionEnd );
1577 // Get the indices to the first and last selected glyphs.
1578 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1579 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1580 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1581 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1583 // Get the lines where the glyphs are laid-out.
1584 const LineRun* lineRun = mVisualModel->mLines.Begin();
1586 LineIndex lineIndex = 0u;
1587 Length numberOfLines = 0u;
1588 mVisualModel->GetNumberOfLines( glyphStart,
1589 1u + glyphEnd - glyphStart,
1592 const LineIndex firstLineIndex = lineIndex;
1594 // Create the structure to store some selection box info.
1595 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1596 selectionBoxLinesInfo.Resize( numberOfLines );
1598 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1599 selectionBoxInfo->minX = MAX_FLOAT;
1600 selectionBoxInfo->maxX = MIN_FLOAT;
1602 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1604 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1605 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1607 lineRun += firstLineIndex;
1609 // The line height is the addition of the line ascender and the line descender.
1610 // However, the line descender has a negative value, hence the subtraction.
1611 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1613 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1615 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1616 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1617 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1619 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1620 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1621 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1623 // Traverse the glyphs.
1624 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1626 const GlyphInfo& glyph = *( glyphsBuffer + index );
1627 const Vector2& position = *( positionsBuffer + index );
1629 if( splitStartGlyph )
1631 // 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.
1633 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1634 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1635 // Get the direction of the character.
1636 CharacterDirection isCurrentRightToLeft = false;
1637 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1639 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1642 // The end point could be in the middle of the ligature.
1643 // Calculate the number of characters selected.
1644 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1646 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1647 const float xPositionAdvance = xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1648 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1650 // Store the min and max 'x' for each line.
1651 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1652 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1654 mEventData->mDecorator->AddHighlight( xPosition,
1657 yPosition + selectionBoxInfo->lineHeight );
1659 splitStartGlyph = false;
1663 if( splitEndGlyph && ( index == glyphEnd ) )
1665 // 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.
1667 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1668 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1669 // Get the direction of the character.
1670 CharacterDirection isCurrentRightToLeft = false;
1671 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1673 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1676 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1678 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1679 const float xPositionAdvance = xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1680 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1682 // Store the min and max 'x' for each line.
1683 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1684 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1686 mEventData->mDecorator->AddHighlight( xPosition,
1689 yPosition + selectionBoxInfo->lineHeight );
1691 splitEndGlyph = false;
1695 const float xPosition = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1696 const float xPositionAdvance = xPosition + glyph.advance;
1697 const float yPosition = selectionBoxInfo->lineOffset + mScrollPosition.y;
1699 // Store the min and max 'x' for each line.
1700 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, xPosition );
1701 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, xPositionAdvance );
1703 mEventData->mDecorator->AddHighlight( xPosition,
1706 yPosition + selectionBoxInfo->lineHeight );
1708 // Whether to retrieve the next line.
1709 if( index == lastGlyphOfLine )
1711 // Retrieve the next line.
1714 // Get the last glyph of the new line.
1715 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1718 if( lineIndex < firstLineIndex + numberOfLines )
1720 // Get the selection box info for the next line.
1721 const float currentLineOffset = selectionBoxInfo->lineOffset;
1724 selectionBoxInfo->minX = MAX_FLOAT;
1725 selectionBoxInfo->maxX = MIN_FLOAT;
1727 // The line height is the addition of the line ascender and the line descender.
1728 // However, the line descender has a negative value, hence the subtraction.
1729 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1731 // Update the line's vertical offset.
1732 selectionBoxInfo->lineOffset = currentLineOffset + selectionBoxInfo->lineHeight;
1737 // Add extra geometry to 'boxify' the selection.
1739 if( 1u < numberOfLines )
1741 // Boxify the first line.
1742 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1743 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1745 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1746 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1750 // Boxify at the beginning of the line.
1751 mEventData->mDecorator->AddHighlight( 0.f,
1752 firstSelectionBoxLineInfo.lineOffset,
1753 firstSelectionBoxLineInfo.minX,
1754 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1759 // Boxify at the end of the line.
1760 mEventData->mDecorator->AddHighlight( firstSelectionBoxLineInfo.maxX,
1761 firstSelectionBoxLineInfo.lineOffset,
1762 mVisualModel->mControlSize.width,
1763 firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight );
1766 // Boxify the central lines.
1767 if( 2u < numberOfLines )
1769 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1770 endIt = selectionBoxLinesInfo.End() - 1u;
1774 const SelectionBoxInfo& info = *it;
1776 mEventData->mDecorator->AddHighlight( 0.f,
1779 info.lineOffset + info.lineHeight );
1781 mEventData->mDecorator->AddHighlight( info.maxX,
1783 mVisualModel->mControlSize.width,
1784 info.lineOffset + info.lineHeight );
1788 // Boxify the last line.
1789 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
1790 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
1792 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
1793 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
1797 // Boxify at the beginning of the line.
1798 mEventData->mDecorator->AddHighlight( 0.f,
1799 lastSelectionBoxLineInfo.lineOffset,
1800 lastSelectionBoxLineInfo.minX,
1801 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1806 // Boxify at the end of the line.
1807 mEventData->mDecorator->AddHighlight( lastSelectionBoxLineInfo.maxX,
1808 lastSelectionBoxLineInfo.lineOffset,
1809 mVisualModel->mControlSize.width,
1810 lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight );
1815 CursorInfo primaryCursorInfo;
1816 GetCursorPosition( mEventData->mLeftSelectionPosition,
1817 primaryCursorInfo );
1819 CursorInfo secondaryCursorInfo;
1820 GetCursorPosition( mEventData->mRightSelectionPosition,
1821 secondaryCursorInfo );
1823 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
1824 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
1826 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
1828 primaryCursorInfo.lineOffset + mScrollPosition.y,
1829 primaryCursorInfo.lineHeight );
1831 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
1832 secondaryPosition.x,
1833 secondaryCursorInfo.lineOffset + mScrollPosition.y,
1834 secondaryCursorInfo.lineHeight );
1836 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
1837 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1839 // Set the flag to update the decorator.
1840 mEventData->mDecoratorUpdated = true;
1843 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
1845 if( NULL == mEventData )
1847 // Nothing to do if there is no text input.
1851 if( IsShowingPlaceholderText() )
1853 // Nothing to do if there is the place-holder text.
1857 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1858 const Length numberOfLines = mVisualModel->mLines.Count();
1859 if( ( 0 == numberOfGlyphs ) ||
1860 ( 0 == numberOfLines ) )
1862 // Nothing to do if there is no text.
1866 // Find which word was selected
1867 CharacterIndex selectionStart( 0 );
1868 CharacterIndex selectionEnd( 0 );
1869 FindSelectionIndices( mVisualModel,
1876 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1878 if( selectionStart == selectionEnd )
1880 ChangeState( EventData::EDITING );
1881 // Nothing to select. i.e. a white space, out of bounds
1885 mEventData->mLeftSelectionPosition = selectionStart;
1886 mEventData->mRightSelectionPosition = selectionEnd;
1889 void Controller::Impl::SetPopupButtons()
1892 * Sets the Popup buttons to be shown depending on State.
1894 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1896 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1899 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1901 if( EventData::SELECTING == mEventData->mState )
1903 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1905 if( !IsClipboardEmpty() )
1907 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1908 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1911 if( !mEventData->mAllTextSelected )
1913 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1916 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1918 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
1920 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1923 if( !IsClipboardEmpty() )
1925 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1926 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1929 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
1931 if ( !IsClipboardEmpty() )
1933 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1934 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1938 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1941 void Controller::Impl::ChangeState( EventData::State newState )
1943 if( NULL == mEventData )
1945 // Nothing to do if there is no text input.
1949 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
1951 if( mEventData->mState != newState )
1953 mEventData->mState = newState;
1955 if( EventData::INACTIVE == mEventData->mState )
1957 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1958 mEventData->mDecorator->StopCursorBlink();
1959 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1960 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1961 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1962 mEventData->mDecorator->SetPopupActive( false );
1963 mEventData->mDecoratorUpdated = true;
1966 else if( EventData::INTERRUPTED == mEventData->mState)
1968 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1969 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1970 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1971 mEventData->mDecorator->SetPopupActive( false );
1972 mEventData->mDecoratorUpdated = true;
1975 else if( EventData::SELECTING == mEventData->mState )
1977 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1978 mEventData->mDecorator->StopCursorBlink();
1979 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1980 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1981 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1982 if( mEventData->mGrabHandlePopupEnabled )
1985 mEventData->mDecorator->SetPopupActive( true );
1987 mEventData->mDecoratorUpdated = true;
1989 else if( EventData::EDITING == mEventData->mState )
1991 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1992 if( mEventData->mCursorBlinkEnabled )
1994 mEventData->mDecorator->StartCursorBlink();
1996 // Grab handle is not shown until a tap is received whilst EDITING
1997 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1998 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1999 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2000 if( mEventData->mGrabHandlePopupEnabled )
2002 mEventData->mDecorator->SetPopupActive( false );
2004 mEventData->mDecoratorUpdated = true;
2007 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2009 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2011 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2012 if( mEventData->mCursorBlinkEnabled )
2014 mEventData->mDecorator->StartCursorBlink();
2016 if( mEventData->mSelectionEnabled )
2018 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2019 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2023 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2025 if( mEventData->mGrabHandlePopupEnabled )
2028 mEventData->mDecorator->SetPopupActive( true );
2031 mEventData->mDecoratorUpdated = true;
2033 else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
2035 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2037 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2038 if( mEventData->mCursorBlinkEnabled )
2040 mEventData->mDecorator->StartCursorBlink();
2042 // Grab handle is not shown until a tap is received whilst EDITING
2043 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2044 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2045 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2046 if( mEventData->mGrabHandlePopupEnabled )
2048 mEventData->mDecorator->SetPopupActive( false );
2050 mEventData->mDecoratorUpdated = true;
2053 else if( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
2055 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2056 mEventData->mDecorator->StopCursorBlink();
2057 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2058 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2059 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2060 if( mEventData->mGrabHandlePopupEnabled )
2062 mEventData->mDecorator->SetPopupActive( false );
2064 mEventData->mDecoratorUpdated = true;
2066 else if( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
2068 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2070 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2071 if( mEventData->mCursorBlinkEnabled )
2073 mEventData->mDecorator->StartCursorBlink();
2075 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2076 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2077 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2078 if( mEventData->mGrabHandlePopupEnabled )
2080 mEventData->mDecorator->SetPopupActive( false );
2082 mEventData->mDecoratorUpdated = true;
2084 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2086 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2088 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2089 if( mEventData->mCursorBlinkEnabled )
2091 mEventData->mDecorator->StartCursorBlink();
2094 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2095 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2096 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2098 if( mEventData->mGrabHandlePopupEnabled )
2101 mEventData->mDecorator->SetPopupActive( true );
2104 mEventData->mDecoratorUpdated = true;
2109 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2110 CursorInfo& cursorInfo )
2112 if( !IsShowingRealText() )
2114 // Do not want to use the place-holder text to set the cursor position.
2116 // Use the line's height of the font's family set to set the cursor's size.
2117 // If there is no font's family set, use the default font.
2118 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2120 cursorInfo.lineOffset = 0.f;
2121 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2122 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2124 switch( mLayoutEngine.GetHorizontalAlignment() )
2126 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2128 cursorInfo.primaryPosition.x = 0.f;
2131 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2133 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2136 case LayoutEngine::HORIZONTAL_ALIGN_END:
2138 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2143 // Nothing else to do.
2147 Text::GetCursorPosition( mVisualModel,
2153 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2155 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2157 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2158 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2160 if( 0.f > cursorInfo.primaryPosition.x )
2162 cursorInfo.primaryPosition.x = 0.f;
2165 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2166 if( cursorInfo.primaryPosition.x > edgeWidth )
2168 cursorInfo.primaryPosition.x = edgeWidth;
2173 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2175 if( NULL == mEventData )
2177 // Nothing to do if there is no text input.
2181 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2183 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2184 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2186 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2187 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2189 if( numberOfCharacters > 1u )
2191 const Script script = mLogicalModel->GetScript( index );
2192 if( HasLigatureMustBreak( script ) )
2194 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2195 numberOfCharacters = 1u;
2200 while( 0u == numberOfCharacters )
2203 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2207 if( index < mEventData->mPrimaryCursorPosition )
2209 cursorIndex -= numberOfCharacters;
2213 cursorIndex += numberOfCharacters;
2219 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2221 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2222 if( NULL == mEventData )
2224 // Nothing to do if there is no text input.
2225 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2229 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2231 // Sets the cursor position.
2232 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2235 cursorInfo.primaryCursorHeight,
2236 cursorInfo.lineHeight );
2237 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2239 // Sets the grab handle position.
2240 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2242 cursorInfo.lineOffset + mScrollPosition.y,
2243 cursorInfo.lineHeight );
2245 if( cursorInfo.isSecondaryCursor )
2247 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2248 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2249 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2250 cursorInfo.secondaryCursorHeight,
2251 cursorInfo.lineHeight );
2252 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2255 // Set which cursors are active according the state.
2256 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2258 if( cursorInfo.isSecondaryCursor )
2260 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2264 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2269 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2272 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2275 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2276 const CursorInfo& cursorInfo )
2278 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2279 ( RIGHT_SELECTION_HANDLE != handleType ) )
2284 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2286 // Sets the handle's position.
2287 mEventData->mDecorator->SetPosition( handleType,
2289 cursorInfo.lineOffset + mScrollPosition.y,
2290 cursorInfo.lineHeight );
2292 // If selection handle at start of the text and other at end of the text then all text is selected.
2293 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2294 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2295 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2298 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
2300 // Clamp between -space & 0.
2302 if( actualSize.width > mVisualModel->mControlSize.width )
2304 const float space = ( actualSize.width - mVisualModel->mControlSize.width );
2305 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2306 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2308 mEventData->mDecoratorUpdated = true;
2312 mScrollPosition.x = 0.f;
2316 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
2318 // Clamp between -space & 0.
2319 if( actualSize.height > mVisualModel->mControlSize.height )
2321 const float space = ( actualSize.height - mVisualModel->mControlSize.height );
2322 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2323 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2325 mEventData->mDecoratorUpdated = true;
2329 mScrollPosition.y = 0.f;
2333 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
2335 const float cursorWidth = mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f;
2337 // position is in actor's coords.
2338 const float positionEnd = position.x + cursorWidth;
2340 // Transform the position to decorator coords.
2341 const float decoratorPositionBegin = position.x + mScrollPosition.x;
2342 const float decoratorPositionEnd = positionEnd + mScrollPosition.x;
2344 if( decoratorPositionBegin < 0.f )
2346 mScrollPosition.x = -position.x;
2348 else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
2350 mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd;
2354 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2356 // Get the current cursor position in decorator coords.
2357 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2359 // Calculate the offset to match the cursor position before the character was deleted.
2360 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2362 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2364 // Makes the new cursor position visible if needed.
2365 ScrollToMakePositionVisible( cursorInfo.primaryPosition );
2368 void Controller::Impl::RequestRelayout()
2370 mControlInterface.RequestTextRelayout();
2375 } // namespace Toolkit