2 * Copyright (c) 2016 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 );
1155 if( lineIndex > 0u )
1157 // Retrieve the cursor position info.
1158 CursorInfo cursorInfo;
1159 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1162 // Get the line above.
1163 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1165 // Get the next hit 'y' point.
1166 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1168 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1169 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1170 mModel->mLogicalModel,
1172 mEventData->mCursorHookPositionX,
1176 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1178 // Get first the line index of the current cursor position index.
1179 CharacterIndex characterIndex = 0u;
1181 if( mEventData->mPrimaryCursorPosition > 0u )
1183 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1186 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1188 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1190 // Retrieve the cursor position info.
1191 CursorInfo cursorInfo;
1192 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1195 // Get the line below.
1196 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1198 // Get the next hit 'y' point.
1199 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1201 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1202 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1203 mModel->mLogicalModel,
1205 mEventData->mCursorHookPositionX,
1210 mEventData->mUpdateCursorPosition = true;
1211 mEventData->mUpdateInputStyle = true;
1212 mEventData->mScrollAfterUpdatePosition = true;
1215 void Controller::Impl::OnTapEvent( const Event& event )
1217 if( NULL != mEventData )
1219 const unsigned int tapCount = event.p1.mUint;
1221 if( 1u == tapCount )
1223 if( IsShowingRealText() )
1225 // Convert from control's coords to text's coords.
1226 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1227 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1229 // Keep the tap 'x' position. Used to move the cursor.
1230 mEventData->mCursorHookPositionX = xPosition;
1232 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1233 mModel->mLogicalModel,
1238 // When the cursor position is changing, delay cursor blinking
1239 mEventData->mDecorator->DelayCursorBlink();
1243 mEventData->mPrimaryCursorPosition = 0u;
1246 mEventData->mUpdateCursorPosition = true;
1247 mEventData->mUpdateGrabHandlePosition = true;
1248 mEventData->mScrollAfterUpdatePosition = true;
1249 mEventData->mUpdateInputStyle = true;
1251 // Notify the cursor position to the imf manager.
1252 if( mEventData->mImfManager )
1254 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1255 mEventData->mImfManager.NotifyCursorPosition();
1261 void Controller::Impl::OnPanEvent( const Event& event )
1263 if( NULL == mEventData )
1265 // Nothing to do if there is no text input.
1269 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1270 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1272 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1274 // Nothing to do if scrolling is not enabled.
1278 const int state = event.p1.mInt;
1282 case Gesture::Started:
1284 // Will remove the cursor, handles or text's popup, ...
1285 ChangeState( EventData::TEXT_PANNING );
1288 case Gesture::Continuing:
1290 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1291 const Vector2 currentScroll = mModel->mScrollPosition;
1293 if( isHorizontalScrollEnabled )
1295 const float displacementX = event.p2.mFloat;
1296 mModel->mScrollPosition.x += displacementX;
1298 ClampHorizontalScroll( layoutSize );
1301 if( isVerticalScrollEnabled )
1303 const float displacementY = event.p3.mFloat;
1304 mModel->mScrollPosition.y += displacementY;
1306 ClampVerticalScroll( layoutSize );
1309 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1312 case Gesture::Finished:
1313 case Gesture::Cancelled: // FALLTHROUGH
1315 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1316 ChangeState( mEventData->mPreviousState );
1324 void Controller::Impl::OnLongPressEvent( const Event& event )
1326 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1328 if( EventData::EDITING == mEventData->mState )
1330 ChangeState ( EventData::EDITING_WITH_POPUP );
1331 mEventData->mDecoratorUpdated = true;
1332 mEventData->mUpdateInputStyle = true;
1336 void Controller::Impl::OnHandleEvent( const Event& event )
1338 if( NULL == mEventData )
1340 // Nothing to do if there is no text input.
1344 const unsigned int state = event.p1.mUint;
1345 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1346 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1348 if( HANDLE_PRESSED == state )
1350 // Convert from decorator's coords to text's coords.
1351 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1352 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1354 // Need to calculate the handle's new position.
1355 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1356 mModel->mLogicalModel,
1361 if( Event::GRAB_HANDLE_EVENT == event.type )
1363 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1365 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1367 // Updates the cursor position if the handle's new position is different than the current one.
1368 mEventData->mUpdateCursorPosition = true;
1369 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1370 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1371 mEventData->mPrimaryCursorPosition = handleNewPosition;
1374 // 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.
1375 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1377 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1379 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1381 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1382 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1384 // Updates the highlight box if the handle's new position is different than the current one.
1385 mEventData->mUpdateHighlightBox = true;
1386 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1387 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1388 mEventData->mLeftSelectionPosition = handleNewPosition;
1391 // 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.
1392 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1394 // Will define the order to scroll the text to match the handle position.
1395 mEventData->mIsLeftHandleSelected = true;
1397 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1399 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1401 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1402 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1404 // Updates the highlight box if the handle's new position is different than the current one.
1405 mEventData->mUpdateHighlightBox = true;
1406 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1407 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1408 mEventData->mRightSelectionPosition = handleNewPosition;
1411 // 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.
1412 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1414 // Will define the order to scroll the text to match the handle position.
1415 mEventData->mIsLeftHandleSelected = false;
1417 } // end ( HANDLE_PRESSED == state )
1418 else if( ( HANDLE_RELEASED == state ) ||
1419 handleStopScrolling )
1421 CharacterIndex handlePosition = 0u;
1422 if( handleStopScrolling || isSmoothHandlePanEnabled )
1424 // Convert from decorator's coords to text's coords.
1425 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1426 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1428 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1429 mModel->mLogicalModel,
1435 if( Event::GRAB_HANDLE_EVENT == event.type )
1437 mEventData->mUpdateCursorPosition = true;
1438 mEventData->mUpdateGrabHandlePosition = true;
1439 mEventData->mUpdateInputStyle = true;
1441 if( !IsClipboardEmpty() )
1443 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1446 if( handleStopScrolling || isSmoothHandlePanEnabled )
1448 mEventData->mScrollAfterUpdatePosition = true;
1449 mEventData->mPrimaryCursorPosition = handlePosition;
1452 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1454 ChangeState( EventData::SELECTING );
1456 mEventData->mUpdateHighlightBox = true;
1457 mEventData->mUpdateLeftSelectionPosition = true;
1458 mEventData->mUpdateRightSelectionPosition = true;
1460 if( handleStopScrolling || isSmoothHandlePanEnabled )
1462 mEventData->mScrollAfterUpdatePosition = true;
1464 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1465 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1467 mEventData->mLeftSelectionPosition = handlePosition;
1471 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1473 ChangeState( EventData::SELECTING );
1475 mEventData->mUpdateHighlightBox = true;
1476 mEventData->mUpdateRightSelectionPosition = true;
1477 mEventData->mUpdateLeftSelectionPosition = true;
1479 if( handleStopScrolling || isSmoothHandlePanEnabled )
1481 mEventData->mScrollAfterUpdatePosition = true;
1482 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1483 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1485 mEventData->mRightSelectionPosition = handlePosition;
1490 mEventData->mDecoratorUpdated = true;
1491 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1492 else if( HANDLE_SCROLLING == state )
1494 const float xSpeed = event.p2.mFloat;
1495 const float ySpeed = event.p3.mFloat;
1496 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1497 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1499 mModel->mScrollPosition.x += xSpeed;
1500 mModel->mScrollPosition.y += ySpeed;
1502 ClampHorizontalScroll( layoutSize );
1503 ClampVerticalScroll( layoutSize );
1505 bool endOfScroll = false;
1506 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1508 // Notify the decorator there is no more text to scroll.
1509 // The decorator won't send more scroll events.
1510 mEventData->mDecorator->NotifyEndOfScroll();
1511 // Still need to set the position of the handle.
1515 // Set the position of the handle.
1516 const bool scrollRightDirection = xSpeed > 0.f;
1517 const bool scrollBottomDirection = ySpeed > 0.f;
1518 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1519 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1521 if( Event::GRAB_HANDLE_EVENT == event.type )
1523 ChangeState( EventData::GRAB_HANDLE_PANNING );
1525 // Get the grab handle position in decorator coords.
1526 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1528 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1530 // Position the grag handle close to either the left or right edge.
1531 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1534 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1536 position.x = mEventData->mCursorHookPositionX;
1538 // Position the grag handle close to either the top or bottom edge.
1539 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1542 // Get the new handle position.
1543 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1544 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1545 mModel->mLogicalModel,
1547 position.x - mModel->mScrollPosition.x,
1548 position.y - mModel->mScrollPosition.y );
1550 if( mEventData->mPrimaryCursorPosition != handlePosition )
1552 mEventData->mUpdateCursorPosition = true;
1553 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1554 mEventData->mScrollAfterUpdatePosition = true;
1555 mEventData->mPrimaryCursorPosition = handlePosition;
1557 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1559 // Updates the decorator if the soft handle panning is enabled.
1560 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1562 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1564 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1566 // Get the selection handle position in decorator coords.
1567 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1569 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1571 // Position the selection handle close to either the left or right edge.
1572 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1575 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1577 position.x = mEventData->mCursorHookPositionX;
1579 // Position the grag handle close to either the top or bottom edge.
1580 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1583 // Get the new handle position.
1584 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1585 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1586 mModel->mLogicalModel,
1588 position.x - mModel->mScrollPosition.x,
1589 position.y - mModel->mScrollPosition.y );
1591 if( leftSelectionHandleEvent )
1593 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1595 if( differentHandles || endOfScroll )
1597 mEventData->mUpdateHighlightBox = true;
1598 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1599 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1600 mEventData->mLeftSelectionPosition = handlePosition;
1605 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1606 if( differentHandles || endOfScroll )
1608 mEventData->mUpdateHighlightBox = true;
1609 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1610 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1611 mEventData->mRightSelectionPosition = handlePosition;
1615 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1617 RepositionSelectionHandles();
1619 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1622 mEventData->mDecoratorUpdated = true;
1623 } // end ( HANDLE_SCROLLING == state )
1626 void Controller::Impl::OnSelectEvent( const Event& event )
1628 if( NULL == mEventData )
1630 // Nothing to do if there is no text.
1634 if( mEventData->mSelectionEnabled )
1636 // Convert from control's coords to text's coords.
1637 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1638 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1640 // Calculates the logical position from the x,y coords.
1641 RepositionSelectionHandles( xPosition,
1646 void Controller::Impl::OnSelectAllEvent()
1648 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1650 if( NULL == mEventData )
1652 // Nothing to do if there is no text.
1656 if( mEventData->mSelectionEnabled )
1658 ChangeState( EventData::SELECTING );
1660 mEventData->mLeftSelectionPosition = 0u;
1661 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1663 mEventData->mScrollAfterUpdatePosition = true;
1664 mEventData->mUpdateLeftSelectionPosition = true;
1665 mEventData->mUpdateRightSelectionPosition = true;
1666 mEventData->mUpdateHighlightBox = true;
1670 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1672 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1674 // Nothing to select if handles are in the same place.
1675 selectedText.clear();
1679 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1681 //Get start and end position of selection
1682 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1683 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1685 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1686 const Length numberOfCharacters = utf32Characters.Count();
1688 // Validate the start and end selection points
1689 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1691 //Get text as a UTF8 string
1692 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1694 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1696 // Keep a copy of the current input style.
1697 InputStyle currentInputStyle;
1698 currentInputStyle.Copy( mEventData->mInputStyle );
1700 // Set as input style the style of the first deleted character.
1701 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1703 // Compare if the input style has changed.
1704 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1706 if( hasInputStyleChanged )
1708 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1709 // Queue the input style changed signal.
1710 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1713 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1715 // Mark the paragraphs to be updated.
1716 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1717 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1719 // Delete text between handles
1720 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1721 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1722 utf32Characters.Erase( first, last );
1724 // Will show the cursor at the first character of the selection.
1725 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1729 // Will show the cursor at the last character of the selection.
1730 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1733 mEventData->mDecoratorUpdated = true;
1737 void Controller::Impl::ShowClipboard()
1741 mClipboard.ShowClipboard();
1745 void Controller::Impl::HideClipboard()
1747 if( mClipboard && mClipboardHideEnabled )
1749 mClipboard.HideClipboard();
1753 void Controller::Impl::SetClipboardHideEnable(bool enable)
1755 mClipboardHideEnabled = enable;
1758 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1760 //Send string to clipboard
1761 return ( mClipboard && mClipboard.SetItem( source ) );
1764 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1766 std::string selectedText;
1767 RetrieveSelection( selectedText, deleteAfterSending );
1768 CopyStringToClipboard( selectedText );
1769 ChangeState( EventData::EDITING );
1772 void Controller::Impl::RequestGetTextFromClipboard()
1776 mClipboard.RequestItem();
1780 void Controller::Impl::RepositionSelectionHandles()
1782 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1783 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1785 if( selectionStart == selectionEnd )
1787 // Nothing to select if handles are in the same place.
1791 mEventData->mDecorator->ClearHighlights();
1793 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1794 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1795 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1796 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1797 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1798 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1799 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1801 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1802 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1803 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1805 // Swap the indices if the start is greater than the end.
1806 const bool indicesSwapped = selectionStart > selectionEnd;
1808 // Tell the decorator to flip the selection handles if needed.
1809 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1811 if( indicesSwapped )
1813 std::swap( selectionStart, selectionEnd );
1816 // Get the indices to the first and last selected glyphs.
1817 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1818 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1819 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1820 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1822 // Get the lines where the glyphs are laid-out.
1823 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1825 LineIndex lineIndex = 0u;
1826 Length numberOfLines = 0u;
1827 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1828 1u + glyphEnd - glyphStart,
1831 const LineIndex firstLineIndex = lineIndex;
1833 // Create the structure to store some selection box info.
1834 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1835 selectionBoxLinesInfo.Resize( numberOfLines );
1837 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1838 selectionBoxInfo->minX = MAX_FLOAT;
1839 selectionBoxInfo->maxX = MIN_FLOAT;
1841 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1842 float minHighlightX = std::numeric_limits<float>::max();
1843 float maxHighlightX = std::numeric_limits<float>::min();
1845 Vector2 highLightPosition; // The highlight position in decorator's coords.
1847 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1849 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1850 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1853 // Transform to decorator's (control) coords.
1854 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1856 lineRun += firstLineIndex;
1858 // The line height is the addition of the line ascender and the line descender.
1859 // However, the line descender has a negative value, hence the subtraction.
1860 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1862 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1864 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1865 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1866 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1868 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1869 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1870 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1872 // The number of quads of the selection box.
1873 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1874 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1876 // Count the actual number of quads.
1877 unsigned int actualNumberOfQuads = 0u;
1880 // Traverse the glyphs.
1881 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1883 const GlyphInfo& glyph = *( glyphsBuffer + index );
1884 const Vector2& position = *( positionsBuffer + index );
1886 if( splitStartGlyph )
1888 // 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.
1890 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1891 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1892 // Get the direction of the character.
1893 CharacterDirection isCurrentRightToLeft = false;
1894 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1896 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1899 // The end point could be in the middle of the ligature.
1900 // Calculate the number of characters selected.
1901 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1903 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1904 quad.y = selectionBoxInfo->lineOffset;
1905 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1906 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1908 // Store the min and max 'x' for each line.
1909 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1910 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1912 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1913 ++actualNumberOfQuads;
1915 splitStartGlyph = false;
1919 if( splitEndGlyph && ( index == glyphEnd ) )
1921 // 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.
1923 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1924 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1925 // Get the direction of the character.
1926 CharacterDirection isCurrentRightToLeft = false;
1927 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1929 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1932 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1934 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1935 quad.y = selectionBoxInfo->lineOffset;
1936 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1937 quad.w = quad.y + selectionBoxInfo->lineHeight;
1939 // Store the min and max 'x' for each line.
1940 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1941 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1943 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1945 ++actualNumberOfQuads;
1947 splitEndGlyph = false;
1951 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1952 quad.y = selectionBoxInfo->lineOffset;
1953 quad.z = quad.x + glyph.advance;
1954 quad.w = quad.y + selectionBoxInfo->lineHeight;
1956 // Store the min and max 'x' for each line.
1957 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1958 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1960 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1962 ++actualNumberOfQuads;
1964 // Whether to retrieve the next line.
1965 if( index == lastGlyphOfLine )
1968 if( lineIndex < firstLineIndex + numberOfLines )
1970 // Retrieve the next line.
1973 // Get the last glyph of the new line.
1974 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1976 // Keep the offset and height of the current selection box.
1977 const float currentLineOffset = selectionBoxInfo->lineOffset;
1978 const float currentLineHeight = selectionBoxInfo->lineHeight;
1980 // Get the selection box info for the next line.
1983 selectionBoxInfo->minX = MAX_FLOAT;
1984 selectionBoxInfo->maxX = MIN_FLOAT;
1986 // Update the line's vertical offset.
1987 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1989 // The line height is the addition of the line ascender and the line descender.
1990 // However, the line descender has a negative value, hence the subtraction.
1991 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1996 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1997 // The final width is calculated after 'boxifying' the selection.
1998 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1999 endIt = selectionBoxLinesInfo.End();
2003 const SelectionBoxInfo& info = *it;
2005 // Update the size of the highlighted text.
2006 highLightSize.height += info.lineHeight;
2007 minHighlightX = std::min( minHighlightX, info.minX );
2008 maxHighlightX = std::max( maxHighlightX, info.maxX );
2011 // Add extra geometry to 'boxify' the selection.
2013 if( 1u < numberOfLines )
2015 // Boxify the first line.
2016 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2017 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2019 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2020 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2025 quad.y = firstSelectionBoxLineInfo.lineOffset;
2026 quad.z = firstSelectionBoxLineInfo.minX;
2027 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2029 // Boxify at the beginning of the line.
2030 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2032 ++actualNumberOfQuads;
2034 // Update the size of the highlighted text.
2035 minHighlightX = 0.f;
2040 quad.x = firstSelectionBoxLineInfo.maxX;
2041 quad.y = firstSelectionBoxLineInfo.lineOffset;
2042 quad.z = mModel->mVisualModel->mControlSize.width;
2043 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2045 // Boxify at the end of the line.
2046 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2048 ++actualNumberOfQuads;
2050 // Update the size of the highlighted text.
2051 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2054 // Boxify the central lines.
2055 if( 2u < numberOfLines )
2057 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2058 endIt = selectionBoxLinesInfo.End() - 1u;
2062 const SelectionBoxInfo& info = *it;
2065 quad.y = info.lineOffset;
2067 quad.w = info.lineOffset + info.lineHeight;
2069 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2071 ++actualNumberOfQuads;
2074 quad.y = info.lineOffset;
2075 quad.z = mModel->mVisualModel->mControlSize.width;
2076 quad.w = info.lineOffset + info.lineHeight;
2078 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2080 ++actualNumberOfQuads;
2083 // Update the size of the highlighted text.
2084 minHighlightX = 0.f;
2085 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2088 // Boxify the last line.
2089 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2090 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2092 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2093 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2098 quad.y = lastSelectionBoxLineInfo.lineOffset;
2099 quad.z = lastSelectionBoxLineInfo.minX;
2100 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2102 // Boxify at the beginning of the line.
2103 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2105 ++actualNumberOfQuads;
2107 // Update the size of the highlighted text.
2108 minHighlightX = 0.f;
2113 quad.x = lastSelectionBoxLineInfo.maxX;
2114 quad.y = lastSelectionBoxLineInfo.lineOffset;
2115 quad.z = mModel->mVisualModel->mControlSize.width;
2116 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2118 // Boxify at the end of the line.
2119 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2121 ++actualNumberOfQuads;
2123 // Update the size of the highlighted text.
2124 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2128 // Set the actual number of quads.
2129 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2131 // Sets the highlight's size and position. In decorator's coords.
2132 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2133 highLightSize.width = maxHighlightX - minHighlightX;
2135 highLightPosition.x = minHighlightX;
2136 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2137 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2139 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2141 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2143 CursorInfo primaryCursorInfo;
2144 GetCursorPosition( mEventData->mLeftSelectionPosition,
2145 primaryCursorInfo );
2147 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2149 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2151 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2152 primaryCursorInfo.lineHeight );
2154 CursorInfo secondaryCursorInfo;
2155 GetCursorPosition( mEventData->mRightSelectionPosition,
2156 secondaryCursorInfo );
2158 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2160 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2161 secondaryPosition.x,
2162 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2163 secondaryCursorInfo.lineHeight );
2166 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2167 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2169 // Set the flag to update the decorator.
2170 mEventData->mDecoratorUpdated = true;
2173 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2175 if( NULL == mEventData )
2177 // Nothing to do if there is no text input.
2181 if( IsShowingPlaceholderText() )
2183 // Nothing to do if there is the place-holder text.
2187 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2188 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2189 if( ( 0 == numberOfGlyphs ) ||
2190 ( 0 == numberOfLines ) )
2192 // Nothing to do if there is no text.
2196 // Find which word was selected
2197 CharacterIndex selectionStart( 0 );
2198 CharacterIndex selectionEnd( 0 );
2199 const bool indicesFound = FindSelectionIndices( mModel->mVisualModel,
2200 mModel->mLogicalModel,
2206 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2210 ChangeState( EventData::SELECTING );
2212 mEventData->mLeftSelectionPosition = selectionStart;
2213 mEventData->mRightSelectionPosition = selectionEnd;
2215 mEventData->mUpdateLeftSelectionPosition = true;
2216 mEventData->mUpdateRightSelectionPosition = true;
2217 mEventData->mUpdateHighlightBox = true;
2219 // It may happen an IMF commit event arrives before the selection event
2220 // if the IMF manager is in pre-edit state. The commit event will set the
2221 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2222 // to false, the highlight box won't be updated.
2223 mEventData->mUpdateCursorPosition = false;
2225 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2229 // Nothing to select. i.e. a white space, out of bounds
2230 ChangeState( EventData::EDITING );
2232 mEventData->mPrimaryCursorPosition = selectionEnd;
2234 mEventData->mUpdateCursorPosition = true;
2235 mEventData->mUpdateGrabHandlePosition = true;
2236 mEventData->mScrollAfterUpdatePosition = true;
2237 mEventData->mUpdateInputStyle = true;
2241 void Controller::Impl::SetPopupButtons()
2244 * Sets the Popup buttons to be shown depending on State.
2246 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2248 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2251 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2253 if( EventData::SELECTING == mEventData->mState )
2255 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2257 if( !IsClipboardEmpty() )
2259 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2260 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2263 if( !mEventData->mAllTextSelected )
2265 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2268 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2270 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2272 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2275 if( !IsClipboardEmpty() )
2277 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2278 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2281 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2283 if ( !IsClipboardEmpty() )
2285 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2286 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2290 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2293 void Controller::Impl::ChangeState( EventData::State newState )
2295 if( NULL == mEventData )
2297 // Nothing to do if there is no text input.
2301 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2303 if( mEventData->mState != newState )
2305 mEventData->mPreviousState = mEventData->mState;
2306 mEventData->mState = newState;
2308 switch( mEventData->mState )
2310 case EventData::INACTIVE:
2312 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2313 mEventData->mDecorator->StopCursorBlink();
2314 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2315 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2316 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2317 mEventData->mDecorator->SetHighlightActive( false );
2318 mEventData->mDecorator->SetPopupActive( false );
2319 mEventData->mDecoratorUpdated = true;
2322 case EventData::INTERRUPTED:
2324 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2325 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2326 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2327 mEventData->mDecorator->SetHighlightActive( false );
2328 mEventData->mDecorator->SetPopupActive( false );
2329 mEventData->mDecoratorUpdated = true;
2332 case EventData::SELECTING:
2334 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2335 mEventData->mDecorator->StopCursorBlink();
2336 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2337 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2338 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2339 mEventData->mDecorator->SetHighlightActive( true );
2340 if( mEventData->mGrabHandlePopupEnabled )
2343 mEventData->mDecorator->SetPopupActive( true );
2345 mEventData->mDecoratorUpdated = true;
2348 case EventData::EDITING:
2350 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2351 if( mEventData->mCursorBlinkEnabled )
2353 mEventData->mDecorator->StartCursorBlink();
2355 // Grab handle is not shown until a tap is received whilst EDITING
2356 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2357 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2358 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2359 mEventData->mDecorator->SetHighlightActive( false );
2360 if( mEventData->mGrabHandlePopupEnabled )
2362 mEventData->mDecorator->SetPopupActive( false );
2364 mEventData->mDecoratorUpdated = true;
2367 case EventData::EDITING_WITH_POPUP:
2369 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2371 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2372 if( mEventData->mCursorBlinkEnabled )
2374 mEventData->mDecorator->StartCursorBlink();
2376 if( mEventData->mSelectionEnabled )
2378 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2379 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2380 mEventData->mDecorator->SetHighlightActive( false );
2384 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2386 if( mEventData->mGrabHandlePopupEnabled )
2389 mEventData->mDecorator->SetPopupActive( true );
2391 mEventData->mDecoratorUpdated = true;
2394 case EventData::EDITING_WITH_GRAB_HANDLE:
2396 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2398 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2399 if( mEventData->mCursorBlinkEnabled )
2401 mEventData->mDecorator->StartCursorBlink();
2403 // Grab handle is not shown until a tap is received whilst EDITING
2404 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2405 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2406 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2407 mEventData->mDecorator->SetHighlightActive( false );
2408 if( mEventData->mGrabHandlePopupEnabled )
2410 mEventData->mDecorator->SetPopupActive( false );
2412 mEventData->mDecoratorUpdated = true;
2415 case EventData::SELECTION_HANDLE_PANNING:
2417 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2418 mEventData->mDecorator->StopCursorBlink();
2419 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2420 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2421 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2422 mEventData->mDecorator->SetHighlightActive( true );
2423 if( mEventData->mGrabHandlePopupEnabled )
2425 mEventData->mDecorator->SetPopupActive( false );
2427 mEventData->mDecoratorUpdated = true;
2430 case EventData::GRAB_HANDLE_PANNING:
2432 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2434 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2435 if( mEventData->mCursorBlinkEnabled )
2437 mEventData->mDecorator->StartCursorBlink();
2439 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2440 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2441 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2442 mEventData->mDecorator->SetHighlightActive( false );
2443 if( mEventData->mGrabHandlePopupEnabled )
2445 mEventData->mDecorator->SetPopupActive( false );
2447 mEventData->mDecoratorUpdated = true;
2450 case EventData::EDITING_WITH_PASTE_POPUP:
2452 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2454 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2455 if( mEventData->mCursorBlinkEnabled )
2457 mEventData->mDecorator->StartCursorBlink();
2460 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2461 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2462 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2463 mEventData->mDecorator->SetHighlightActive( false );
2465 if( mEventData->mGrabHandlePopupEnabled )
2468 mEventData->mDecorator->SetPopupActive( true );
2470 mEventData->mDecoratorUpdated = true;
2473 case EventData::TEXT_PANNING:
2475 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2476 mEventData->mDecorator->StopCursorBlink();
2477 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2478 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2479 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2481 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2482 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2483 mEventData->mDecorator->SetHighlightActive( true );
2486 if( mEventData->mGrabHandlePopupEnabled )
2488 mEventData->mDecorator->SetPopupActive( false );
2491 mEventData->mDecoratorUpdated = true;
2498 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2499 CursorInfo& cursorInfo )
2501 if( !IsShowingRealText() )
2503 // Do not want to use the place-holder text to set the cursor position.
2505 // Use the line's height of the font's family set to set the cursor's size.
2506 // If there is no font's family set, use the default font.
2507 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2509 cursorInfo.lineOffset = 0.f;
2510 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2511 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2513 switch( mModel->mHorizontalAlignment )
2515 case Layout::HORIZONTAL_ALIGN_BEGIN:
2517 cursorInfo.primaryPosition.x = 0.f;
2520 case Layout::HORIZONTAL_ALIGN_CENTER:
2522 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2525 case Layout::HORIZONTAL_ALIGN_END:
2527 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2532 // Nothing else to do.
2536 Text::GetCursorPosition( mModel->mVisualModel,
2537 mModel->mLogicalModel,
2542 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2544 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2546 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2547 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2549 if( 0.f > cursorInfo.primaryPosition.x )
2551 cursorInfo.primaryPosition.x = 0.f;
2554 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2555 if( cursorInfo.primaryPosition.x > edgeWidth )
2557 cursorInfo.primaryPosition.x = edgeWidth;
2562 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2564 if( NULL == mEventData )
2566 // Nothing to do if there is no text input.
2570 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2572 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2573 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2575 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2576 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2578 if( numberOfCharacters > 1u )
2580 const Script script = mModel->mLogicalModel->GetScript( index );
2581 if( HasLigatureMustBreak( script ) )
2583 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2584 numberOfCharacters = 1u;
2589 while( 0u == numberOfCharacters )
2592 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2596 if( index < mEventData->mPrimaryCursorPosition )
2598 cursorIndex -= numberOfCharacters;
2602 cursorIndex += numberOfCharacters;
2605 // Will update the cursor hook position.
2606 mEventData->mUpdateCursorHookPosition = true;
2611 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2613 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2614 if( NULL == mEventData )
2616 // Nothing to do if there is no text input.
2617 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2621 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2623 // Sets the cursor position.
2624 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2627 cursorInfo.primaryCursorHeight,
2628 cursorInfo.lineHeight );
2629 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2631 if( mEventData->mUpdateGrabHandlePosition )
2633 // Sets the grab handle position.
2634 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2636 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2637 cursorInfo.lineHeight );
2640 if( cursorInfo.isSecondaryCursor )
2642 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2643 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2644 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2645 cursorInfo.secondaryCursorHeight,
2646 cursorInfo.lineHeight );
2647 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2650 // Set which cursors are active according the state.
2651 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2653 if( cursorInfo.isSecondaryCursor )
2655 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2659 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2664 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2667 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2670 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2671 const CursorInfo& cursorInfo )
2673 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2674 ( RIGHT_SELECTION_HANDLE != handleType ) )
2679 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2681 // Sets the handle's position.
2682 mEventData->mDecorator->SetPosition( handleType,
2684 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2685 cursorInfo.lineHeight );
2687 // If selection handle at start of the text and other at end of the text then all text is selected.
2688 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2689 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2690 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2693 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2695 // Clamp between -space & -alignment offset.
2697 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2699 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2700 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2701 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2703 mEventData->mDecoratorUpdated = true;
2707 mModel->mScrollPosition.x = 0.f;
2711 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2713 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2715 // Nothing to do if the text is single line.
2719 // Clamp between -space & 0.
2720 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2722 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2723 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2724 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2726 mEventData->mDecoratorUpdated = true;
2730 mModel->mScrollPosition.y = 0.f;
2734 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2736 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2738 // position is in actor's coords.
2739 const float positionEndX = position.x + cursorWidth;
2740 const float positionEndY = position.y + lineHeight;
2742 // Transform the position to decorator coords.
2743 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2744 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2746 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2747 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2749 if( decoratorPositionBeginX < 0.f )
2751 mModel->mScrollPosition.x = -position.x;
2753 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2755 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2758 if( decoratorPositionBeginY < 0.f )
2760 mModel->mScrollPosition.y = -position.y;
2762 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2764 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2768 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2770 // Get the current cursor position in decorator coords.
2771 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2773 // Calculate the offset to match the cursor position before the character was deleted.
2774 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2775 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2777 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2778 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2780 // Makes the new cursor position visible if needed.
2781 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2784 void Controller::Impl::RequestRelayout()
2786 if( NULL != mControlInterface )
2788 mControlInterface->RequestTextRelayout();
2794 } // namespace Toolkit