2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderTextActive(),
74 mPlaceholderTextInactive(),
75 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77 mInputStyleChangedQueue(),
78 mPreviousState( INACTIVE ),
80 mPrimaryCursorPosition( 0u ),
81 mLeftSelectionPosition( 0u ),
82 mRightSelectionPosition( 0u ),
83 mPreEditStartPosition( 0u ),
85 mCursorHookPositionX( 0.f ),
86 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
87 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
88 mIsShowingPlaceholderText( false ),
89 mPreEditFlag( false ),
90 mDecoratorUpdated( false ),
91 mCursorBlinkEnabled( true ),
92 mGrabHandleEnabled( true ),
93 mGrabHandlePopupEnabled( true ),
94 mSelectionEnabled( true ),
95 mUpdateCursorHookPosition( false ),
96 mUpdateCursorPosition( false ),
97 mUpdateGrabHandlePosition( false ),
98 mUpdateLeftSelectionPosition( false ),
99 mUpdateRightSelectionPosition( false ),
100 mIsLeftHandleSelected( false ),
101 mIsRightHandleSelected( false ),
102 mUpdateHighlightBox( false ),
103 mScrollAfterUpdatePosition( false ),
104 mScrollAfterDelete( false ),
105 mAllTextSelected( false ),
106 mUpdateInputStyle( false ),
107 mPasswordInput( false )
109 mImfManager = ImfManager::Get();
112 EventData::~EventData()
115 bool Controller::Impl::ProcessInputEvents()
117 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
118 if( NULL == mEventData )
120 // Nothing to do if there is no text input.
121 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
125 if( mEventData->mDecorator )
127 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
128 iter != mEventData->mEventQueue.end();
133 case Event::CURSOR_KEY_EVENT:
135 OnCursorKeyEvent( *iter );
138 case Event::TAP_EVENT:
143 case Event::LONG_PRESS_EVENT:
145 OnLongPressEvent( *iter );
148 case Event::PAN_EVENT:
153 case Event::GRAB_HANDLE_EVENT:
154 case Event::LEFT_SELECTION_HANDLE_EVENT:
155 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
157 OnHandleEvent( *iter );
162 OnSelectEvent( *iter );
165 case Event::SELECT_ALL:
174 if( mEventData->mUpdateCursorPosition ||
175 mEventData->mUpdateHighlightBox )
180 // The cursor must also be repositioned after inserts into the model
181 if( mEventData->mUpdateCursorPosition )
183 // Updates the cursor position and scrolls the text to make it visible.
184 CursorInfo cursorInfo;
185 // Calculate the cursor position from the new cursor index.
186 GetCursorPosition( mEventData->mPrimaryCursorPosition,
189 if( mEventData->mUpdateCursorHookPosition )
191 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
192 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
193 mEventData->mUpdateCursorHookPosition = false;
196 // Scroll first the text after delete ...
197 if( mEventData->mScrollAfterDelete )
199 ScrollTextToMatchCursor( cursorInfo );
202 // ... then, text can be scrolled to make the cursor visible.
203 if( mEventData->mScrollAfterUpdatePosition )
205 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
206 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
208 mEventData->mScrollAfterUpdatePosition = false;
209 mEventData->mScrollAfterDelete = false;
211 UpdateCursorPosition( cursorInfo );
213 mEventData->mDecoratorUpdated = true;
214 mEventData->mUpdateCursorPosition = false;
215 mEventData->mUpdateGrabHandlePosition = false;
219 CursorInfo leftHandleInfo;
220 CursorInfo rightHandleInfo;
222 if( mEventData->mUpdateHighlightBox )
224 GetCursorPosition( mEventData->mLeftSelectionPosition,
227 GetCursorPosition( mEventData->mRightSelectionPosition,
230 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
232 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
234 CursorInfo& infoLeft = leftHandleInfo;
236 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
237 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
239 CursorInfo& infoRight = rightHandleInfo;
241 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
242 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
246 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
248 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
249 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
254 if( mEventData->mUpdateLeftSelectionPosition )
256 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
260 mEventData->mDecoratorUpdated = true;
261 mEventData->mUpdateLeftSelectionPosition = false;
264 if( mEventData->mUpdateRightSelectionPosition )
266 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
270 mEventData->mDecoratorUpdated = true;
271 mEventData->mUpdateRightSelectionPosition = false;
274 if( mEventData->mUpdateHighlightBox )
276 RepositionSelectionHandles();
278 mEventData->mUpdateLeftSelectionPosition = false;
279 mEventData->mUpdateRightSelectionPosition = false;
280 mEventData->mUpdateHighlightBox = false;
281 mEventData->mIsLeftHandleSelected = false;
282 mEventData->mIsRightHandleSelected = false;
285 mEventData->mScrollAfterUpdatePosition = false;
288 if( mEventData->mUpdateInputStyle )
290 // Keep a copy of the current input style.
291 InputStyle currentInputStyle;
292 currentInputStyle.Copy( mEventData->mInputStyle );
294 // Set the default style first.
295 RetrieveDefaultInputStyle( mEventData->mInputStyle );
297 // Get the character index from the cursor index.
298 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
300 // Retrieve the style from the style runs stored in the logical model.
301 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
303 // Compare if the input style has changed.
304 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
306 if( hasInputStyleChanged )
308 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
309 // Queue the input style changed signal.
310 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
313 mEventData->mUpdateInputStyle = false;
316 mEventData->mEventQueue.clear();
318 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
320 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
321 mEventData->mDecoratorUpdated = false;
323 return decoratorUpdated;
326 void Controller::Impl::NotifyImfManager()
328 if( mEventData && mEventData->mImfManager )
330 CharacterIndex cursorPosition = GetLogicalCursorPosition();
332 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
334 // Update the cursor position by removing the initial white spaces.
335 if( cursorPosition < numberOfWhiteSpaces )
341 cursorPosition -= numberOfWhiteSpaces;
344 mEventData->mImfManager.SetCursorPosition( cursorPosition );
345 mEventData->mImfManager.NotifyCursorPosition();
349 void Controller::Impl::NotifyImfMultiLineStatus()
353 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
354 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
358 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
360 CharacterIndex cursorPosition = 0u;
364 if( ( EventData::SELECTING == mEventData->mState ) ||
365 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
367 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
371 cursorPosition = mEventData->mPrimaryCursorPosition;
375 return cursorPosition;
378 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
380 Length numberOfWhiteSpaces = 0u;
382 // Get the buffer to the text.
383 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
385 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
386 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
388 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
394 return numberOfWhiteSpaces;
397 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
399 // Get the total number of characters.
400 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
402 // Retrieve the text.
403 if( 0u != numberOfCharacters )
405 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
409 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
411 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
412 mTextUpdateInfo.mStartGlyphIndex = 0u;
413 mTextUpdateInfo.mStartLineIndex = 0u;
414 numberOfCharacters = 0u;
416 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
417 if( 0u == numberOfParagraphs )
419 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
420 numberOfCharacters = 0u;
422 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
424 // Nothing else to do if there are no paragraphs.
428 // Find the paragraphs to be updated.
429 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
430 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
432 // Text is being added at the end of the current text.
433 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
435 // Text is being added in a new paragraph after the last character of the text.
436 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
437 numberOfCharacters = 0u;
438 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
440 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
441 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
443 // Nothing else to do;
447 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
451 Length numberOfCharactersToUpdate = 0u;
452 if( mTextUpdateInfo.mFullRelayoutNeeded )
454 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
458 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
460 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
461 numberOfCharactersToUpdate,
462 paragraphsToBeUpdated );
465 if( 0u != paragraphsToBeUpdated.Count() )
467 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
468 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
469 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
471 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
472 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
474 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
475 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
476 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
477 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
479 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
480 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
482 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
486 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
490 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
491 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
494 void Controller::Impl::ClearFullModelData( OperationsMask operations )
496 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
498 mModel->mLogicalModel->mLineBreakInfo.Clear();
499 mModel->mLogicalModel->mParagraphInfo.Clear();
502 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
504 mModel->mLogicalModel->mLineBreakInfo.Clear();
507 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
509 mModel->mLogicalModel->mScriptRuns.Clear();
512 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
514 mModel->mLogicalModel->mFontRuns.Clear();
517 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
519 if( NO_OPERATION != ( BIDI_INFO & operations ) )
521 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
522 mModel->mLogicalModel->mCharacterDirections.Clear();
525 if( NO_OPERATION != ( REORDER & operations ) )
527 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
528 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
529 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
533 BidirectionalLineInfoRun& bidiLineInfo = *it;
535 free( bidiLineInfo.visualToLogicalMap );
536 bidiLineInfo.visualToLogicalMap = NULL;
538 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
542 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
544 mModel->mVisualModel->mGlyphs.Clear();
545 mModel->mVisualModel->mGlyphsToCharacters.Clear();
546 mModel->mVisualModel->mCharactersToGlyph.Clear();
547 mModel->mVisualModel->mCharactersPerGlyph.Clear();
548 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
549 mModel->mVisualModel->mGlyphPositions.Clear();
552 if( NO_OPERATION != ( LAYOUT & operations ) )
554 mModel->mVisualModel->mLines.Clear();
557 if( NO_OPERATION != ( COLOR & operations ) )
559 mModel->mVisualModel->mColorIndices.Clear();
563 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
565 const CharacterIndex endIndexPlusOne = endIndex + 1u;
567 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
569 // Clear the line break info.
570 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
572 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
573 lineBreakInfoBuffer + endIndexPlusOne );
575 // Clear the paragraphs.
576 ClearCharacterRuns( startIndex,
578 mModel->mLogicalModel->mParagraphInfo );
581 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
583 // Clear the word break info.
584 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
586 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
587 wordBreakInfoBuffer + endIndexPlusOne );
590 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
592 // Clear the scripts.
593 ClearCharacterRuns( startIndex,
595 mModel->mLogicalModel->mScriptRuns );
598 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
601 ClearCharacterRuns( startIndex,
603 mModel->mLogicalModel->mFontRuns );
606 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
608 if( NO_OPERATION != ( BIDI_INFO & operations ) )
610 // Clear the bidirectional paragraph info.
611 ClearCharacterRuns( startIndex,
613 mModel->mLogicalModel->mBidirectionalParagraphInfo );
615 // Clear the character's directions.
616 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
618 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
619 characterDirectionsBuffer + endIndexPlusOne );
622 if( NO_OPERATION != ( REORDER & operations ) )
624 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
625 uint32_t endRemoveIndex = startRemoveIndex;
626 ClearCharacterRuns( startIndex,
628 mModel->mLogicalModel->mBidirectionalLineInfo,
632 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
634 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
635 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
636 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
640 BidirectionalLineInfoRun& bidiLineInfo = *it;
642 free( bidiLineInfo.visualToLogicalMap );
643 bidiLineInfo.visualToLogicalMap = NULL;
646 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
647 bidirectionalLineInfoBuffer + endRemoveIndex );
652 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
654 const CharacterIndex endIndexPlusOne = endIndex + 1u;
655 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
657 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
658 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
659 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
661 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
662 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
664 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
666 // Update the character to glyph indices.
667 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
668 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
672 CharacterIndex& index = *it;
673 index -= numberOfGlyphsRemoved;
676 // Clear the character to glyph conversion table.
677 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
678 charactersToGlyphBuffer + endIndexPlusOne );
680 // Clear the glyphs per character table.
681 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
682 glyphsPerCharacterBuffer + endIndexPlusOne );
684 // Clear the glyphs buffer.
685 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
686 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
687 glyphsBuffer + endGlyphIndexPlusOne );
689 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
691 // Update the glyph to character indices.
692 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
693 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
697 CharacterIndex& index = *it;
698 index -= numberOfCharactersRemoved;
701 // Clear the glyphs to characters buffer.
702 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
703 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
705 // Clear the characters per glyph buffer.
706 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
707 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
708 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
710 // Clear the positions buffer.
711 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
712 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
713 positionsBuffer + endGlyphIndexPlusOne );
716 if( NO_OPERATION != ( LAYOUT & operations ) )
719 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
720 uint32_t endRemoveIndex = startRemoveIndex;
721 ClearCharacterRuns( startIndex,
723 mModel->mVisualModel->mLines,
727 // Will update the glyph runs.
728 startRemoveIndex = mModel->mVisualModel->mLines.Count();
729 endRemoveIndex = startRemoveIndex;
730 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
731 endGlyphIndexPlusOne - 1u,
732 mModel->mVisualModel->mLines,
736 // Set the line index from where to insert the new laid-out lines.
737 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
739 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
740 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
741 linesBuffer + endRemoveIndex );
744 if( NO_OPERATION != ( COLOR & operations ) )
746 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
748 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
749 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
750 colorIndexBuffer + endGlyphIndexPlusOne );
755 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
757 if( mTextUpdateInfo.mClearAll ||
758 ( ( 0u == startIndex ) &&
759 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
761 ClearFullModelData( operations );
765 // Clear the model data related with characters.
766 ClearCharacterModelData( startIndex, endIndex, operations );
768 // Clear the model data related with glyphs.
769 ClearGlyphModelData( startIndex, endIndex, operations );
772 // The estimated number of lines. Used to avoid reallocations when layouting.
773 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
775 mModel->mVisualModel->ClearCaches();
778 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
780 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
782 // Calculate the operations to be done.
783 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
785 if( NO_OPERATION == operations )
787 // Nothing to do if no operations are pending and required.
791 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
792 Vector<Character> displayCharacters;
793 bool useHiddenText = false;
794 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
796 mHiddenInput->Substitute( srcCharacters,displayCharacters );
797 useHiddenText = true;
800 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
801 const Length numberOfCharacters = utf32Characters.Count();
803 // Index to the first character of the first paragraph to be updated.
804 CharacterIndex startIndex = 0u;
805 // Number of characters of the paragraphs to be removed.
806 Length paragraphCharacters = 0u;
808 CalculateTextUpdateIndices( paragraphCharacters );
809 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
811 if( mTextUpdateInfo.mClearAll ||
812 ( 0u != paragraphCharacters ) )
814 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
817 mTextUpdateInfo.mClearAll = false;
819 // Whether the model is updated.
820 bool updated = false;
822 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
823 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
825 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
827 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
828 // calculate the bidirectional info for each 'paragraph'.
829 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
830 // is not shaped together).
831 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
833 SetLineBreakInfo( utf32Characters,
835 requestedNumberOfCharacters,
838 // Create the paragraph info.
839 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
840 requestedNumberOfCharacters );
844 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
845 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
847 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
848 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
850 SetWordBreakInfo( utf32Characters,
852 requestedNumberOfCharacters,
857 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
858 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
860 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
861 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
863 if( getScripts || validateFonts )
865 // Validates the fonts assigned by the application or assigns default ones.
866 // It makes sure all the characters are going to be rendered by the correct font.
867 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
871 // Retrieves the scripts used in the text.
872 multilanguageSupport.SetScripts( utf32Characters,
874 requestedNumberOfCharacters,
880 // Validate the fonts set through the mark-up string.
881 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
883 // Get the default font's description.
884 TextAbstraction::FontDescription defaultFontDescription;
885 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
886 if( NULL != mFontDefaults )
888 defaultFontDescription = mFontDefaults->mFontDescription;
889 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
892 // Validates the fonts. If there is a character with no assigned font it sets a default one.
893 // After this call, fonts are validated.
894 multilanguageSupport.ValidateFonts( utf32Characters,
897 defaultFontDescription,
900 requestedNumberOfCharacters,
906 Vector<Character> mirroredUtf32Characters;
907 bool textMirrored = false;
908 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
909 if( NO_OPERATION != ( BIDI_INFO & operations ) )
911 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
912 bidirectionalInfo.Reserve( numberOfParagraphs );
914 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
915 SetBidirectionalInfo( utf32Characters,
919 requestedNumberOfCharacters,
922 if( 0u != bidirectionalInfo.Count() )
924 // Only set the character directions if there is right to left characters.
925 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
926 GetCharactersDirection( bidirectionalInfo,
929 requestedNumberOfCharacters,
932 // This paragraph has right to left text. Some characters may need to be mirrored.
933 // TODO: consider if the mirrored string can be stored as well.
935 textMirrored = GetMirroredText( utf32Characters,
939 requestedNumberOfCharacters,
940 mirroredUtf32Characters );
944 // There is no right to left characters. Clear the directions vector.
945 mModel->mLogicalModel->mCharacterDirections.Clear();
950 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
951 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
952 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
953 Vector<GlyphIndex> newParagraphGlyphs;
954 newParagraphGlyphs.Reserve( numberOfParagraphs );
956 const Length currentNumberOfGlyphs = glyphs.Count();
957 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
959 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
961 ShapeText( textToShape,
966 mTextUpdateInfo.mStartGlyphIndex,
967 requestedNumberOfCharacters,
969 glyphsToCharactersMap,
971 newParagraphGlyphs );
973 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
974 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
975 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
979 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
981 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
983 GlyphInfo* glyphsBuffer = glyphs.Begin();
984 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
986 // Update the width and advance of all new paragraph characters.
987 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
989 const GlyphIndex index = *it;
990 GlyphInfo& glyph = *( glyphsBuffer + index );
992 glyph.xBearing = 0.f;
999 if( NO_OPERATION != ( COLOR & operations ) )
1001 // Set the color runs in glyphs.
1002 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1003 mModel->mVisualModel->mCharactersToGlyph,
1004 mModel->mVisualModel->mGlyphsPerCharacter,
1006 mTextUpdateInfo.mStartGlyphIndex,
1007 requestedNumberOfCharacters,
1008 mModel->mVisualModel->mColors,
1009 mModel->mVisualModel->mColorIndices );
1014 if( ( NULL != mEventData ) &&
1015 mEventData->mPreEditFlag &&
1016 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1018 // Add the underline for the pre-edit text.
1019 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1020 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1022 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1023 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1024 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1025 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1027 GlyphRun underlineRun;
1028 underlineRun.glyphIndex = glyphStart;
1029 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1031 // TODO: At the moment the underline runs are only for pre-edit.
1032 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1035 // The estimated number of lines. Used to avoid reallocations when layouting.
1036 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1038 // Set the previous number of characters for the next time the text is updated.
1039 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1044 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1046 // Sets the default text's color.
1047 inputStyle.textColor = mTextColor;
1048 inputStyle.isDefaultColor = true;
1050 inputStyle.familyName.clear();
1051 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1052 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1053 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1054 inputStyle.size = 0.f;
1056 inputStyle.lineSpacing = 0.f;
1058 inputStyle.underlineProperties.clear();
1059 inputStyle.shadowProperties.clear();
1060 inputStyle.embossProperties.clear();
1061 inputStyle.outlineProperties.clear();
1063 inputStyle.isFamilyDefined = false;
1064 inputStyle.isWeightDefined = false;
1065 inputStyle.isWidthDefined = false;
1066 inputStyle.isSlantDefined = false;
1067 inputStyle.isSizeDefined = false;
1069 inputStyle.isLineSpacingDefined = false;
1071 inputStyle.isUnderlineDefined = false;
1072 inputStyle.isShadowDefined = false;
1073 inputStyle.isEmbossDefined = false;
1074 inputStyle.isOutlineDefined = false;
1076 // Sets the default font's family name, weight, width, slant and size.
1079 if( mFontDefaults->familyDefined )
1081 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1082 inputStyle.isFamilyDefined = true;
1085 if( mFontDefaults->weightDefined )
1087 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1088 inputStyle.isWeightDefined = true;
1091 if( mFontDefaults->widthDefined )
1093 inputStyle.width = mFontDefaults->mFontDescription.width;
1094 inputStyle.isWidthDefined = true;
1097 if( mFontDefaults->slantDefined )
1099 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1100 inputStyle.isSlantDefined = true;
1103 if( mFontDefaults->sizeDefined )
1105 inputStyle.size = mFontDefaults->mDefaultPointSize;
1106 inputStyle.isSizeDefined = true;
1111 float Controller::Impl::GetDefaultFontLineHeight()
1113 FontId defaultFontId = 0u;
1114 if( NULL == mFontDefaults )
1116 TextAbstraction::FontDescription fontDescription;
1117 defaultFontId = mFontClient.GetFontId( fontDescription );
1121 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1124 Text::FontMetrics fontMetrics;
1125 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1127 return( fontMetrics.ascender - fontMetrics.descender );
1130 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1132 if( NULL == mEventData )
1134 // Nothing to do if there is no text input.
1138 int keyCode = event.p1.mInt;
1140 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1142 if( mEventData->mPrimaryCursorPosition > 0u )
1144 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1147 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1149 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1151 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1154 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1156 // Get first the line index of the current cursor position index.
1157 CharacterIndex characterIndex = 0u;
1159 if( mEventData->mPrimaryCursorPosition > 0u )
1161 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1164 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1165 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1167 // Retrieve the cursor position info.
1168 CursorInfo cursorInfo;
1169 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1172 // Get the line above.
1173 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1175 // Get the next hit 'y' point.
1176 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1178 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1179 bool matchedCharacter = false;
1180 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1181 mModel->mLogicalModel,
1183 mEventData->mCursorHookPositionX,
1185 CharacterHitTest::TAP,
1188 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1190 // Get first the line index of the current cursor position index.
1191 CharacterIndex characterIndex = 0u;
1193 if( mEventData->mPrimaryCursorPosition > 0u )
1195 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1198 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1200 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1202 // Retrieve the cursor position info.
1203 CursorInfo cursorInfo;
1204 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1207 // Get the line below.
1208 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1210 // Get the next hit 'y' point.
1211 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1213 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1214 bool matchedCharacter = false;
1215 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1216 mModel->mLogicalModel,
1218 mEventData->mCursorHookPositionX,
1220 CharacterHitTest::TAP,
1225 mEventData->mUpdateCursorPosition = true;
1226 mEventData->mUpdateInputStyle = true;
1227 mEventData->mScrollAfterUpdatePosition = true;
1230 void Controller::Impl::OnTapEvent( const Event& event )
1232 if( NULL != mEventData )
1234 const unsigned int tapCount = event.p1.mUint;
1236 if( 1u == tapCount )
1238 if( IsShowingRealText() )
1240 // Convert from control's coords to text's coords.
1241 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1242 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1244 // Keep the tap 'x' position. Used to move the cursor.
1245 mEventData->mCursorHookPositionX = xPosition;
1247 // Whether to touch point hits on a glyph.
1248 bool matchedCharacter = false;
1249 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1250 mModel->mLogicalModel,
1254 CharacterHitTest::TAP,
1257 // When the cursor position is changing, delay cursor blinking
1258 mEventData->mDecorator->DelayCursorBlink();
1262 mEventData->mPrimaryCursorPosition = 0u;
1265 mEventData->mUpdateCursorPosition = true;
1266 mEventData->mUpdateGrabHandlePosition = true;
1267 mEventData->mScrollAfterUpdatePosition = true;
1268 mEventData->mUpdateInputStyle = true;
1270 // Notify the cursor position to the imf manager.
1271 if( mEventData->mImfManager )
1273 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1274 mEventData->mImfManager.NotifyCursorPosition();
1277 else if( 2u == tapCount )
1279 if( mEventData->mSelectionEnabled )
1281 // Convert from control's coords to text's coords.
1282 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1283 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1285 // Calculates the logical position from the x,y coords.
1286 RepositionSelectionHandles( xPosition,
1288 mEventData->mDoubleTapAction );
1294 void Controller::Impl::OnPanEvent( const Event& event )
1296 if( NULL == mEventData )
1298 // Nothing to do if there is no text input.
1302 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1303 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1305 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1307 // Nothing to do if scrolling is not enabled.
1311 const int state = event.p1.mInt;
1315 case Gesture::Started:
1317 // Will remove the cursor, handles or text's popup, ...
1318 ChangeState( EventData::TEXT_PANNING );
1321 case Gesture::Continuing:
1323 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1324 const Vector2 currentScroll = mModel->mScrollPosition;
1326 if( isHorizontalScrollEnabled )
1328 const float displacementX = event.p2.mFloat;
1329 mModel->mScrollPosition.x += displacementX;
1331 ClampHorizontalScroll( layoutSize );
1334 if( isVerticalScrollEnabled )
1336 const float displacementY = event.p3.mFloat;
1337 mModel->mScrollPosition.y += displacementY;
1339 ClampVerticalScroll( layoutSize );
1342 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1345 case Gesture::Finished:
1346 case Gesture::Cancelled: // FALLTHROUGH
1348 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1349 ChangeState( mEventData->mPreviousState );
1357 void Controller::Impl::OnLongPressEvent( const Event& event )
1359 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1361 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1363 ChangeState( EventData::EDITING_WITH_POPUP );
1364 mEventData->mDecoratorUpdated = true;
1365 mEventData->mUpdateInputStyle = true;
1369 if( mEventData->mSelectionEnabled )
1371 // Convert from control's coords to text's coords.
1372 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1373 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1375 // Calculates the logical position from the x,y coords.
1376 RepositionSelectionHandles( xPosition,
1378 mEventData->mLongPressAction );
1383 void Controller::Impl::OnHandleEvent( const Event& event )
1385 if( NULL == mEventData )
1387 // Nothing to do if there is no text input.
1391 const unsigned int state = event.p1.mUint;
1392 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1393 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1395 if( HANDLE_PRESSED == state )
1397 // Convert from decorator's coords to text's coords.
1398 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1399 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1401 // Need to calculate the handle's new position.
1402 bool matchedCharacter = false;
1403 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1404 mModel->mLogicalModel,
1408 CharacterHitTest::SCROLL,
1411 if( Event::GRAB_HANDLE_EVENT == event.type )
1413 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1415 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1417 // Updates the cursor position if the handle's new position is different than the current one.
1418 mEventData->mUpdateCursorPosition = true;
1419 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1420 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1421 mEventData->mPrimaryCursorPosition = handleNewPosition;
1424 // 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.
1425 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1427 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1429 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1431 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1432 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1434 // Updates the highlight box if the handle's new position is different than the current one.
1435 mEventData->mUpdateHighlightBox = true;
1436 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1437 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1438 mEventData->mLeftSelectionPosition = handleNewPosition;
1441 // 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.
1442 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1444 // Will define the order to scroll the text to match the handle position.
1445 mEventData->mIsLeftHandleSelected = true;
1446 mEventData->mIsRightHandleSelected = false;
1448 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1450 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1452 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1453 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1455 // Updates the highlight box if the handle's new position is different than the current one.
1456 mEventData->mUpdateHighlightBox = true;
1457 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1458 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1459 mEventData->mRightSelectionPosition = handleNewPosition;
1462 // 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.
1463 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1465 // Will define the order to scroll the text to match the handle position.
1466 mEventData->mIsLeftHandleSelected = false;
1467 mEventData->mIsRightHandleSelected = true;
1469 } // end ( HANDLE_PRESSED == state )
1470 else if( ( HANDLE_RELEASED == state ) ||
1471 handleStopScrolling )
1473 CharacterIndex handlePosition = 0u;
1474 if( handleStopScrolling || isSmoothHandlePanEnabled )
1476 // Convert from decorator's coords to text's coords.
1477 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1478 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1480 bool matchedCharacter = false;
1481 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1482 mModel->mLogicalModel,
1486 CharacterHitTest::SCROLL,
1490 if( Event::GRAB_HANDLE_EVENT == event.type )
1492 mEventData->mUpdateCursorPosition = true;
1493 mEventData->mUpdateGrabHandlePosition = true;
1494 mEventData->mUpdateInputStyle = true;
1496 if( !IsClipboardEmpty() )
1498 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1501 if( handleStopScrolling || isSmoothHandlePanEnabled )
1503 mEventData->mScrollAfterUpdatePosition = true;
1504 mEventData->mPrimaryCursorPosition = handlePosition;
1507 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1509 ChangeState( EventData::SELECTING );
1511 mEventData->mUpdateHighlightBox = true;
1512 mEventData->mUpdateLeftSelectionPosition = true;
1513 mEventData->mUpdateRightSelectionPosition = true;
1515 if( handleStopScrolling || isSmoothHandlePanEnabled )
1517 mEventData->mScrollAfterUpdatePosition = true;
1519 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1520 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1522 mEventData->mLeftSelectionPosition = handlePosition;
1526 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1528 ChangeState( EventData::SELECTING );
1530 mEventData->mUpdateHighlightBox = true;
1531 mEventData->mUpdateRightSelectionPosition = true;
1532 mEventData->mUpdateLeftSelectionPosition = true;
1534 if( handleStopScrolling || isSmoothHandlePanEnabled )
1536 mEventData->mScrollAfterUpdatePosition = true;
1537 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1538 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1540 mEventData->mRightSelectionPosition = handlePosition;
1545 mEventData->mDecoratorUpdated = true;
1546 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1547 else if( HANDLE_SCROLLING == state )
1549 const float xSpeed = event.p2.mFloat;
1550 const float ySpeed = event.p3.mFloat;
1551 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1552 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1554 mModel->mScrollPosition.x += xSpeed;
1555 mModel->mScrollPosition.y += ySpeed;
1557 ClampHorizontalScroll( layoutSize );
1558 ClampVerticalScroll( layoutSize );
1560 bool endOfScroll = false;
1561 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1563 // Notify the decorator there is no more text to scroll.
1564 // The decorator won't send more scroll events.
1565 mEventData->mDecorator->NotifyEndOfScroll();
1566 // Still need to set the position of the handle.
1570 // Set the position of the handle.
1571 const bool scrollRightDirection = xSpeed > 0.f;
1572 const bool scrollBottomDirection = ySpeed > 0.f;
1573 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1574 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1576 if( Event::GRAB_HANDLE_EVENT == event.type )
1578 ChangeState( EventData::GRAB_HANDLE_PANNING );
1580 // Get the grab handle position in decorator coords.
1581 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1583 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1585 // Position the grag handle close to either the left or right edge.
1586 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1589 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1591 position.x = mEventData->mCursorHookPositionX;
1593 // Position the grag handle close to either the top or bottom edge.
1594 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1597 // Get the new handle position.
1598 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1599 bool matchedCharacter = false;
1600 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1601 mModel->mLogicalModel,
1603 position.x - mModel->mScrollPosition.x,
1604 position.y - mModel->mScrollPosition.y,
1605 CharacterHitTest::SCROLL,
1608 if( mEventData->mPrimaryCursorPosition != handlePosition )
1610 mEventData->mUpdateCursorPosition = true;
1611 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1612 mEventData->mScrollAfterUpdatePosition = true;
1613 mEventData->mPrimaryCursorPosition = handlePosition;
1615 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1617 // Updates the decorator if the soft handle panning is enabled.
1618 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1620 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1622 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1624 // Get the selection handle position in decorator coords.
1625 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1627 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1629 // Position the selection handle close to either the left or right edge.
1630 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1633 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1635 position.x = mEventData->mCursorHookPositionX;
1637 // Position the grag handle close to either the top or bottom edge.
1638 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1641 // Get the new handle position.
1642 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1643 bool matchedCharacter = false;
1644 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1645 mModel->mLogicalModel,
1647 position.x - mModel->mScrollPosition.x,
1648 position.y - mModel->mScrollPosition.y,
1649 CharacterHitTest::SCROLL,
1652 if( leftSelectionHandleEvent )
1654 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1656 if( differentHandles || endOfScroll )
1658 mEventData->mUpdateHighlightBox = true;
1659 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1660 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1661 mEventData->mLeftSelectionPosition = handlePosition;
1666 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1667 if( differentHandles || endOfScroll )
1669 mEventData->mUpdateHighlightBox = true;
1670 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1671 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1672 mEventData->mRightSelectionPosition = handlePosition;
1676 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1678 RepositionSelectionHandles();
1680 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1683 mEventData->mDecoratorUpdated = true;
1684 } // end ( HANDLE_SCROLLING == state )
1687 void Controller::Impl::OnSelectEvent( const Event& event )
1689 if( NULL == mEventData )
1691 // Nothing to do if there is no text.
1695 if( mEventData->mSelectionEnabled )
1697 // Convert from control's coords to text's coords.
1698 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1699 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1701 // Calculates the logical position from the x,y coords.
1702 RepositionSelectionHandles( xPosition,
1704 Controller::NoTextTap::HIGHLIGHT );
1708 void Controller::Impl::OnSelectAllEvent()
1710 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1712 if( NULL == mEventData )
1714 // Nothing to do if there is no text.
1718 if( mEventData->mSelectionEnabled )
1720 ChangeState( EventData::SELECTING );
1722 mEventData->mLeftSelectionPosition = 0u;
1723 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1725 mEventData->mScrollAfterUpdatePosition = true;
1726 mEventData->mUpdateLeftSelectionPosition = true;
1727 mEventData->mUpdateRightSelectionPosition = true;
1728 mEventData->mUpdateHighlightBox = true;
1732 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1734 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1736 // Nothing to select if handles are in the same place.
1737 selectedText.clear();
1741 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1743 //Get start and end position of selection
1744 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1745 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1747 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1748 const Length numberOfCharacters = utf32Characters.Count();
1750 // Validate the start and end selection points
1751 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1753 //Get text as a UTF8 string
1754 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1756 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1758 // Keep a copy of the current input style.
1759 InputStyle currentInputStyle;
1760 currentInputStyle.Copy( mEventData->mInputStyle );
1762 // Set as input style the style of the first deleted character.
1763 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1765 // Compare if the input style has changed.
1766 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1768 if( hasInputStyleChanged )
1770 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1771 // Queue the input style changed signal.
1772 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1775 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1777 // Mark the paragraphs to be updated.
1778 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1779 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1781 // Delete text between handles
1782 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1783 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1784 utf32Characters.Erase( first, last );
1786 // Will show the cursor at the first character of the selection.
1787 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1791 // Will show the cursor at the last character of the selection.
1792 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1795 mEventData->mDecoratorUpdated = true;
1799 void Controller::Impl::ShowClipboard()
1803 mClipboard.ShowClipboard();
1807 void Controller::Impl::HideClipboard()
1809 if( mClipboard && mClipboardHideEnabled )
1811 mClipboard.HideClipboard();
1815 void Controller::Impl::SetClipboardHideEnable(bool enable)
1817 mClipboardHideEnabled = enable;
1820 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1822 //Send string to clipboard
1823 return ( mClipboard && mClipboard.SetItem( source ) );
1826 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1828 std::string selectedText;
1829 RetrieveSelection( selectedText, deleteAfterSending );
1830 CopyStringToClipboard( selectedText );
1831 ChangeState( EventData::EDITING );
1834 void Controller::Impl::RequestGetTextFromClipboard()
1838 mClipboard.RequestItem();
1842 void Controller::Impl::RepositionSelectionHandles()
1844 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1845 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1847 if( selectionStart == selectionEnd )
1849 // Nothing to select if handles are in the same place.
1853 mEventData->mDecorator->ClearHighlights();
1855 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1856 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1857 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1858 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1859 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1860 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1861 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1863 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1864 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1865 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1867 // Swap the indices if the start is greater than the end.
1868 const bool indicesSwapped = selectionStart > selectionEnd;
1870 // Tell the decorator to flip the selection handles if needed.
1871 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1873 if( indicesSwapped )
1875 std::swap( selectionStart, selectionEnd );
1878 // Get the indices to the first and last selected glyphs.
1879 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1880 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1881 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1882 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1884 // Get the lines where the glyphs are laid-out.
1885 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1887 LineIndex lineIndex = 0u;
1888 Length numberOfLines = 0u;
1889 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1890 1u + glyphEnd - glyphStart,
1893 const LineIndex firstLineIndex = lineIndex;
1895 // Create the structure to store some selection box info.
1896 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1897 selectionBoxLinesInfo.Resize( numberOfLines );
1899 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1900 selectionBoxInfo->minX = MAX_FLOAT;
1901 selectionBoxInfo->maxX = MIN_FLOAT;
1903 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1904 float minHighlightX = std::numeric_limits<float>::max();
1905 float maxHighlightX = std::numeric_limits<float>::min();
1907 Vector2 highLightPosition; // The highlight position in decorator's coords.
1909 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1911 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1912 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1915 // Transform to decorator's (control) coords.
1916 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1918 lineRun += firstLineIndex;
1920 // The line height is the addition of the line ascender and the line descender.
1921 // However, the line descender has a negative value, hence the subtraction.
1922 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1924 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1926 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1927 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1928 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1930 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1931 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1932 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1934 // The number of quads of the selection box.
1935 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1936 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1938 // Count the actual number of quads.
1939 unsigned int actualNumberOfQuads = 0u;
1942 // Traverse the glyphs.
1943 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1945 const GlyphInfo& glyph = *( glyphsBuffer + index );
1946 const Vector2& position = *( positionsBuffer + index );
1948 if( splitStartGlyph )
1950 // 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.
1952 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1953 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1954 // Get the direction of the character.
1955 CharacterDirection isCurrentRightToLeft = false;
1956 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1958 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1961 // The end point could be in the middle of the ligature.
1962 // Calculate the number of characters selected.
1963 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1965 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1966 quad.y = selectionBoxInfo->lineOffset;
1967 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1968 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1970 // Store the min and max 'x' for each line.
1971 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1972 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1974 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1975 ++actualNumberOfQuads;
1977 splitStartGlyph = false;
1981 if( splitEndGlyph && ( index == glyphEnd ) )
1983 // 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.
1985 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1986 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1987 // Get the direction of the character.
1988 CharacterDirection isCurrentRightToLeft = false;
1989 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1991 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1994 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1996 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1997 quad.y = selectionBoxInfo->lineOffset;
1998 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1999 quad.w = quad.y + selectionBoxInfo->lineHeight;
2001 // Store the min and max 'x' for each line.
2002 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2003 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2005 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2007 ++actualNumberOfQuads;
2009 splitEndGlyph = false;
2013 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2014 quad.y = selectionBoxInfo->lineOffset;
2015 quad.z = quad.x + glyph.advance;
2016 quad.w = quad.y + selectionBoxInfo->lineHeight;
2018 // Store the min and max 'x' for each line.
2019 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2020 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2022 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2024 ++actualNumberOfQuads;
2026 // Whether to retrieve the next line.
2027 if( index == lastGlyphOfLine )
2030 if( lineIndex < firstLineIndex + numberOfLines )
2032 // Retrieve the next line.
2035 // Get the last glyph of the new line.
2036 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2038 // Keep the offset and height of the current selection box.
2039 const float currentLineOffset = selectionBoxInfo->lineOffset;
2040 const float currentLineHeight = selectionBoxInfo->lineHeight;
2042 // Get the selection box info for the next line.
2045 selectionBoxInfo->minX = MAX_FLOAT;
2046 selectionBoxInfo->maxX = MIN_FLOAT;
2048 // Update the line's vertical offset.
2049 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2051 // The line height is the addition of the line ascender and the line descender.
2052 // However, the line descender has a negative value, hence the subtraction.
2053 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2058 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2059 // The final width is calculated after 'boxifying' the selection.
2060 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2061 endIt = selectionBoxLinesInfo.End();
2065 const SelectionBoxInfo& info = *it;
2067 // Update the size of the highlighted text.
2068 highLightSize.height += info.lineHeight;
2069 minHighlightX = std::min( minHighlightX, info.minX );
2070 maxHighlightX = std::max( maxHighlightX, info.maxX );
2073 // Add extra geometry to 'boxify' the selection.
2075 if( 1u < numberOfLines )
2077 // Boxify the first line.
2078 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2079 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2081 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2082 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2087 quad.y = firstSelectionBoxLineInfo.lineOffset;
2088 quad.z = firstSelectionBoxLineInfo.minX;
2089 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2091 // Boxify at the beginning of the line.
2092 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2094 ++actualNumberOfQuads;
2096 // Update the size of the highlighted text.
2097 minHighlightX = 0.f;
2102 quad.x = firstSelectionBoxLineInfo.maxX;
2103 quad.y = firstSelectionBoxLineInfo.lineOffset;
2104 quad.z = mModel->mVisualModel->mControlSize.width;
2105 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2107 // Boxify at the end of the line.
2108 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2110 ++actualNumberOfQuads;
2112 // Update the size of the highlighted text.
2113 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2116 // Boxify the central lines.
2117 if( 2u < numberOfLines )
2119 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2120 endIt = selectionBoxLinesInfo.End() - 1u;
2124 const SelectionBoxInfo& info = *it;
2127 quad.y = info.lineOffset;
2129 quad.w = info.lineOffset + info.lineHeight;
2131 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2133 ++actualNumberOfQuads;
2136 quad.y = info.lineOffset;
2137 quad.z = mModel->mVisualModel->mControlSize.width;
2138 quad.w = info.lineOffset + info.lineHeight;
2140 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2142 ++actualNumberOfQuads;
2145 // Update the size of the highlighted text.
2146 minHighlightX = 0.f;
2147 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2150 // Boxify the last line.
2151 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2152 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2154 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2155 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2160 quad.y = lastSelectionBoxLineInfo.lineOffset;
2161 quad.z = lastSelectionBoxLineInfo.minX;
2162 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2164 // Boxify at the beginning of the line.
2165 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2167 ++actualNumberOfQuads;
2169 // Update the size of the highlighted text.
2170 minHighlightX = 0.f;
2175 quad.x = lastSelectionBoxLineInfo.maxX;
2176 quad.y = lastSelectionBoxLineInfo.lineOffset;
2177 quad.z = mModel->mVisualModel->mControlSize.width;
2178 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2180 // Boxify at the end of the line.
2181 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2183 ++actualNumberOfQuads;
2185 // Update the size of the highlighted text.
2186 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2190 // Set the actual number of quads.
2191 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2193 // Sets the highlight's size and position. In decorator's coords.
2194 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2195 highLightSize.width = maxHighlightX - minHighlightX;
2197 highLightPosition.x = minHighlightX;
2198 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2199 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2201 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2203 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2205 CursorInfo primaryCursorInfo;
2206 GetCursorPosition( mEventData->mLeftSelectionPosition,
2207 primaryCursorInfo );
2209 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2211 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2213 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2214 primaryCursorInfo.lineHeight );
2216 CursorInfo secondaryCursorInfo;
2217 GetCursorPosition( mEventData->mRightSelectionPosition,
2218 secondaryCursorInfo );
2220 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2222 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2223 secondaryPosition.x,
2224 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2225 secondaryCursorInfo.lineHeight );
2228 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2229 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2231 // Set the flag to update the decorator.
2232 mEventData->mDecoratorUpdated = true;
2235 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2237 if( NULL == mEventData )
2239 // Nothing to do if there is no text input.
2243 if( IsShowingPlaceholderText() )
2245 // Nothing to do if there is the place-holder text.
2249 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2250 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2251 if( ( 0 == numberOfGlyphs ) ||
2252 ( 0 == numberOfLines ) )
2254 // Nothing to do if there is no text.
2258 // Find which word was selected
2259 CharacterIndex selectionStart( 0 );
2260 CharacterIndex selectionEnd( 0 );
2261 CharacterIndex noTextHitIndex( 0 );
2262 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2263 mModel->mLogicalModel,
2270 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2272 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2274 ChangeState( EventData::SELECTING );
2276 mEventData->mLeftSelectionPosition = selectionStart;
2277 mEventData->mRightSelectionPosition = selectionEnd;
2279 mEventData->mUpdateLeftSelectionPosition = true;
2280 mEventData->mUpdateRightSelectionPosition = true;
2281 mEventData->mUpdateHighlightBox = true;
2283 // It may happen an IMF commit event arrives before the selection event
2284 // if the IMF manager is in pre-edit state. The commit event will set the
2285 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2286 // to false, the highlight box won't be updated.
2287 mEventData->mUpdateCursorPosition = false;
2289 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2291 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2293 // Nothing to select. i.e. a white space, out of bounds
2294 ChangeState( EventData::EDITING_WITH_POPUP );
2296 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2298 mEventData->mUpdateCursorPosition = true;
2299 mEventData->mUpdateGrabHandlePosition = true;
2300 mEventData->mScrollAfterUpdatePosition = true;
2301 mEventData->mUpdateInputStyle = true;
2303 else if( Controller::NoTextTap::NO_ACTION == action )
2305 // Nothing to select. i.e. a white space, out of bounds
2306 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2308 mEventData->mUpdateCursorPosition = true;
2309 mEventData->mUpdateGrabHandlePosition = true;
2310 mEventData->mScrollAfterUpdatePosition = true;
2311 mEventData->mUpdateInputStyle = true;
2315 void Controller::Impl::SetPopupButtons()
2318 * Sets the Popup buttons to be shown depending on State.
2320 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2322 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2325 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2327 if( EventData::SELECTING == mEventData->mState )
2329 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2331 if( !IsClipboardEmpty() )
2333 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2334 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2337 if( !mEventData->mAllTextSelected )
2339 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2342 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2344 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2346 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2349 if( !IsClipboardEmpty() )
2351 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2352 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2355 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2357 if ( !IsClipboardEmpty() )
2359 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2360 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2364 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2367 void Controller::Impl::ChangeState( EventData::State newState )
2369 if( NULL == mEventData )
2371 // Nothing to do if there is no text input.
2375 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2377 if( mEventData->mState != newState )
2379 mEventData->mPreviousState = mEventData->mState;
2380 mEventData->mState = newState;
2382 switch( mEventData->mState )
2384 case EventData::INACTIVE:
2386 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2387 mEventData->mDecorator->StopCursorBlink();
2388 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2389 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2390 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2391 mEventData->mDecorator->SetHighlightActive( false );
2392 mEventData->mDecorator->SetPopupActive( false );
2393 mEventData->mDecoratorUpdated = true;
2396 case EventData::INTERRUPTED:
2398 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2399 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2400 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2401 mEventData->mDecorator->SetHighlightActive( false );
2402 mEventData->mDecorator->SetPopupActive( false );
2403 mEventData->mDecoratorUpdated = true;
2406 case EventData::SELECTING:
2408 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2409 mEventData->mDecorator->StopCursorBlink();
2410 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2411 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2412 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2413 mEventData->mDecorator->SetHighlightActive( true );
2414 if( mEventData->mGrabHandlePopupEnabled )
2417 mEventData->mDecorator->SetPopupActive( true );
2419 mEventData->mDecoratorUpdated = true;
2422 case EventData::EDITING:
2424 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2425 if( mEventData->mCursorBlinkEnabled )
2427 mEventData->mDecorator->StartCursorBlink();
2429 // Grab handle is not shown until a tap is received whilst EDITING
2430 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2431 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2432 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2433 mEventData->mDecorator->SetHighlightActive( false );
2434 if( mEventData->mGrabHandlePopupEnabled )
2436 mEventData->mDecorator->SetPopupActive( false );
2438 mEventData->mDecoratorUpdated = true;
2441 case EventData::EDITING_WITH_POPUP:
2443 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2445 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2446 if( mEventData->mCursorBlinkEnabled )
2448 mEventData->mDecorator->StartCursorBlink();
2450 if( mEventData->mSelectionEnabled )
2452 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2453 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2454 mEventData->mDecorator->SetHighlightActive( false );
2458 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2460 if( mEventData->mGrabHandlePopupEnabled )
2463 mEventData->mDecorator->SetPopupActive( true );
2465 mEventData->mDecoratorUpdated = true;
2468 case EventData::EDITING_WITH_GRAB_HANDLE:
2470 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2472 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2473 if( mEventData->mCursorBlinkEnabled )
2475 mEventData->mDecorator->StartCursorBlink();
2477 // Grab handle is not shown until a tap is received whilst EDITING
2478 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2479 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2480 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2481 mEventData->mDecorator->SetHighlightActive( false );
2482 if( mEventData->mGrabHandlePopupEnabled )
2484 mEventData->mDecorator->SetPopupActive( false );
2486 mEventData->mDecoratorUpdated = true;
2489 case EventData::SELECTION_HANDLE_PANNING:
2491 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2492 mEventData->mDecorator->StopCursorBlink();
2493 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2494 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2495 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2496 mEventData->mDecorator->SetHighlightActive( true );
2497 if( mEventData->mGrabHandlePopupEnabled )
2499 mEventData->mDecorator->SetPopupActive( false );
2501 mEventData->mDecoratorUpdated = true;
2504 case EventData::GRAB_HANDLE_PANNING:
2506 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2508 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2509 if( mEventData->mCursorBlinkEnabled )
2511 mEventData->mDecorator->StartCursorBlink();
2513 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2514 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2515 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2516 mEventData->mDecorator->SetHighlightActive( false );
2517 if( mEventData->mGrabHandlePopupEnabled )
2519 mEventData->mDecorator->SetPopupActive( false );
2521 mEventData->mDecoratorUpdated = true;
2524 case EventData::EDITING_WITH_PASTE_POPUP:
2526 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2528 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2529 if( mEventData->mCursorBlinkEnabled )
2531 mEventData->mDecorator->StartCursorBlink();
2534 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2535 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2536 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2537 mEventData->mDecorator->SetHighlightActive( false );
2539 if( mEventData->mGrabHandlePopupEnabled )
2542 mEventData->mDecorator->SetPopupActive( true );
2544 mEventData->mDecoratorUpdated = true;
2547 case EventData::TEXT_PANNING:
2549 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2550 mEventData->mDecorator->StopCursorBlink();
2551 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2552 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2553 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2555 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2556 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2557 mEventData->mDecorator->SetHighlightActive( true );
2560 if( mEventData->mGrabHandlePopupEnabled )
2562 mEventData->mDecorator->SetPopupActive( false );
2565 mEventData->mDecoratorUpdated = true;
2572 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2573 CursorInfo& cursorInfo )
2575 if( !IsShowingRealText() )
2577 // Do not want to use the place-holder text to set the cursor position.
2579 // Use the line's height of the font's family set to set the cursor's size.
2580 // If there is no font's family set, use the default font.
2581 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2583 cursorInfo.lineOffset = 0.f;
2584 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2585 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2587 switch( mModel->mHorizontalAlignment )
2589 case Layout::HORIZONTAL_ALIGN_BEGIN:
2591 cursorInfo.primaryPosition.x = 0.f;
2594 case Layout::HORIZONTAL_ALIGN_CENTER:
2596 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2599 case Layout::HORIZONTAL_ALIGN_END:
2601 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2606 // Nothing else to do.
2610 Text::GetCursorPosition( mModel->mVisualModel,
2611 mModel->mLogicalModel,
2616 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2618 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2620 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2621 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2623 if( 0.f > cursorInfo.primaryPosition.x )
2625 cursorInfo.primaryPosition.x = 0.f;
2628 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2629 if( cursorInfo.primaryPosition.x > edgeWidth )
2631 cursorInfo.primaryPosition.x = edgeWidth;
2636 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2638 if( NULL == mEventData )
2640 // Nothing to do if there is no text input.
2644 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2646 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2647 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2649 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2650 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2652 if( numberOfCharacters > 1u )
2654 const Script script = mModel->mLogicalModel->GetScript( index );
2655 if( HasLigatureMustBreak( script ) )
2657 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2658 numberOfCharacters = 1u;
2663 while( 0u == numberOfCharacters )
2666 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2670 if( index < mEventData->mPrimaryCursorPosition )
2672 cursorIndex -= numberOfCharacters;
2676 cursorIndex += numberOfCharacters;
2679 // Will update the cursor hook position.
2680 mEventData->mUpdateCursorHookPosition = true;
2685 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2687 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2688 if( NULL == mEventData )
2690 // Nothing to do if there is no text input.
2691 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2695 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2697 // Sets the cursor position.
2698 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2701 cursorInfo.primaryCursorHeight,
2702 cursorInfo.lineHeight );
2703 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2705 if( mEventData->mUpdateGrabHandlePosition )
2707 // Sets the grab handle position.
2708 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2710 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2711 cursorInfo.lineHeight );
2714 if( cursorInfo.isSecondaryCursor )
2716 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2717 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2718 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2719 cursorInfo.secondaryCursorHeight,
2720 cursorInfo.lineHeight );
2721 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2724 // Set which cursors are active according the state.
2725 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2727 if( cursorInfo.isSecondaryCursor )
2729 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2733 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2738 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2741 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2744 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2745 const CursorInfo& cursorInfo )
2747 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2748 ( RIGHT_SELECTION_HANDLE != handleType ) )
2753 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2755 // Sets the handle's position.
2756 mEventData->mDecorator->SetPosition( handleType,
2758 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2759 cursorInfo.lineHeight );
2761 // If selection handle at start of the text and other at end of the text then all text is selected.
2762 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2763 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2764 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2767 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2769 // Clamp between -space & -alignment offset.
2771 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2773 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2774 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2775 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2777 mEventData->mDecoratorUpdated = true;
2781 mModel->mScrollPosition.x = 0.f;
2785 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2787 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2789 // Nothing to do if the text is single line.
2793 // Clamp between -space & 0.
2794 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2796 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2797 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2798 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2800 mEventData->mDecoratorUpdated = true;
2804 mModel->mScrollPosition.y = 0.f;
2808 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2810 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2812 // position is in actor's coords.
2813 const float positionEndX = position.x + cursorWidth;
2814 const float positionEndY = position.y + lineHeight;
2816 // Transform the position to decorator coords.
2817 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2818 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2820 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2821 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2823 if( decoratorPositionBeginX < 0.f )
2825 mModel->mScrollPosition.x = -position.x;
2827 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2829 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2832 if( decoratorPositionBeginY < 0.f )
2834 mModel->mScrollPosition.y = -position.y;
2836 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2838 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2842 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2844 // Get the current cursor position in decorator coords.
2845 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2847 // Calculate the offset to match the cursor position before the character was deleted.
2848 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2849 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2851 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2852 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2854 // Makes the new cursor position visible if needed.
2855 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2858 void Controller::Impl::RequestRelayout()
2860 if( NULL != mControlInterface )
2862 mControlInterface->RequestTextRelayout();
2868 } // namespace Toolkit