2 * Copyright (c) 2017 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-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
58 const uint32_t STAR = 0x2A;
71 EventData::EventData( DecoratorPtr decorator )
72 : mDecorator( decorator ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mIsShowingPlaceholderText( false ),
88 mPreEditFlag( false ),
89 mDecoratorUpdated( false ),
90 mCursorBlinkEnabled( true ),
91 mGrabHandleEnabled( true ),
92 mGrabHandlePopupEnabled( true ),
93 mSelectionEnabled( true ),
94 mUpdateCursorHookPosition( false ),
95 mUpdateCursorPosition( false ),
96 mUpdateGrabHandlePosition( false ),
97 mUpdateLeftSelectionPosition( false ),
98 mUpdateRightSelectionPosition( false ),
99 mIsLeftHandleSelected( false ),
100 mUpdateHighlightBox( false ),
101 mScrollAfterUpdatePosition( false ),
102 mScrollAfterDelete( false ),
103 mAllTextSelected( false ),
104 mUpdateInputStyle( false ),
105 mPasswordInput( false )
107 mImfManager = ImfManager::Get();
110 EventData::~EventData()
113 bool Controller::Impl::ProcessInputEvents()
115 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
116 if( NULL == mEventData )
118 // Nothing to do if there is no text input.
119 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
123 if( mEventData->mDecorator )
125 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
126 iter != mEventData->mEventQueue.end();
131 case Event::CURSOR_KEY_EVENT:
133 OnCursorKeyEvent( *iter );
136 case Event::TAP_EVENT:
141 case Event::LONG_PRESS_EVENT:
143 OnLongPressEvent( *iter );
146 case Event::PAN_EVENT:
151 case Event::GRAB_HANDLE_EVENT:
152 case Event::LEFT_SELECTION_HANDLE_EVENT:
153 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
155 OnHandleEvent( *iter );
160 OnSelectEvent( *iter );
163 case Event::SELECT_ALL:
172 if( mEventData->mUpdateCursorPosition ||
173 mEventData->mUpdateHighlightBox )
178 // The cursor must also be repositioned after inserts into the model
179 if( mEventData->mUpdateCursorPosition )
181 // Updates the cursor position and scrolls the text to make it visible.
182 CursorInfo cursorInfo;
183 // Calculate the cursor position from the new cursor index.
184 GetCursorPosition( mEventData->mPrimaryCursorPosition,
187 if( mEventData->mUpdateCursorHookPosition )
189 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
190 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
191 mEventData->mUpdateCursorHookPosition = false;
194 // Scroll first the text after delete ...
195 if( mEventData->mScrollAfterDelete )
197 ScrollTextToMatchCursor( cursorInfo );
200 // ... then, text can be scrolled to make the cursor visible.
201 if( mEventData->mScrollAfterUpdatePosition )
203 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
204 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
206 mEventData->mScrollAfterUpdatePosition = false;
207 mEventData->mScrollAfterDelete = false;
209 UpdateCursorPosition( cursorInfo );
211 mEventData->mDecoratorUpdated = true;
212 mEventData->mUpdateCursorPosition = false;
213 mEventData->mUpdateGrabHandlePosition = false;
217 CursorInfo leftHandleInfo;
218 CursorInfo rightHandleInfo;
220 if( mEventData->mUpdateHighlightBox )
222 GetCursorPosition( mEventData->mLeftSelectionPosition,
225 GetCursorPosition( mEventData->mRightSelectionPosition,
228 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
230 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
232 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
233 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
237 if( mEventData->mUpdateLeftSelectionPosition )
239 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
243 mEventData->mDecoratorUpdated = true;
244 mEventData->mUpdateLeftSelectionPosition = false;
247 if( mEventData->mUpdateRightSelectionPosition )
249 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
253 mEventData->mDecoratorUpdated = true;
254 mEventData->mUpdateRightSelectionPosition = false;
257 if( mEventData->mUpdateHighlightBox )
259 RepositionSelectionHandles();
261 mEventData->mUpdateLeftSelectionPosition = false;
262 mEventData->mUpdateRightSelectionPosition = false;
263 mEventData->mUpdateHighlightBox = false;
266 mEventData->mScrollAfterUpdatePosition = false;
269 if( mEventData->mUpdateInputStyle )
271 // Keep a copy of the current input style.
272 InputStyle currentInputStyle;
273 currentInputStyle.Copy( mEventData->mInputStyle );
275 // Set the default style first.
276 RetrieveDefaultInputStyle( mEventData->mInputStyle );
278 // Get the character index from the cursor index.
279 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
281 // Retrieve the style from the style runs stored in the logical model.
282 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
284 // Compare if the input style has changed.
285 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
287 if( hasInputStyleChanged )
289 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
290 // Queue the input style changed signal.
291 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
294 mEventData->mUpdateInputStyle = false;
297 mEventData->mEventQueue.clear();
299 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
301 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
302 mEventData->mDecoratorUpdated = false;
304 return decoratorUpdated;
307 void Controller::Impl::NotifyImfManager()
309 if( mEventData && mEventData->mImfManager )
311 CharacterIndex cursorPosition = GetLogicalCursorPosition();
313 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
315 // Update the cursor position by removing the initial white spaces.
316 if( cursorPosition < numberOfWhiteSpaces )
322 cursorPosition -= numberOfWhiteSpaces;
325 mEventData->mImfManager.SetCursorPosition( cursorPosition );
326 mEventData->mImfManager.NotifyCursorPosition();
330 void Controller::Impl::NotifyImfMultiLineStatus()
334 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
335 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
339 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
341 CharacterIndex cursorPosition = 0u;
345 if( ( EventData::SELECTING == mEventData->mState ) ||
346 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
348 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
352 cursorPosition = mEventData->mPrimaryCursorPosition;
356 return cursorPosition;
359 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
361 Length numberOfWhiteSpaces = 0u;
363 // Get the buffer to the text.
364 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
366 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
367 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
369 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
375 return numberOfWhiteSpaces;
378 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
380 // Get the total number of characters.
381 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
383 // Retrieve the text.
384 if( 0u != numberOfCharacters )
386 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
390 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
392 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
393 mTextUpdateInfo.mStartGlyphIndex = 0u;
394 mTextUpdateInfo.mStartLineIndex = 0u;
395 numberOfCharacters = 0u;
397 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
398 if( 0u == numberOfParagraphs )
400 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
401 numberOfCharacters = 0u;
403 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
405 // Nothing else to do if there are no paragraphs.
409 // Find the paragraphs to be updated.
410 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
411 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
413 // Text is being added at the end of the current text.
414 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
416 // Text is being added in a new paragraph after the last character of the text.
417 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
418 numberOfCharacters = 0u;
419 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
421 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
422 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
424 // Nothing else to do;
428 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
432 Length numberOfCharactersToUpdate = 0u;
433 if( mTextUpdateInfo.mFullRelayoutNeeded )
435 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
439 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
441 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
442 numberOfCharactersToUpdate,
443 paragraphsToBeUpdated );
446 if( 0u != paragraphsToBeUpdated.Count() )
448 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
449 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
450 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
452 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
453 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
455 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
456 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
457 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
458 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
460 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
461 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
463 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
467 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
471 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
472 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
475 void Controller::Impl::ClearFullModelData( OperationsMask operations )
477 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
479 mModel->mLogicalModel->mLineBreakInfo.Clear();
480 mModel->mLogicalModel->mParagraphInfo.Clear();
483 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
485 mModel->mLogicalModel->mLineBreakInfo.Clear();
488 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
490 mModel->mLogicalModel->mScriptRuns.Clear();
493 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
495 mModel->mLogicalModel->mFontRuns.Clear();
498 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
500 if( NO_OPERATION != ( BIDI_INFO & operations ) )
502 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
503 mModel->mLogicalModel->mCharacterDirections.Clear();
506 if( NO_OPERATION != ( REORDER & operations ) )
508 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
509 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
510 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
514 BidirectionalLineInfoRun& bidiLineInfo = *it;
516 free( bidiLineInfo.visualToLogicalMap );
517 bidiLineInfo.visualToLogicalMap = NULL;
519 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
523 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
525 mModel->mVisualModel->mGlyphs.Clear();
526 mModel->mVisualModel->mGlyphsToCharacters.Clear();
527 mModel->mVisualModel->mCharactersToGlyph.Clear();
528 mModel->mVisualModel->mCharactersPerGlyph.Clear();
529 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
530 mModel->mVisualModel->mGlyphPositions.Clear();
533 if( NO_OPERATION != ( LAYOUT & operations ) )
535 mModel->mVisualModel->mLines.Clear();
538 if( NO_OPERATION != ( COLOR & operations ) )
540 mModel->mVisualModel->mColorIndices.Clear();
544 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
546 const CharacterIndex endIndexPlusOne = endIndex + 1u;
548 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
550 // Clear the line break info.
551 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
553 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
554 lineBreakInfoBuffer + endIndexPlusOne );
556 // Clear the paragraphs.
557 ClearCharacterRuns( startIndex,
559 mModel->mLogicalModel->mParagraphInfo );
562 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
564 // Clear the word break info.
565 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
567 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
568 wordBreakInfoBuffer + endIndexPlusOne );
571 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
573 // Clear the scripts.
574 ClearCharacterRuns( startIndex,
576 mModel->mLogicalModel->mScriptRuns );
579 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
582 ClearCharacterRuns( startIndex,
584 mModel->mLogicalModel->mFontRuns );
587 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
589 if( NO_OPERATION != ( BIDI_INFO & operations ) )
591 // Clear the bidirectional paragraph info.
592 ClearCharacterRuns( startIndex,
594 mModel->mLogicalModel->mBidirectionalParagraphInfo );
596 // Clear the character's directions.
597 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
599 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
600 characterDirectionsBuffer + endIndexPlusOne );
603 if( NO_OPERATION != ( REORDER & operations ) )
605 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
606 uint32_t endRemoveIndex = startRemoveIndex;
607 ClearCharacterRuns( startIndex,
609 mModel->mLogicalModel->mBidirectionalLineInfo,
613 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
615 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
616 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
617 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
621 BidirectionalLineInfoRun& bidiLineInfo = *it;
623 free( bidiLineInfo.visualToLogicalMap );
624 bidiLineInfo.visualToLogicalMap = NULL;
627 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
628 bidirectionalLineInfoBuffer + endRemoveIndex );
633 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
635 const CharacterIndex endIndexPlusOne = endIndex + 1u;
636 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
638 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
639 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
640 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
642 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
643 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
645 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
647 // Update the character to glyph indices.
648 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
649 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
653 CharacterIndex& index = *it;
654 index -= numberOfGlyphsRemoved;
657 // Clear the character to glyph conversion table.
658 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
659 charactersToGlyphBuffer + endIndexPlusOne );
661 // Clear the glyphs per character table.
662 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
663 glyphsPerCharacterBuffer + endIndexPlusOne );
665 // Clear the glyphs buffer.
666 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
667 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
668 glyphsBuffer + endGlyphIndexPlusOne );
670 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
672 // Update the glyph to character indices.
673 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
674 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
678 CharacterIndex& index = *it;
679 index -= numberOfCharactersRemoved;
682 // Clear the glyphs to characters buffer.
683 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
684 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
686 // Clear the characters per glyph buffer.
687 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
688 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
689 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
691 // Clear the positions buffer.
692 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
693 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
694 positionsBuffer + endGlyphIndexPlusOne );
697 if( NO_OPERATION != ( LAYOUT & operations ) )
700 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
701 uint32_t endRemoveIndex = startRemoveIndex;
702 ClearCharacterRuns( startIndex,
704 mModel->mVisualModel->mLines,
708 // Will update the glyph runs.
709 startRemoveIndex = mModel->mVisualModel->mLines.Count();
710 endRemoveIndex = startRemoveIndex;
711 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
712 endGlyphIndexPlusOne - 1u,
713 mModel->mVisualModel->mLines,
717 // Set the line index from where to insert the new laid-out lines.
718 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
720 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
721 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
722 linesBuffer + endRemoveIndex );
725 if( NO_OPERATION != ( COLOR & operations ) )
727 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
729 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
730 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
731 colorIndexBuffer + endGlyphIndexPlusOne );
736 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
738 if( mTextUpdateInfo.mClearAll ||
739 ( ( 0u == startIndex ) &&
740 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
742 ClearFullModelData( operations );
746 // Clear the model data related with characters.
747 ClearCharacterModelData( startIndex, endIndex, operations );
749 // Clear the model data related with glyphs.
750 ClearGlyphModelData( startIndex, endIndex, operations );
753 // The estimated number of lines. Used to avoid reallocations when layouting.
754 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
756 mModel->mVisualModel->ClearCaches();
759 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
761 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
763 // Calculate the operations to be done.
764 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
766 if( NO_OPERATION == operations )
768 // Nothing to do if no operations are pending and required.
772 Vector<Character> utf32CharactersStar;
773 const Length characterCount = mModel->mLogicalModel->mText.Count();
774 const bool isPasswordInput = ( mEventData != NULL && mEventData->mPasswordInput &&
775 !mEventData->mIsShowingPlaceholderText && characterCount > 0 );
779 utf32CharactersStar.Resize( characterCount );
781 uint32_t* begin = utf32CharactersStar.Begin();
782 uint32_t* end = begin + characterCount;
783 while ( begin < end )
789 Vector<Character>& utf32Characters = isPasswordInput ? utf32CharactersStar : mModel->mLogicalModel->mText;
790 const Length numberOfCharacters = utf32Characters.Count();
792 // Index to the first character of the first paragraph to be updated.
793 CharacterIndex startIndex = 0u;
794 // Number of characters of the paragraphs to be removed.
795 Length paragraphCharacters = 0u;
797 CalculateTextUpdateIndices( paragraphCharacters );
798 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
800 if( mTextUpdateInfo.mClearAll ||
801 ( 0u != paragraphCharacters ) )
803 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
806 mTextUpdateInfo.mClearAll = false;
808 // Whether the model is updated.
809 bool updated = false;
811 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
812 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
814 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
816 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
817 // calculate the bidirectional info for each 'paragraph'.
818 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
819 // is not shaped together).
820 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
822 SetLineBreakInfo( utf32Characters,
824 requestedNumberOfCharacters,
827 // Create the paragraph info.
828 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
829 requestedNumberOfCharacters );
833 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
834 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
836 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
837 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
839 SetWordBreakInfo( utf32Characters,
841 requestedNumberOfCharacters,
846 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
847 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
849 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
850 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
852 if( getScripts || validateFonts )
854 // Validates the fonts assigned by the application or assigns default ones.
855 // It makes sure all the characters are going to be rendered by the correct font.
856 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
860 // Retrieves the scripts used in the text.
861 multilanguageSupport.SetScripts( utf32Characters,
863 requestedNumberOfCharacters,
869 // Validate the fonts set through the mark-up string.
870 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
872 // Get the default font's description.
873 TextAbstraction::FontDescription defaultFontDescription;
874 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
875 if( NULL != mFontDefaults )
877 defaultFontDescription = mFontDefaults->mFontDescription;
878 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
881 // Validates the fonts. If there is a character with no assigned font it sets a default one.
882 // After this call, fonts are validated.
883 multilanguageSupport.ValidateFonts( utf32Characters,
886 defaultFontDescription,
889 requestedNumberOfCharacters,
895 Vector<Character> mirroredUtf32Characters;
896 bool textMirrored = false;
897 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
898 if( NO_OPERATION != ( BIDI_INFO & operations ) )
900 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
901 bidirectionalInfo.Reserve( numberOfParagraphs );
903 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
904 SetBidirectionalInfo( utf32Characters,
908 requestedNumberOfCharacters,
911 if( 0u != bidirectionalInfo.Count() )
913 // Only set the character directions if there is right to left characters.
914 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
915 GetCharactersDirection( bidirectionalInfo,
918 requestedNumberOfCharacters,
921 // This paragraph has right to left text. Some characters may need to be mirrored.
922 // TODO: consider if the mirrored string can be stored as well.
924 textMirrored = GetMirroredText( utf32Characters,
928 requestedNumberOfCharacters,
929 mirroredUtf32Characters );
933 // There is no right to left characters. Clear the directions vector.
934 mModel->mLogicalModel->mCharacterDirections.Clear();
939 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
940 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
941 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
942 Vector<GlyphIndex> newParagraphGlyphs;
943 newParagraphGlyphs.Reserve( numberOfParagraphs );
945 const Length currentNumberOfGlyphs = glyphs.Count();
946 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
948 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
950 ShapeText( textToShape,
955 mTextUpdateInfo.mStartGlyphIndex,
956 requestedNumberOfCharacters,
958 glyphsToCharactersMap,
960 newParagraphGlyphs );
962 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
963 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
964 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
968 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
970 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
972 GlyphInfo* glyphsBuffer = glyphs.Begin();
973 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
975 // Update the width and advance of all new paragraph characters.
976 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
978 const GlyphIndex index = *it;
979 GlyphInfo& glyph = *( glyphsBuffer + index );
981 glyph.xBearing = 0.f;
988 if( NO_OPERATION != ( COLOR & operations ) )
990 // Set the color runs in glyphs.
991 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
992 mModel->mVisualModel->mCharactersToGlyph,
993 mModel->mVisualModel->mGlyphsPerCharacter,
995 mTextUpdateInfo.mStartGlyphIndex,
996 requestedNumberOfCharacters,
997 mModel->mVisualModel->mColors,
998 mModel->mVisualModel->mColorIndices );
1003 if( ( NULL != mEventData ) &&
1004 mEventData->mPreEditFlag &&
1005 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1007 // Add the underline for the pre-edit text.
1008 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1009 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1011 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1012 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1013 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1014 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1016 GlyphRun underlineRun;
1017 underlineRun.glyphIndex = glyphStart;
1018 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1020 // TODO: At the moment the underline runs are only for pre-edit.
1021 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1024 // The estimated number of lines. Used to avoid reallocations when layouting.
1025 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1027 // Set the previous number of characters for the next time the text is updated.
1028 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1033 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1035 // Sets the default text's color.
1036 inputStyle.textColor = mTextColor;
1037 inputStyle.isDefaultColor = true;
1039 inputStyle.familyName.clear();
1040 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1041 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1042 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1043 inputStyle.size = 0.f;
1045 inputStyle.lineSpacing = 0.f;
1047 inputStyle.underlineProperties.clear();
1048 inputStyle.shadowProperties.clear();
1049 inputStyle.embossProperties.clear();
1050 inputStyle.outlineProperties.clear();
1052 inputStyle.isFamilyDefined = false;
1053 inputStyle.isWeightDefined = false;
1054 inputStyle.isWidthDefined = false;
1055 inputStyle.isSlantDefined = false;
1056 inputStyle.isSizeDefined = false;
1058 inputStyle.isLineSpacingDefined = false;
1060 inputStyle.isUnderlineDefined = false;
1061 inputStyle.isShadowDefined = false;
1062 inputStyle.isEmbossDefined = false;
1063 inputStyle.isOutlineDefined = false;
1065 // Sets the default font's family name, weight, width, slant and size.
1068 if( mFontDefaults->familyDefined )
1070 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1071 inputStyle.isFamilyDefined = true;
1074 if( mFontDefaults->weightDefined )
1076 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1077 inputStyle.isWeightDefined = true;
1080 if( mFontDefaults->widthDefined )
1082 inputStyle.width = mFontDefaults->mFontDescription.width;
1083 inputStyle.isWidthDefined = true;
1086 if( mFontDefaults->slantDefined )
1088 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1089 inputStyle.isSlantDefined = true;
1092 if( mFontDefaults->sizeDefined )
1094 inputStyle.size = mFontDefaults->mDefaultPointSize;
1095 inputStyle.isSizeDefined = true;
1100 float Controller::Impl::GetDefaultFontLineHeight()
1102 FontId defaultFontId = 0u;
1103 if( NULL == mFontDefaults )
1105 TextAbstraction::FontDescription fontDescription;
1106 defaultFontId = mFontClient.GetFontId( fontDescription );
1110 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1113 Text::FontMetrics fontMetrics;
1114 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1116 return( fontMetrics.ascender - fontMetrics.descender );
1119 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1121 if( NULL == mEventData )
1123 // Nothing to do if there is no text input.
1127 int keyCode = event.p1.mInt;
1129 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1131 if( mEventData->mPrimaryCursorPosition > 0u )
1133 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1136 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1138 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1140 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1143 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1145 // Get first the line index of the current cursor position index.
1146 CharacterIndex characterIndex = 0u;
1148 if( mEventData->mPrimaryCursorPosition > 0u )
1150 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1153 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1154 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1156 // Retrieve the cursor position info.
1157 CursorInfo cursorInfo;
1158 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1161 // Get the line above.
1162 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1164 // Get the next hit 'y' point.
1165 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1167 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1168 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1169 mModel->mLogicalModel,
1171 mEventData->mCursorHookPositionX,
1174 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1176 // Get first the line index of the current cursor position index.
1177 CharacterIndex characterIndex = 0u;
1179 if( mEventData->mPrimaryCursorPosition > 0u )
1181 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1184 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1186 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1188 // Retrieve the cursor position info.
1189 CursorInfo cursorInfo;
1190 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1193 // Get the line below.
1194 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1196 // Get the next hit 'y' point.
1197 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1199 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1200 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1201 mModel->mLogicalModel,
1203 mEventData->mCursorHookPositionX,
1208 mEventData->mUpdateCursorPosition = true;
1209 mEventData->mUpdateInputStyle = true;
1210 mEventData->mScrollAfterUpdatePosition = true;
1213 void Controller::Impl::OnTapEvent( const Event& event )
1215 if( NULL != mEventData )
1217 const unsigned int tapCount = event.p1.mUint;
1219 if( 1u == tapCount )
1221 if( IsShowingRealText() )
1223 // Convert from control's coords to text's coords.
1224 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1225 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1227 // Keep the tap 'x' position. Used to move the cursor.
1228 mEventData->mCursorHookPositionX = xPosition;
1230 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1231 mModel->mLogicalModel,
1236 // When the cursor position is changing, delay cursor blinking
1237 mEventData->mDecorator->DelayCursorBlink();
1241 mEventData->mPrimaryCursorPosition = 0u;
1244 mEventData->mUpdateCursorPosition = true;
1245 mEventData->mUpdateGrabHandlePosition = true;
1246 mEventData->mScrollAfterUpdatePosition = true;
1247 mEventData->mUpdateInputStyle = true;
1249 // Notify the cursor position to the imf manager.
1250 if( mEventData->mImfManager )
1252 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1253 mEventData->mImfManager.NotifyCursorPosition();
1259 void Controller::Impl::OnPanEvent( const Event& event )
1261 if( NULL == mEventData )
1263 // Nothing to do if there is no text input.
1267 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1268 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1270 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1272 // Nothing to do if scrolling is not enabled.
1276 const int state = event.p1.mInt;
1280 case Gesture::Started:
1282 // Will remove the cursor, handles or text's popup, ...
1283 ChangeState( EventData::TEXT_PANNING );
1286 case Gesture::Continuing:
1288 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1289 const Vector2 currentScroll = mModel->mScrollPosition;
1291 if( isHorizontalScrollEnabled )
1293 const float displacementX = event.p2.mFloat;
1294 mModel->mScrollPosition.x += displacementX;
1296 ClampHorizontalScroll( layoutSize );
1299 if( isVerticalScrollEnabled )
1301 const float displacementY = event.p3.mFloat;
1302 mModel->mScrollPosition.y += displacementY;
1304 ClampVerticalScroll( layoutSize );
1307 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1310 case Gesture::Finished:
1311 case Gesture::Cancelled: // FALLTHROUGH
1313 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1314 ChangeState( mEventData->mPreviousState );
1322 void Controller::Impl::OnLongPressEvent( const Event& event )
1324 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1326 if( EventData::EDITING == mEventData->mState )
1328 ChangeState ( EventData::EDITING_WITH_POPUP );
1329 mEventData->mDecoratorUpdated = true;
1330 mEventData->mUpdateInputStyle = true;
1334 void Controller::Impl::OnHandleEvent( const Event& event )
1336 if( NULL == mEventData )
1338 // Nothing to do if there is no text input.
1342 const unsigned int state = event.p1.mUint;
1343 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1344 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1346 if( HANDLE_PRESSED == state )
1348 // Convert from decorator's coords to text's coords.
1349 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1350 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1352 // Need to calculate the handle's new position.
1353 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1354 mModel->mLogicalModel,
1359 if( Event::GRAB_HANDLE_EVENT == event.type )
1361 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1363 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1365 // Updates the cursor position if the handle's new position is different than the current one.
1366 mEventData->mUpdateCursorPosition = true;
1367 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1368 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1369 mEventData->mPrimaryCursorPosition = handleNewPosition;
1372 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1373 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1375 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1377 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1379 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1380 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1382 // Updates the highlight box if the handle's new position is different than the current one.
1383 mEventData->mUpdateHighlightBox = true;
1384 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1385 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1386 mEventData->mLeftSelectionPosition = handleNewPosition;
1389 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1390 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1392 // Will define the order to scroll the text to match the handle position.
1393 mEventData->mIsLeftHandleSelected = true;
1395 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1397 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1399 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1400 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1402 // Updates the highlight box if the handle's new position is different than the current one.
1403 mEventData->mUpdateHighlightBox = true;
1404 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1405 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1406 mEventData->mRightSelectionPosition = handleNewPosition;
1409 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1410 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1412 // Will define the order to scroll the text to match the handle position.
1413 mEventData->mIsLeftHandleSelected = false;
1415 } // end ( HANDLE_PRESSED == state )
1416 else if( ( HANDLE_RELEASED == state ) ||
1417 handleStopScrolling )
1419 CharacterIndex handlePosition = 0u;
1420 if( handleStopScrolling || isSmoothHandlePanEnabled )
1422 // Convert from decorator's coords to text's coords.
1423 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1424 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1426 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1427 mModel->mLogicalModel,
1433 if( Event::GRAB_HANDLE_EVENT == event.type )
1435 mEventData->mUpdateCursorPosition = true;
1436 mEventData->mUpdateGrabHandlePosition = true;
1437 mEventData->mUpdateInputStyle = true;
1439 if( !IsClipboardEmpty() )
1441 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1444 if( handleStopScrolling || isSmoothHandlePanEnabled )
1446 mEventData->mScrollAfterUpdatePosition = true;
1447 mEventData->mPrimaryCursorPosition = handlePosition;
1450 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1452 ChangeState( EventData::SELECTING );
1454 mEventData->mUpdateHighlightBox = true;
1455 mEventData->mUpdateLeftSelectionPosition = true;
1456 mEventData->mUpdateRightSelectionPosition = true;
1458 if( handleStopScrolling || isSmoothHandlePanEnabled )
1460 mEventData->mScrollAfterUpdatePosition = true;
1462 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1463 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1465 mEventData->mLeftSelectionPosition = handlePosition;
1469 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1471 ChangeState( EventData::SELECTING );
1473 mEventData->mUpdateHighlightBox = true;
1474 mEventData->mUpdateRightSelectionPosition = true;
1475 mEventData->mUpdateLeftSelectionPosition = true;
1477 if( handleStopScrolling || isSmoothHandlePanEnabled )
1479 mEventData->mScrollAfterUpdatePosition = true;
1480 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1481 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1483 mEventData->mRightSelectionPosition = handlePosition;
1488 mEventData->mDecoratorUpdated = true;
1489 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1490 else if( HANDLE_SCROLLING == state )
1492 const float xSpeed = event.p2.mFloat;
1493 const float ySpeed = event.p3.mFloat;
1494 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1495 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1497 mModel->mScrollPosition.x += xSpeed;
1498 mModel->mScrollPosition.y += ySpeed;
1500 ClampHorizontalScroll( layoutSize );
1501 ClampVerticalScroll( layoutSize );
1503 bool endOfScroll = false;
1504 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1506 // Notify the decorator there is no more text to scroll.
1507 // The decorator won't send more scroll events.
1508 mEventData->mDecorator->NotifyEndOfScroll();
1509 // Still need to set the position of the handle.
1513 // Set the position of the handle.
1514 const bool scrollRightDirection = xSpeed > 0.f;
1515 const bool scrollBottomDirection = ySpeed > 0.f;
1516 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1517 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1519 if( Event::GRAB_HANDLE_EVENT == event.type )
1521 ChangeState( EventData::GRAB_HANDLE_PANNING );
1523 // Get the grab handle position in decorator coords.
1524 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1526 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1528 // Position the grag handle close to either the left or right edge.
1529 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1532 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1534 position.x = mEventData->mCursorHookPositionX;
1536 // Position the grag handle close to either the top or bottom edge.
1537 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1540 // Get the new handle position.
1541 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1542 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1543 mModel->mLogicalModel,
1545 position.x - mModel->mScrollPosition.x,
1546 position.y - mModel->mScrollPosition.y );
1548 if( mEventData->mPrimaryCursorPosition != handlePosition )
1550 mEventData->mUpdateCursorPosition = true;
1551 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1552 mEventData->mScrollAfterUpdatePosition = true;
1553 mEventData->mPrimaryCursorPosition = handlePosition;
1555 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1557 // Updates the decorator if the soft handle panning is enabled.
1558 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1560 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1562 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1564 // Get the selection handle position in decorator coords.
1565 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1567 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1569 // Position the selection handle close to either the left or right edge.
1570 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1573 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1575 position.x = mEventData->mCursorHookPositionX;
1577 // Position the grag handle close to either the top or bottom edge.
1578 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1581 // Get the new handle position.
1582 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1583 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1584 mModel->mLogicalModel,
1586 position.x - mModel->mScrollPosition.x,
1587 position.y - mModel->mScrollPosition.y );
1589 if( leftSelectionHandleEvent )
1591 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1593 if( differentHandles || endOfScroll )
1595 mEventData->mUpdateHighlightBox = true;
1596 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1597 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1598 mEventData->mLeftSelectionPosition = handlePosition;
1603 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1604 if( differentHandles || endOfScroll )
1606 mEventData->mUpdateHighlightBox = true;
1607 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1608 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1609 mEventData->mRightSelectionPosition = handlePosition;
1613 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1615 RepositionSelectionHandles();
1617 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1620 mEventData->mDecoratorUpdated = true;
1621 } // end ( HANDLE_SCROLLING == state )
1624 void Controller::Impl::OnSelectEvent( const Event& event )
1626 if( NULL == mEventData )
1628 // Nothing to do if there is no text.
1632 if( mEventData->mSelectionEnabled )
1634 // Convert from control's coords to text's coords.
1635 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1636 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1638 // Calculates the logical position from the x,y coords.
1639 RepositionSelectionHandles( xPosition,
1644 void Controller::Impl::OnSelectAllEvent()
1646 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1648 if( NULL == mEventData )
1650 // Nothing to do if there is no text.
1654 if( mEventData->mSelectionEnabled )
1656 ChangeState( EventData::SELECTING );
1658 mEventData->mLeftSelectionPosition = 0u;
1659 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1661 mEventData->mScrollAfterUpdatePosition = true;
1662 mEventData->mUpdateLeftSelectionPosition = true;
1663 mEventData->mUpdateRightSelectionPosition = true;
1664 mEventData->mUpdateHighlightBox = true;
1668 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1670 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1672 // Nothing to select if handles are in the same place.
1673 selectedText.clear();
1677 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1679 //Get start and end position of selection
1680 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1681 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1683 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1684 const Length numberOfCharacters = utf32Characters.Count();
1686 // Validate the start and end selection points
1687 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1689 //Get text as a UTF8 string
1690 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1692 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1694 // Keep a copy of the current input style.
1695 InputStyle currentInputStyle;
1696 currentInputStyle.Copy( mEventData->mInputStyle );
1698 // Set as input style the style of the first deleted character.
1699 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1701 // Compare if the input style has changed.
1702 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1704 if( hasInputStyleChanged )
1706 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1707 // Queue the input style changed signal.
1708 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1711 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1713 // Mark the paragraphs to be updated.
1714 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1715 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1717 // Delete text between handles
1718 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1719 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1720 utf32Characters.Erase( first, last );
1722 // Will show the cursor at the first character of the selection.
1723 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1727 // Will show the cursor at the last character of the selection.
1728 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1731 mEventData->mDecoratorUpdated = true;
1735 void Controller::Impl::ShowClipboard()
1739 mClipboard.ShowClipboard();
1743 void Controller::Impl::HideClipboard()
1745 if( mClipboard && mClipboardHideEnabled )
1747 mClipboard.HideClipboard();
1751 void Controller::Impl::SetClipboardHideEnable(bool enable)
1753 mClipboardHideEnabled = enable;
1756 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1758 //Send string to clipboard
1759 return ( mClipboard && mClipboard.SetItem( source ) );
1762 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1764 std::string selectedText;
1765 RetrieveSelection( selectedText, deleteAfterSending );
1766 CopyStringToClipboard( selectedText );
1767 ChangeState( EventData::EDITING );
1770 void Controller::Impl::RequestGetTextFromClipboard()
1774 mClipboard.RequestItem();
1778 void Controller::Impl::RepositionSelectionHandles()
1780 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1781 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1783 if( selectionStart == selectionEnd )
1785 // Nothing to select if handles are in the same place.
1789 mEventData->mDecorator->ClearHighlights();
1791 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1792 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1793 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1794 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1795 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1796 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1797 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1799 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1800 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1801 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1803 // Swap the indices if the start is greater than the end.
1804 const bool indicesSwapped = selectionStart > selectionEnd;
1806 // Tell the decorator to flip the selection handles if needed.
1807 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1809 if( indicesSwapped )
1811 std::swap( selectionStart, selectionEnd );
1814 // Get the indices to the first and last selected glyphs.
1815 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1816 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1817 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1818 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1820 // Get the lines where the glyphs are laid-out.
1821 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1823 LineIndex lineIndex = 0u;
1824 Length numberOfLines = 0u;
1825 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1826 1u + glyphEnd - glyphStart,
1829 const LineIndex firstLineIndex = lineIndex;
1831 // Create the structure to store some selection box info.
1832 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1833 selectionBoxLinesInfo.Resize( numberOfLines );
1835 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1836 selectionBoxInfo->minX = MAX_FLOAT;
1837 selectionBoxInfo->maxX = MIN_FLOAT;
1839 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1840 float minHighlightX = std::numeric_limits<float>::max();
1841 float maxHighlightX = std::numeric_limits<float>::min();
1843 Vector2 highLightPosition; // The highlight position in decorator's coords.
1845 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1847 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1848 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1851 // Transform to decorator's (control) coords.
1852 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1854 lineRun += firstLineIndex;
1856 // The line height is the addition of the line ascender and the line descender.
1857 // However, the line descender has a negative value, hence the subtraction.
1858 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1860 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1862 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1863 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1864 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1866 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1867 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1868 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1870 // The number of quads of the selection box.
1871 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1872 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1874 // Count the actual number of quads.
1875 unsigned int actualNumberOfQuads = 0u;
1878 // Traverse the glyphs.
1879 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1881 const GlyphInfo& glyph = *( glyphsBuffer + index );
1882 const Vector2& position = *( positionsBuffer + index );
1884 if( splitStartGlyph )
1886 // 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.
1888 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1889 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1890 // Get the direction of the character.
1891 CharacterDirection isCurrentRightToLeft = false;
1892 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1894 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1897 // The end point could be in the middle of the ligature.
1898 // Calculate the number of characters selected.
1899 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1901 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1902 quad.y = selectionBoxInfo->lineOffset;
1903 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1904 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1906 // Store the min and max 'x' for each line.
1907 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1908 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1910 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1911 ++actualNumberOfQuads;
1913 splitStartGlyph = false;
1917 if( splitEndGlyph && ( index == glyphEnd ) )
1919 // 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.
1921 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1922 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1923 // Get the direction of the character.
1924 CharacterDirection isCurrentRightToLeft = false;
1925 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1927 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1930 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1932 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1933 quad.y = selectionBoxInfo->lineOffset;
1934 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1935 quad.w = quad.y + selectionBoxInfo->lineHeight;
1937 // Store the min and max 'x' for each line.
1938 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1939 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1941 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1943 ++actualNumberOfQuads;
1945 splitEndGlyph = false;
1949 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1950 quad.y = selectionBoxInfo->lineOffset;
1951 quad.z = quad.x + glyph.advance;
1952 quad.w = quad.y + selectionBoxInfo->lineHeight;
1954 // Store the min and max 'x' for each line.
1955 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1956 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1958 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1960 ++actualNumberOfQuads;
1962 // Whether to retrieve the next line.
1963 if( index == lastGlyphOfLine )
1966 if( lineIndex < firstLineIndex + numberOfLines )
1968 // Retrieve the next line.
1971 // Get the last glyph of the new line.
1972 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1974 // Keep the offset and height of the current selection box.
1975 const float currentLineOffset = selectionBoxInfo->lineOffset;
1976 const float currentLineHeight = selectionBoxInfo->lineHeight;
1978 // Get the selection box info for the next line.
1981 selectionBoxInfo->minX = MAX_FLOAT;
1982 selectionBoxInfo->maxX = MIN_FLOAT;
1984 // Update the line's vertical offset.
1985 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1987 // The line height is the addition of the line ascender and the line descender.
1988 // However, the line descender has a negative value, hence the subtraction.
1989 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1994 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1995 // The final width is calculated after 'boxifying' the selection.
1996 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1997 endIt = selectionBoxLinesInfo.End();
2001 const SelectionBoxInfo& info = *it;
2003 // Update the size of the highlighted text.
2004 highLightSize.height += info.lineHeight;
2005 minHighlightX = std::min( minHighlightX, info.minX );
2006 maxHighlightX = std::max( maxHighlightX, info.maxX );
2009 // Add extra geometry to 'boxify' the selection.
2011 if( 1u < numberOfLines )
2013 // Boxify the first line.
2014 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2015 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2017 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2018 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2023 quad.y = firstSelectionBoxLineInfo.lineOffset;
2024 quad.z = firstSelectionBoxLineInfo.minX;
2025 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2027 // Boxify at the beginning of the line.
2028 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2030 ++actualNumberOfQuads;
2032 // Update the size of the highlighted text.
2033 minHighlightX = 0.f;
2038 quad.x = firstSelectionBoxLineInfo.maxX;
2039 quad.y = firstSelectionBoxLineInfo.lineOffset;
2040 quad.z = mModel->mVisualModel->mControlSize.width;
2041 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2043 // Boxify at the end of the line.
2044 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2046 ++actualNumberOfQuads;
2048 // Update the size of the highlighted text.
2049 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2052 // Boxify the central lines.
2053 if( 2u < numberOfLines )
2055 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2056 endIt = selectionBoxLinesInfo.End() - 1u;
2060 const SelectionBoxInfo& info = *it;
2063 quad.y = info.lineOffset;
2065 quad.w = info.lineOffset + info.lineHeight;
2067 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2069 ++actualNumberOfQuads;
2072 quad.y = info.lineOffset;
2073 quad.z = mModel->mVisualModel->mControlSize.width;
2074 quad.w = info.lineOffset + info.lineHeight;
2076 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2078 ++actualNumberOfQuads;
2081 // Update the size of the highlighted text.
2082 minHighlightX = 0.f;
2083 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2086 // Boxify the last line.
2087 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2088 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2090 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2091 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2096 quad.y = lastSelectionBoxLineInfo.lineOffset;
2097 quad.z = lastSelectionBoxLineInfo.minX;
2098 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2100 // Boxify at the beginning of the line.
2101 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2103 ++actualNumberOfQuads;
2105 // Update the size of the highlighted text.
2106 minHighlightX = 0.f;
2111 quad.x = lastSelectionBoxLineInfo.maxX;
2112 quad.y = lastSelectionBoxLineInfo.lineOffset;
2113 quad.z = mModel->mVisualModel->mControlSize.width;
2114 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2116 // Boxify at the end of the line.
2117 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2119 ++actualNumberOfQuads;
2121 // Update the size of the highlighted text.
2122 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2126 // Set the actual number of quads.
2127 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2129 // Sets the highlight's size and position. In decorator's coords.
2130 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2131 highLightSize.width = maxHighlightX - minHighlightX;
2133 highLightPosition.x = minHighlightX;
2134 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2135 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2137 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2139 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2141 CursorInfo primaryCursorInfo;
2142 GetCursorPosition( mEventData->mLeftSelectionPosition,
2143 primaryCursorInfo );
2145 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2147 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2149 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2150 primaryCursorInfo.lineHeight );
2152 CursorInfo secondaryCursorInfo;
2153 GetCursorPosition( mEventData->mRightSelectionPosition,
2154 secondaryCursorInfo );
2156 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2158 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2159 secondaryPosition.x,
2160 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2161 secondaryCursorInfo.lineHeight );
2164 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2165 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2167 // Set the flag to update the decorator.
2168 mEventData->mDecoratorUpdated = true;
2171 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2173 if( NULL == mEventData )
2175 // Nothing to do if there is no text input.
2179 if( IsShowingPlaceholderText() )
2181 // Nothing to do if there is the place-holder text.
2185 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2186 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2187 if( ( 0 == numberOfGlyphs ) ||
2188 ( 0 == numberOfLines ) )
2190 // Nothing to do if there is no text.
2194 // Find which word was selected
2195 CharacterIndex selectionStart( 0 );
2196 CharacterIndex selectionEnd( 0 );
2197 const bool indicesFound = FindSelectionIndices( mModel->mVisualModel,
2198 mModel->mLogicalModel,
2204 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2208 ChangeState( EventData::SELECTING );
2210 mEventData->mLeftSelectionPosition = selectionStart;
2211 mEventData->mRightSelectionPosition = selectionEnd;
2213 mEventData->mUpdateLeftSelectionPosition = true;
2214 mEventData->mUpdateRightSelectionPosition = true;
2215 mEventData->mUpdateHighlightBox = true;
2217 // It may happen an IMF commit event arrives before the selection event
2218 // if the IMF manager is in pre-edit state. The commit event will set the
2219 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2220 // to false, the highlight box won't be updated.
2221 mEventData->mUpdateCursorPosition = false;
2223 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2227 // Nothing to select. i.e. a white space, out of bounds
2228 ChangeState( EventData::EDITING );
2230 mEventData->mPrimaryCursorPosition = selectionEnd;
2232 mEventData->mUpdateCursorPosition = true;
2233 mEventData->mUpdateGrabHandlePosition = true;
2234 mEventData->mScrollAfterUpdatePosition = true;
2235 mEventData->mUpdateInputStyle = true;
2239 void Controller::Impl::SetPopupButtons()
2242 * Sets the Popup buttons to be shown depending on State.
2244 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2246 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2249 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2251 if( EventData::SELECTING == mEventData->mState )
2253 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2255 if( !IsClipboardEmpty() )
2257 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2258 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2261 if( !mEventData->mAllTextSelected )
2263 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2266 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2268 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2270 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2273 if( !IsClipboardEmpty() )
2275 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2276 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2279 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2281 if ( !IsClipboardEmpty() )
2283 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2284 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2288 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2291 void Controller::Impl::ChangeState( EventData::State newState )
2293 if( NULL == mEventData )
2295 // Nothing to do if there is no text input.
2299 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2301 if( mEventData->mState != newState )
2303 mEventData->mPreviousState = mEventData->mState;
2304 mEventData->mState = newState;
2306 switch( mEventData->mState )
2308 case EventData::INACTIVE:
2310 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2311 mEventData->mDecorator->StopCursorBlink();
2312 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2313 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2314 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2315 mEventData->mDecorator->SetHighlightActive( false );
2316 mEventData->mDecorator->SetPopupActive( false );
2317 mEventData->mDecoratorUpdated = true;
2320 case EventData::INTERRUPTED:
2322 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2323 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2324 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2325 mEventData->mDecorator->SetHighlightActive( false );
2326 mEventData->mDecorator->SetPopupActive( false );
2327 mEventData->mDecoratorUpdated = true;
2330 case EventData::SELECTING:
2332 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2333 mEventData->mDecorator->StopCursorBlink();
2334 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2335 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2336 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2337 mEventData->mDecorator->SetHighlightActive( true );
2338 if( mEventData->mGrabHandlePopupEnabled )
2341 mEventData->mDecorator->SetPopupActive( true );
2343 mEventData->mDecoratorUpdated = true;
2346 case EventData::EDITING:
2348 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2349 if( mEventData->mCursorBlinkEnabled )
2351 mEventData->mDecorator->StartCursorBlink();
2353 // Grab handle is not shown until a tap is received whilst EDITING
2354 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2355 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2356 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2357 mEventData->mDecorator->SetHighlightActive( false );
2358 if( mEventData->mGrabHandlePopupEnabled )
2360 mEventData->mDecorator->SetPopupActive( false );
2362 mEventData->mDecoratorUpdated = true;
2365 case EventData::EDITING_WITH_POPUP:
2367 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2369 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2370 if( mEventData->mCursorBlinkEnabled )
2372 mEventData->mDecorator->StartCursorBlink();
2374 if( mEventData->mSelectionEnabled )
2376 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2377 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2378 mEventData->mDecorator->SetHighlightActive( false );
2382 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2384 if( mEventData->mGrabHandlePopupEnabled )
2387 mEventData->mDecorator->SetPopupActive( true );
2389 mEventData->mDecoratorUpdated = true;
2392 case EventData::EDITING_WITH_GRAB_HANDLE:
2394 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2396 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2397 if( mEventData->mCursorBlinkEnabled )
2399 mEventData->mDecorator->StartCursorBlink();
2401 // Grab handle is not shown until a tap is received whilst EDITING
2402 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2403 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2404 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2405 mEventData->mDecorator->SetHighlightActive( false );
2406 if( mEventData->mGrabHandlePopupEnabled )
2408 mEventData->mDecorator->SetPopupActive( false );
2410 mEventData->mDecoratorUpdated = true;
2413 case EventData::SELECTION_HANDLE_PANNING:
2415 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2416 mEventData->mDecorator->StopCursorBlink();
2417 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2418 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2419 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2420 mEventData->mDecorator->SetHighlightActive( true );
2421 if( mEventData->mGrabHandlePopupEnabled )
2423 mEventData->mDecorator->SetPopupActive( false );
2425 mEventData->mDecoratorUpdated = true;
2428 case EventData::GRAB_HANDLE_PANNING:
2430 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2432 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2433 if( mEventData->mCursorBlinkEnabled )
2435 mEventData->mDecorator->StartCursorBlink();
2437 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2438 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2439 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2440 mEventData->mDecorator->SetHighlightActive( false );
2441 if( mEventData->mGrabHandlePopupEnabled )
2443 mEventData->mDecorator->SetPopupActive( false );
2445 mEventData->mDecoratorUpdated = true;
2448 case EventData::EDITING_WITH_PASTE_POPUP:
2450 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2452 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2453 if( mEventData->mCursorBlinkEnabled )
2455 mEventData->mDecorator->StartCursorBlink();
2458 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2459 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2460 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2461 mEventData->mDecorator->SetHighlightActive( false );
2463 if( mEventData->mGrabHandlePopupEnabled )
2466 mEventData->mDecorator->SetPopupActive( true );
2468 mEventData->mDecoratorUpdated = true;
2471 case EventData::TEXT_PANNING:
2473 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2474 mEventData->mDecorator->StopCursorBlink();
2475 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2476 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2477 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2479 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2480 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2481 mEventData->mDecorator->SetHighlightActive( true );
2484 if( mEventData->mGrabHandlePopupEnabled )
2486 mEventData->mDecorator->SetPopupActive( false );
2489 mEventData->mDecoratorUpdated = true;
2496 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2497 CursorInfo& cursorInfo )
2499 if( !IsShowingRealText() )
2501 // Do not want to use the place-holder text to set the cursor position.
2503 // Use the line's height of the font's family set to set the cursor's size.
2504 // If there is no font's family set, use the default font.
2505 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2507 cursorInfo.lineOffset = 0.f;
2508 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2509 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2511 switch( mModel->mHorizontalAlignment )
2513 case Layout::HORIZONTAL_ALIGN_BEGIN:
2515 cursorInfo.primaryPosition.x = 0.f;
2518 case Layout::HORIZONTAL_ALIGN_CENTER:
2520 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2523 case Layout::HORIZONTAL_ALIGN_END:
2525 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2530 // Nothing else to do.
2534 Text::GetCursorPosition( mModel->mVisualModel,
2535 mModel->mLogicalModel,
2540 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2542 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2544 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2545 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2547 if( 0.f > cursorInfo.primaryPosition.x )
2549 cursorInfo.primaryPosition.x = 0.f;
2552 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2553 if( cursorInfo.primaryPosition.x > edgeWidth )
2555 cursorInfo.primaryPosition.x = edgeWidth;
2560 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2562 if( NULL == mEventData )
2564 // Nothing to do if there is no text input.
2568 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2570 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2571 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2573 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2574 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2576 if( numberOfCharacters > 1u )
2578 const Script script = mModel->mLogicalModel->GetScript( index );
2579 if( HasLigatureMustBreak( script ) )
2581 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2582 numberOfCharacters = 1u;
2587 while( 0u == numberOfCharacters )
2590 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2594 if( index < mEventData->mPrimaryCursorPosition )
2596 cursorIndex -= numberOfCharacters;
2600 cursorIndex += numberOfCharacters;
2603 // Will update the cursor hook position.
2604 mEventData->mUpdateCursorHookPosition = true;
2609 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2611 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2612 if( NULL == mEventData )
2614 // Nothing to do if there is no text input.
2615 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2619 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2621 // Sets the cursor position.
2622 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2625 cursorInfo.primaryCursorHeight,
2626 cursorInfo.lineHeight );
2627 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2629 if( mEventData->mUpdateGrabHandlePosition )
2631 // Sets the grab handle position.
2632 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2634 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2635 cursorInfo.lineHeight );
2638 if( cursorInfo.isSecondaryCursor )
2640 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2641 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2642 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2643 cursorInfo.secondaryCursorHeight,
2644 cursorInfo.lineHeight );
2645 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2648 // Set which cursors are active according the state.
2649 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2651 if( cursorInfo.isSecondaryCursor )
2653 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2657 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2662 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2665 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2668 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2669 const CursorInfo& cursorInfo )
2671 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2672 ( RIGHT_SELECTION_HANDLE != handleType ) )
2677 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2679 // Sets the handle's position.
2680 mEventData->mDecorator->SetPosition( handleType,
2682 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2683 cursorInfo.lineHeight );
2685 // If selection handle at start of the text and other at end of the text then all text is selected.
2686 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2687 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2688 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2691 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2693 // Clamp between -space & -alignment offset.
2695 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2697 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2698 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2699 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2701 mEventData->mDecoratorUpdated = true;
2705 mModel->mScrollPosition.x = 0.f;
2709 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2711 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2713 // Nothing to do if the text is single line.
2717 // Clamp between -space & 0.
2718 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2720 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2721 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2722 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2724 mEventData->mDecoratorUpdated = true;
2728 mModel->mScrollPosition.y = 0.f;
2732 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2734 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2736 // position is in actor's coords.
2737 const float positionEndX = position.x + cursorWidth;
2738 const float positionEndY = position.y + lineHeight;
2740 // Transform the position to decorator coords.
2741 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2742 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2744 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2745 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2747 if( decoratorPositionBeginX < 0.f )
2749 mModel->mScrollPosition.x = -position.x;
2751 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2753 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2756 if( decoratorPositionBeginY < 0.f )
2758 mModel->mScrollPosition.y = -position.y;
2760 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2762 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2766 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2768 // Get the current cursor position in decorator coords.
2769 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2771 // Calculate the offset to match the cursor position before the character was deleted.
2772 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2773 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2775 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2776 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2778 // Makes the new cursor position visible if needed.
2779 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2782 void Controller::Impl::RequestRelayout()
2784 if( NULL != mControlInterface )
2786 mControlInterface->RequestTextRelayout();
2792 } // namespace Toolkit