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 mPlaceholderFont( NULL ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false ),
109 mIsPlaceholderPixelSize( false ),
110 mIsPlaceholderElideEnabled( false ),
111 mPlaceholderEllipsisFlag( false )
113 mImfManager = ImfManager::Get();
116 EventData::~EventData()
119 bool Controller::Impl::ProcessInputEvents()
121 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
122 if( NULL == mEventData )
124 // Nothing to do if there is no text input.
125 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
129 if( mEventData->mDecorator )
131 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
132 iter != mEventData->mEventQueue.end();
137 case Event::CURSOR_KEY_EVENT:
139 OnCursorKeyEvent( *iter );
142 case Event::TAP_EVENT:
147 case Event::LONG_PRESS_EVENT:
149 OnLongPressEvent( *iter );
152 case Event::PAN_EVENT:
157 case Event::GRAB_HANDLE_EVENT:
158 case Event::LEFT_SELECTION_HANDLE_EVENT:
159 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
161 OnHandleEvent( *iter );
166 OnSelectEvent( *iter );
169 case Event::SELECT_ALL:
178 if( mEventData->mUpdateCursorPosition ||
179 mEventData->mUpdateHighlightBox )
184 // The cursor must also be repositioned after inserts into the model
185 if( mEventData->mUpdateCursorPosition )
187 // Updates the cursor position and scrolls the text to make it visible.
188 CursorInfo cursorInfo;
189 // Calculate the cursor position from the new cursor index.
190 GetCursorPosition( mEventData->mPrimaryCursorPosition,
193 if( mEventData->mUpdateCursorHookPosition )
195 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
196 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
197 mEventData->mUpdateCursorHookPosition = false;
200 // Scroll first the text after delete ...
201 if( mEventData->mScrollAfterDelete )
203 ScrollTextToMatchCursor( cursorInfo );
206 // ... then, text can be scrolled to make the cursor visible.
207 if( mEventData->mScrollAfterUpdatePosition )
209 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
210 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
212 mEventData->mScrollAfterUpdatePosition = false;
213 mEventData->mScrollAfterDelete = false;
215 UpdateCursorPosition( cursorInfo );
217 mEventData->mDecoratorUpdated = true;
218 mEventData->mUpdateCursorPosition = false;
219 mEventData->mUpdateGrabHandlePosition = false;
223 CursorInfo leftHandleInfo;
224 CursorInfo rightHandleInfo;
226 if( mEventData->mUpdateHighlightBox )
228 GetCursorPosition( mEventData->mLeftSelectionPosition,
231 GetCursorPosition( mEventData->mRightSelectionPosition,
234 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
236 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
238 CursorInfo& infoLeft = leftHandleInfo;
240 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
241 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
243 CursorInfo& infoRight = rightHandleInfo;
245 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
246 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
250 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
252 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
253 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
258 if( mEventData->mUpdateLeftSelectionPosition )
260 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
264 mEventData->mDecoratorUpdated = true;
265 mEventData->mUpdateLeftSelectionPosition = false;
268 if( mEventData->mUpdateRightSelectionPosition )
270 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
274 mEventData->mDecoratorUpdated = true;
275 mEventData->mUpdateRightSelectionPosition = false;
278 if( mEventData->mUpdateHighlightBox )
280 RepositionSelectionHandles();
282 mEventData->mUpdateLeftSelectionPosition = false;
283 mEventData->mUpdateRightSelectionPosition = false;
284 mEventData->mUpdateHighlightBox = false;
285 mEventData->mIsLeftHandleSelected = false;
286 mEventData->mIsRightHandleSelected = false;
289 mEventData->mScrollAfterUpdatePosition = false;
292 if( mEventData->mUpdateInputStyle )
294 // Keep a copy of the current input style.
295 InputStyle currentInputStyle;
296 currentInputStyle.Copy( mEventData->mInputStyle );
298 // Set the default style first.
299 RetrieveDefaultInputStyle( mEventData->mInputStyle );
301 // Get the character index from the cursor index.
302 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
304 // Retrieve the style from the style runs stored in the logical model.
305 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
307 // Compare if the input style has changed.
308 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
310 if( hasInputStyleChanged )
312 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
313 // Queue the input style changed signal.
314 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
317 mEventData->mUpdateInputStyle = false;
320 mEventData->mEventQueue.clear();
322 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
324 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
325 mEventData->mDecoratorUpdated = false;
327 return decoratorUpdated;
330 void Controller::Impl::NotifyImfManager()
332 if( mEventData && mEventData->mImfManager )
334 CharacterIndex cursorPosition = GetLogicalCursorPosition();
336 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
338 // Update the cursor position by removing the initial white spaces.
339 if( cursorPosition < numberOfWhiteSpaces )
345 cursorPosition -= numberOfWhiteSpaces;
348 mEventData->mImfManager.SetCursorPosition( cursorPosition );
349 mEventData->mImfManager.NotifyCursorPosition();
353 void Controller::Impl::NotifyImfMultiLineStatus()
357 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
358 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
362 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
364 CharacterIndex cursorPosition = 0u;
368 if( ( EventData::SELECTING == mEventData->mState ) ||
369 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
371 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
375 cursorPosition = mEventData->mPrimaryCursorPosition;
379 return cursorPosition;
382 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
384 Length numberOfWhiteSpaces = 0u;
386 // Get the buffer to the text.
387 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
389 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
390 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
392 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
398 return numberOfWhiteSpaces;
401 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
403 // Get the total number of characters.
404 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
406 // Retrieve the text.
407 if( 0u != numberOfCharacters )
409 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
413 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
415 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
416 mTextUpdateInfo.mStartGlyphIndex = 0u;
417 mTextUpdateInfo.mStartLineIndex = 0u;
418 numberOfCharacters = 0u;
420 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
421 if( 0u == numberOfParagraphs )
423 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
424 numberOfCharacters = 0u;
426 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
428 // Nothing else to do if there are no paragraphs.
432 // Find the paragraphs to be updated.
433 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
434 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
436 // Text is being added at the end of the current text.
437 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
439 // Text is being added in a new paragraph after the last character of the text.
440 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
441 numberOfCharacters = 0u;
442 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
444 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
445 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
447 // Nothing else to do;
451 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
455 Length numberOfCharactersToUpdate = 0u;
456 if( mTextUpdateInfo.mFullRelayoutNeeded )
458 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
462 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
464 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
465 numberOfCharactersToUpdate,
466 paragraphsToBeUpdated );
469 if( 0u != paragraphsToBeUpdated.Count() )
471 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
472 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
473 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
475 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
476 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
478 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
479 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
480 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
481 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
483 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
484 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
486 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
490 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
494 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
495 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
498 void Controller::Impl::ClearFullModelData( OperationsMask operations )
500 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
502 mModel->mLogicalModel->mLineBreakInfo.Clear();
503 mModel->mLogicalModel->mParagraphInfo.Clear();
506 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
508 mModel->mLogicalModel->mLineBreakInfo.Clear();
511 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
513 mModel->mLogicalModel->mScriptRuns.Clear();
516 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
518 mModel->mLogicalModel->mFontRuns.Clear();
521 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
523 if( NO_OPERATION != ( BIDI_INFO & operations ) )
525 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
526 mModel->mLogicalModel->mCharacterDirections.Clear();
529 if( NO_OPERATION != ( REORDER & operations ) )
531 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
532 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
533 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
537 BidirectionalLineInfoRun& bidiLineInfo = *it;
539 free( bidiLineInfo.visualToLogicalMap );
540 bidiLineInfo.visualToLogicalMap = NULL;
542 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
546 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
548 mModel->mVisualModel->mGlyphs.Clear();
549 mModel->mVisualModel->mGlyphsToCharacters.Clear();
550 mModel->mVisualModel->mCharactersToGlyph.Clear();
551 mModel->mVisualModel->mCharactersPerGlyph.Clear();
552 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
553 mModel->mVisualModel->mGlyphPositions.Clear();
556 if( NO_OPERATION != ( LAYOUT & operations ) )
558 mModel->mVisualModel->mLines.Clear();
561 if( NO_OPERATION != ( COLOR & operations ) )
563 mModel->mVisualModel->mColorIndices.Clear();
567 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
569 const CharacterIndex endIndexPlusOne = endIndex + 1u;
571 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
573 // Clear the line break info.
574 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
576 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
577 lineBreakInfoBuffer + endIndexPlusOne );
579 // Clear the paragraphs.
580 ClearCharacterRuns( startIndex,
582 mModel->mLogicalModel->mParagraphInfo );
585 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
587 // Clear the word break info.
588 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
590 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
591 wordBreakInfoBuffer + endIndexPlusOne );
594 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
596 // Clear the scripts.
597 ClearCharacterRuns( startIndex,
599 mModel->mLogicalModel->mScriptRuns );
602 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
605 ClearCharacterRuns( startIndex,
607 mModel->mLogicalModel->mFontRuns );
610 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
612 if( NO_OPERATION != ( BIDI_INFO & operations ) )
614 // Clear the bidirectional paragraph info.
615 ClearCharacterRuns( startIndex,
617 mModel->mLogicalModel->mBidirectionalParagraphInfo );
619 // Clear the character's directions.
620 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
622 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
623 characterDirectionsBuffer + endIndexPlusOne );
626 if( NO_OPERATION != ( REORDER & operations ) )
628 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
629 uint32_t endRemoveIndex = startRemoveIndex;
630 ClearCharacterRuns( startIndex,
632 mModel->mLogicalModel->mBidirectionalLineInfo,
636 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
638 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
639 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
640 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
644 BidirectionalLineInfoRun& bidiLineInfo = *it;
646 free( bidiLineInfo.visualToLogicalMap );
647 bidiLineInfo.visualToLogicalMap = NULL;
650 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
651 bidirectionalLineInfoBuffer + endRemoveIndex );
656 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
658 const CharacterIndex endIndexPlusOne = endIndex + 1u;
659 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
661 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
662 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
663 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
665 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
666 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
668 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
670 // Update the character to glyph indices.
671 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
672 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
676 CharacterIndex& index = *it;
677 index -= numberOfGlyphsRemoved;
680 // Clear the character to glyph conversion table.
681 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
682 charactersToGlyphBuffer + endIndexPlusOne );
684 // Clear the glyphs per character table.
685 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
686 glyphsPerCharacterBuffer + endIndexPlusOne );
688 // Clear the glyphs buffer.
689 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
690 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
691 glyphsBuffer + endGlyphIndexPlusOne );
693 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
695 // Update the glyph to character indices.
696 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
697 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
701 CharacterIndex& index = *it;
702 index -= numberOfCharactersRemoved;
705 // Clear the glyphs to characters buffer.
706 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
707 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
709 // Clear the characters per glyph buffer.
710 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
711 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
712 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
714 // Clear the positions buffer.
715 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
716 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
717 positionsBuffer + endGlyphIndexPlusOne );
720 if( NO_OPERATION != ( LAYOUT & operations ) )
723 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
724 uint32_t endRemoveIndex = startRemoveIndex;
725 ClearCharacterRuns( startIndex,
727 mModel->mVisualModel->mLines,
731 // Will update the glyph runs.
732 startRemoveIndex = mModel->mVisualModel->mLines.Count();
733 endRemoveIndex = startRemoveIndex;
734 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
735 endGlyphIndexPlusOne - 1u,
736 mModel->mVisualModel->mLines,
740 // Set the line index from where to insert the new laid-out lines.
741 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
743 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
744 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
745 linesBuffer + endRemoveIndex );
748 if( NO_OPERATION != ( COLOR & operations ) )
750 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
752 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
753 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
754 colorIndexBuffer + endGlyphIndexPlusOne );
759 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
761 if( mTextUpdateInfo.mClearAll ||
762 ( ( 0u == startIndex ) &&
763 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
765 ClearFullModelData( operations );
769 // Clear the model data related with characters.
770 ClearCharacterModelData( startIndex, endIndex, operations );
772 // Clear the model data related with glyphs.
773 ClearGlyphModelData( startIndex, endIndex, operations );
776 // The estimated number of lines. Used to avoid reallocations when layouting.
777 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
779 mModel->mVisualModel->ClearCaches();
782 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
784 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
786 // Calculate the operations to be done.
787 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
789 if( NO_OPERATION == operations )
791 // Nothing to do if no operations are pending and required.
795 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
796 Vector<Character> displayCharacters;
797 bool useHiddenText = false;
798 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
800 mHiddenInput->Substitute( srcCharacters,displayCharacters );
801 useHiddenText = true;
804 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
805 const Length numberOfCharacters = utf32Characters.Count();
807 // Index to the first character of the first paragraph to be updated.
808 CharacterIndex startIndex = 0u;
809 // Number of characters of the paragraphs to be removed.
810 Length paragraphCharacters = 0u;
812 CalculateTextUpdateIndices( paragraphCharacters );
813 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
815 if( mTextUpdateInfo.mClearAll ||
816 ( 0u != paragraphCharacters ) )
818 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
821 mTextUpdateInfo.mClearAll = false;
823 // Whether the model is updated.
824 bool updated = false;
826 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
827 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
829 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
831 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
832 // calculate the bidirectional info for each 'paragraph'.
833 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
834 // is not shaped together).
835 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
837 SetLineBreakInfo( utf32Characters,
839 requestedNumberOfCharacters,
842 // Create the paragraph info.
843 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
844 requestedNumberOfCharacters );
848 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
849 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
851 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
852 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
854 SetWordBreakInfo( utf32Characters,
856 requestedNumberOfCharacters,
861 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
862 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
864 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
865 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
867 if( getScripts || validateFonts )
869 // Validates the fonts assigned by the application or assigns default ones.
870 // It makes sure all the characters are going to be rendered by the correct font.
871 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
875 // Retrieves the scripts used in the text.
876 multilanguageSupport.SetScripts( utf32Characters,
878 requestedNumberOfCharacters,
884 // Validate the fonts set through the mark-up string.
885 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
887 // Get the default font's description.
888 TextAbstraction::FontDescription defaultFontDescription;
889 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
891 if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) )
893 // If the placeholder font is set specifically, only placeholder font is changed.
894 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
895 if( mEventData->mPlaceholderFont->sizeDefined )
897 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
900 else if( NULL != mFontDefaults )
902 // Set the normal font and the placeholder font.
903 defaultFontDescription = mFontDefaults->mFontDescription;
904 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
907 // Validates the fonts. If there is a character with no assigned font it sets a default one.
908 // After this call, fonts are validated.
909 multilanguageSupport.ValidateFonts( utf32Characters,
912 defaultFontDescription,
915 requestedNumberOfCharacters,
921 Vector<Character> mirroredUtf32Characters;
922 bool textMirrored = false;
923 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
924 if( NO_OPERATION != ( BIDI_INFO & operations ) )
926 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
927 bidirectionalInfo.Reserve( numberOfParagraphs );
929 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
930 SetBidirectionalInfo( utf32Characters,
934 requestedNumberOfCharacters,
937 if( 0u != bidirectionalInfo.Count() )
939 // Only set the character directions if there is right to left characters.
940 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
941 GetCharactersDirection( bidirectionalInfo,
944 requestedNumberOfCharacters,
947 // This paragraph has right to left text. Some characters may need to be mirrored.
948 // TODO: consider if the mirrored string can be stored as well.
950 textMirrored = GetMirroredText( utf32Characters,
954 requestedNumberOfCharacters,
955 mirroredUtf32Characters );
959 // There is no right to left characters. Clear the directions vector.
960 mModel->mLogicalModel->mCharacterDirections.Clear();
965 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
966 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
967 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
968 Vector<GlyphIndex> newParagraphGlyphs;
969 newParagraphGlyphs.Reserve( numberOfParagraphs );
971 const Length currentNumberOfGlyphs = glyphs.Count();
972 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
974 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
976 ShapeText( textToShape,
981 mTextUpdateInfo.mStartGlyphIndex,
982 requestedNumberOfCharacters,
984 glyphsToCharactersMap,
986 newParagraphGlyphs );
988 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
989 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
990 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
994 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
996 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
998 GlyphInfo* glyphsBuffer = glyphs.Begin();
999 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1001 // Update the width and advance of all new paragraph characters.
1002 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1004 const GlyphIndex index = *it;
1005 GlyphInfo& glyph = *( glyphsBuffer + index );
1007 glyph.xBearing = 0.f;
1009 glyph.advance = 0.f;
1014 if( NO_OPERATION != ( COLOR & operations ) )
1016 // Set the color runs in glyphs.
1017 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1018 mModel->mVisualModel->mCharactersToGlyph,
1019 mModel->mVisualModel->mGlyphsPerCharacter,
1021 mTextUpdateInfo.mStartGlyphIndex,
1022 requestedNumberOfCharacters,
1023 mModel->mVisualModel->mColors,
1024 mModel->mVisualModel->mColorIndices );
1029 if( ( NULL != mEventData ) &&
1030 mEventData->mPreEditFlag &&
1031 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1033 // Add the underline for the pre-edit text.
1034 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1035 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1037 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1038 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1039 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1040 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1042 GlyphRun underlineRun;
1043 underlineRun.glyphIndex = glyphStart;
1044 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1046 // TODO: At the moment the underline runs are only for pre-edit.
1047 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1050 // The estimated number of lines. Used to avoid reallocations when layouting.
1051 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1053 // Set the previous number of characters for the next time the text is updated.
1054 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1059 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1061 // Sets the default text's color.
1062 inputStyle.textColor = mTextColor;
1063 inputStyle.isDefaultColor = true;
1065 inputStyle.familyName.clear();
1066 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1067 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1068 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1069 inputStyle.size = 0.f;
1071 inputStyle.lineSpacing = 0.f;
1073 inputStyle.underlineProperties.clear();
1074 inputStyle.shadowProperties.clear();
1075 inputStyle.embossProperties.clear();
1076 inputStyle.outlineProperties.clear();
1078 inputStyle.isFamilyDefined = false;
1079 inputStyle.isWeightDefined = false;
1080 inputStyle.isWidthDefined = false;
1081 inputStyle.isSlantDefined = false;
1082 inputStyle.isSizeDefined = false;
1084 inputStyle.isLineSpacingDefined = false;
1086 inputStyle.isUnderlineDefined = false;
1087 inputStyle.isShadowDefined = false;
1088 inputStyle.isEmbossDefined = false;
1089 inputStyle.isOutlineDefined = false;
1091 // Sets the default font's family name, weight, width, slant and size.
1094 if( mFontDefaults->familyDefined )
1096 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1097 inputStyle.isFamilyDefined = true;
1100 if( mFontDefaults->weightDefined )
1102 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1103 inputStyle.isWeightDefined = true;
1106 if( mFontDefaults->widthDefined )
1108 inputStyle.width = mFontDefaults->mFontDescription.width;
1109 inputStyle.isWidthDefined = true;
1112 if( mFontDefaults->slantDefined )
1114 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1115 inputStyle.isSlantDefined = true;
1118 if( mFontDefaults->sizeDefined )
1120 inputStyle.size = mFontDefaults->mDefaultPointSize;
1121 inputStyle.isSizeDefined = true;
1126 float Controller::Impl::GetDefaultFontLineHeight()
1128 FontId defaultFontId = 0u;
1129 if( NULL == mFontDefaults )
1131 TextAbstraction::FontDescription fontDescription;
1132 defaultFontId = mFontClient.GetFontId( fontDescription );
1136 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1139 Text::FontMetrics fontMetrics;
1140 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1142 return( fontMetrics.ascender - fontMetrics.descender );
1145 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1147 if( NULL == mEventData )
1149 // Nothing to do if there is no text input.
1153 int keyCode = event.p1.mInt;
1155 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1157 if( mEventData->mPrimaryCursorPosition > 0u )
1159 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1162 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1164 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1166 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1169 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1171 // Get first the line index of the current cursor position index.
1172 CharacterIndex characterIndex = 0u;
1174 if( mEventData->mPrimaryCursorPosition > 0u )
1176 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1179 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1180 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1182 // Retrieve the cursor position info.
1183 CursorInfo cursorInfo;
1184 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1187 // Get the line above.
1188 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1190 // Get the next hit 'y' point.
1191 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1193 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1194 bool matchedCharacter = false;
1195 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1196 mModel->mLogicalModel,
1198 mEventData->mCursorHookPositionX,
1200 CharacterHitTest::TAP,
1203 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1205 // Get first the line index of the current cursor position index.
1206 CharacterIndex characterIndex = 0u;
1208 if( mEventData->mPrimaryCursorPosition > 0u )
1210 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1213 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1215 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1217 // Retrieve the cursor position info.
1218 CursorInfo cursorInfo;
1219 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1222 // Get the line below.
1223 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1225 // Get the next hit 'y' point.
1226 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1228 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1229 bool matchedCharacter = false;
1230 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1231 mModel->mLogicalModel,
1233 mEventData->mCursorHookPositionX,
1235 CharacterHitTest::TAP,
1240 mEventData->mUpdateCursorPosition = true;
1241 mEventData->mUpdateInputStyle = true;
1242 mEventData->mScrollAfterUpdatePosition = true;
1245 void Controller::Impl::OnTapEvent( const Event& event )
1247 if( NULL != mEventData )
1249 const unsigned int tapCount = event.p1.mUint;
1251 if( 1u == tapCount )
1253 if( IsShowingRealText() )
1255 // Convert from control's coords to text's coords.
1256 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1257 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1259 // Keep the tap 'x' position. Used to move the cursor.
1260 mEventData->mCursorHookPositionX = xPosition;
1262 // Whether to touch point hits on a glyph.
1263 bool matchedCharacter = false;
1264 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1265 mModel->mLogicalModel,
1269 CharacterHitTest::TAP,
1272 // When the cursor position is changing, delay cursor blinking
1273 mEventData->mDecorator->DelayCursorBlink();
1277 mEventData->mPrimaryCursorPosition = 0u;
1280 mEventData->mUpdateCursorPosition = true;
1281 mEventData->mUpdateGrabHandlePosition = true;
1282 mEventData->mScrollAfterUpdatePosition = true;
1283 mEventData->mUpdateInputStyle = true;
1285 // Notify the cursor position to the imf manager.
1286 if( mEventData->mImfManager )
1288 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1289 mEventData->mImfManager.NotifyCursorPosition();
1292 else if( 2u == tapCount )
1294 if( mEventData->mSelectionEnabled )
1296 // Convert from control's coords to text's coords.
1297 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1298 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1300 // Calculates the logical position from the x,y coords.
1301 RepositionSelectionHandles( xPosition,
1303 mEventData->mDoubleTapAction );
1309 void Controller::Impl::OnPanEvent( const Event& event )
1311 if( NULL == mEventData )
1313 // Nothing to do if there is no text input.
1317 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1318 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1320 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1322 // Nothing to do if scrolling is not enabled.
1326 const int state = event.p1.mInt;
1330 case Gesture::Started:
1332 // Will remove the cursor, handles or text's popup, ...
1333 ChangeState( EventData::TEXT_PANNING );
1336 case Gesture::Continuing:
1338 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1339 const Vector2 currentScroll = mModel->mScrollPosition;
1341 if( isHorizontalScrollEnabled )
1343 const float displacementX = event.p2.mFloat;
1344 mModel->mScrollPosition.x += displacementX;
1346 ClampHorizontalScroll( layoutSize );
1349 if( isVerticalScrollEnabled )
1351 const float displacementY = event.p3.mFloat;
1352 mModel->mScrollPosition.y += displacementY;
1354 ClampVerticalScroll( layoutSize );
1357 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1360 case Gesture::Finished:
1361 case Gesture::Cancelled: // FALLTHROUGH
1363 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1364 ChangeState( mEventData->mPreviousState );
1372 void Controller::Impl::OnLongPressEvent( const Event& event )
1374 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1376 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1378 ChangeState( EventData::EDITING_WITH_POPUP );
1379 mEventData->mDecoratorUpdated = true;
1380 mEventData->mUpdateInputStyle = true;
1384 if( mEventData->mSelectionEnabled )
1386 // Convert from control's coords to text's coords.
1387 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1388 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1390 // Calculates the logical position from the x,y coords.
1391 RepositionSelectionHandles( xPosition,
1393 mEventData->mLongPressAction );
1398 void Controller::Impl::OnHandleEvent( const Event& event )
1400 if( NULL == mEventData )
1402 // Nothing to do if there is no text input.
1406 const unsigned int state = event.p1.mUint;
1407 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1408 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1410 if( HANDLE_PRESSED == state )
1412 // Convert from decorator's coords to text's coords.
1413 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1414 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1416 // Need to calculate the handle's new position.
1417 bool matchedCharacter = false;
1418 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1419 mModel->mLogicalModel,
1423 CharacterHitTest::SCROLL,
1426 if( Event::GRAB_HANDLE_EVENT == event.type )
1428 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1430 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1432 // Updates the cursor position if the handle's new position is different than the current one.
1433 mEventData->mUpdateCursorPosition = true;
1434 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1435 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1436 mEventData->mPrimaryCursorPosition = handleNewPosition;
1439 // 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.
1440 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1442 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1444 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1446 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1447 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1449 // Updates the highlight box if the handle's new position is different than the current one.
1450 mEventData->mUpdateHighlightBox = true;
1451 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1452 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1453 mEventData->mLeftSelectionPosition = handleNewPosition;
1456 // 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.
1457 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1459 // Will define the order to scroll the text to match the handle position.
1460 mEventData->mIsLeftHandleSelected = true;
1461 mEventData->mIsRightHandleSelected = false;
1463 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1465 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1467 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1468 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1470 // Updates the highlight box if the handle's new position is different than the current one.
1471 mEventData->mUpdateHighlightBox = true;
1472 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1473 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1474 mEventData->mRightSelectionPosition = handleNewPosition;
1477 // 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.
1478 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1480 // Will define the order to scroll the text to match the handle position.
1481 mEventData->mIsLeftHandleSelected = false;
1482 mEventData->mIsRightHandleSelected = true;
1484 } // end ( HANDLE_PRESSED == state )
1485 else if( ( HANDLE_RELEASED == state ) ||
1486 handleStopScrolling )
1488 CharacterIndex handlePosition = 0u;
1489 if( handleStopScrolling || isSmoothHandlePanEnabled )
1491 // Convert from decorator's coords to text's coords.
1492 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1493 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1495 bool matchedCharacter = false;
1496 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1497 mModel->mLogicalModel,
1501 CharacterHitTest::SCROLL,
1505 if( Event::GRAB_HANDLE_EVENT == event.type )
1507 mEventData->mUpdateCursorPosition = true;
1508 mEventData->mUpdateGrabHandlePosition = true;
1509 mEventData->mUpdateInputStyle = true;
1511 if( !IsClipboardEmpty() )
1513 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1516 if( handleStopScrolling || isSmoothHandlePanEnabled )
1518 mEventData->mScrollAfterUpdatePosition = true;
1519 mEventData->mPrimaryCursorPosition = handlePosition;
1522 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1524 ChangeState( EventData::SELECTING );
1526 mEventData->mUpdateHighlightBox = true;
1527 mEventData->mUpdateLeftSelectionPosition = true;
1528 mEventData->mUpdateRightSelectionPosition = true;
1530 if( handleStopScrolling || isSmoothHandlePanEnabled )
1532 mEventData->mScrollAfterUpdatePosition = true;
1534 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1535 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1537 mEventData->mLeftSelectionPosition = handlePosition;
1541 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1543 ChangeState( EventData::SELECTING );
1545 mEventData->mUpdateHighlightBox = true;
1546 mEventData->mUpdateRightSelectionPosition = true;
1547 mEventData->mUpdateLeftSelectionPosition = true;
1549 if( handleStopScrolling || isSmoothHandlePanEnabled )
1551 mEventData->mScrollAfterUpdatePosition = true;
1552 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1553 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1555 mEventData->mRightSelectionPosition = handlePosition;
1560 mEventData->mDecoratorUpdated = true;
1561 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1562 else if( HANDLE_SCROLLING == state )
1564 const float xSpeed = event.p2.mFloat;
1565 const float ySpeed = event.p3.mFloat;
1566 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1567 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1569 mModel->mScrollPosition.x += xSpeed;
1570 mModel->mScrollPosition.y += ySpeed;
1572 ClampHorizontalScroll( layoutSize );
1573 ClampVerticalScroll( layoutSize );
1575 bool endOfScroll = false;
1576 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1578 // Notify the decorator there is no more text to scroll.
1579 // The decorator won't send more scroll events.
1580 mEventData->mDecorator->NotifyEndOfScroll();
1581 // Still need to set the position of the handle.
1585 // Set the position of the handle.
1586 const bool scrollRightDirection = xSpeed > 0.f;
1587 const bool scrollBottomDirection = ySpeed > 0.f;
1588 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1589 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1591 if( Event::GRAB_HANDLE_EVENT == event.type )
1593 ChangeState( EventData::GRAB_HANDLE_PANNING );
1595 // Get the grab handle position in decorator coords.
1596 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1598 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1600 // Position the grag handle close to either the left or right edge.
1601 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1604 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1606 position.x = mEventData->mCursorHookPositionX;
1608 // Position the grag handle close to either the top or bottom edge.
1609 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1612 // Get the new handle position.
1613 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1614 bool matchedCharacter = false;
1615 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1616 mModel->mLogicalModel,
1618 position.x - mModel->mScrollPosition.x,
1619 position.y - mModel->mScrollPosition.y,
1620 CharacterHitTest::SCROLL,
1623 if( mEventData->mPrimaryCursorPosition != handlePosition )
1625 mEventData->mUpdateCursorPosition = true;
1626 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1627 mEventData->mScrollAfterUpdatePosition = true;
1628 mEventData->mPrimaryCursorPosition = handlePosition;
1630 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1632 // Updates the decorator if the soft handle panning is enabled.
1633 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1635 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1637 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1639 // Get the selection handle position in decorator coords.
1640 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1642 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1644 // Position the selection handle close to either the left or right edge.
1645 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1648 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1650 position.x = mEventData->mCursorHookPositionX;
1652 // Position the grag handle close to either the top or bottom edge.
1653 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1656 // Get the new handle position.
1657 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1658 bool matchedCharacter = false;
1659 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1660 mModel->mLogicalModel,
1662 position.x - mModel->mScrollPosition.x,
1663 position.y - mModel->mScrollPosition.y,
1664 CharacterHitTest::SCROLL,
1667 if( leftSelectionHandleEvent )
1669 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1671 if( differentHandles || endOfScroll )
1673 mEventData->mUpdateHighlightBox = true;
1674 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1675 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1676 mEventData->mLeftSelectionPosition = handlePosition;
1681 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1682 if( differentHandles || endOfScroll )
1684 mEventData->mUpdateHighlightBox = true;
1685 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1686 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1687 mEventData->mRightSelectionPosition = handlePosition;
1691 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1693 RepositionSelectionHandles();
1695 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1698 mEventData->mDecoratorUpdated = true;
1699 } // end ( HANDLE_SCROLLING == state )
1702 void Controller::Impl::OnSelectEvent( const Event& event )
1704 if( NULL == mEventData )
1706 // Nothing to do if there is no text.
1710 if( mEventData->mSelectionEnabled )
1712 // Convert from control's coords to text's coords.
1713 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1714 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1716 // Calculates the logical position from the x,y coords.
1717 RepositionSelectionHandles( xPosition,
1719 Controller::NoTextTap::HIGHLIGHT );
1723 void Controller::Impl::OnSelectAllEvent()
1725 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1727 if( NULL == mEventData )
1729 // Nothing to do if there is no text.
1733 if( mEventData->mSelectionEnabled )
1735 ChangeState( EventData::SELECTING );
1737 mEventData->mLeftSelectionPosition = 0u;
1738 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1740 mEventData->mScrollAfterUpdatePosition = true;
1741 mEventData->mUpdateLeftSelectionPosition = true;
1742 mEventData->mUpdateRightSelectionPosition = true;
1743 mEventData->mUpdateHighlightBox = true;
1747 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1749 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1751 // Nothing to select if handles are in the same place.
1752 selectedText.clear();
1756 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1758 //Get start and end position of selection
1759 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1760 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1762 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1763 const Length numberOfCharacters = utf32Characters.Count();
1765 // Validate the start and end selection points
1766 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1768 //Get text as a UTF8 string
1769 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1771 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1773 // Keep a copy of the current input style.
1774 InputStyle currentInputStyle;
1775 currentInputStyle.Copy( mEventData->mInputStyle );
1777 // Set as input style the style of the first deleted character.
1778 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1780 // Compare if the input style has changed.
1781 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1783 if( hasInputStyleChanged )
1785 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1786 // Queue the input style changed signal.
1787 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1790 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1792 // Mark the paragraphs to be updated.
1793 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1795 mTextUpdateInfo.mCharacterIndex = 0;
1796 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1797 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1798 mTextUpdateInfo.mClearAll = true;
1802 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1803 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1806 // Delete text between handles
1807 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1808 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1809 utf32Characters.Erase( first, last );
1811 // Will show the cursor at the first character of the selection.
1812 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1816 // Will show the cursor at the last character of the selection.
1817 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1820 mEventData->mDecoratorUpdated = true;
1824 void Controller::Impl::ShowClipboard()
1828 mClipboard.ShowClipboard();
1832 void Controller::Impl::HideClipboard()
1834 if( mClipboard && mClipboardHideEnabled )
1836 mClipboard.HideClipboard();
1840 void Controller::Impl::SetClipboardHideEnable(bool enable)
1842 mClipboardHideEnabled = enable;
1845 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1847 //Send string to clipboard
1848 return ( mClipboard && mClipboard.SetItem( source ) );
1851 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1853 std::string selectedText;
1854 RetrieveSelection( selectedText, deleteAfterSending );
1855 CopyStringToClipboard( selectedText );
1856 ChangeState( EventData::EDITING );
1859 void Controller::Impl::RequestGetTextFromClipboard()
1863 mClipboard.RequestItem();
1867 void Controller::Impl::RepositionSelectionHandles()
1869 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1870 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1872 if( selectionStart == selectionEnd )
1874 // Nothing to select if handles are in the same place.
1878 mEventData->mDecorator->ClearHighlights();
1880 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1881 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1882 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1883 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1884 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1885 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1886 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1888 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1889 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1890 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1892 // Swap the indices if the start is greater than the end.
1893 const bool indicesSwapped = selectionStart > selectionEnd;
1895 // Tell the decorator to flip the selection handles if needed.
1896 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1898 if( indicesSwapped )
1900 std::swap( selectionStart, selectionEnd );
1903 // Get the indices to the first and last selected glyphs.
1904 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1905 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1906 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1907 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1909 // Get the lines where the glyphs are laid-out.
1910 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1912 LineIndex lineIndex = 0u;
1913 Length numberOfLines = 0u;
1914 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1915 1u + glyphEnd - glyphStart,
1918 const LineIndex firstLineIndex = lineIndex;
1920 // Create the structure to store some selection box info.
1921 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1922 selectionBoxLinesInfo.Resize( numberOfLines );
1924 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1925 selectionBoxInfo->minX = MAX_FLOAT;
1926 selectionBoxInfo->maxX = MIN_FLOAT;
1928 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1929 float minHighlightX = std::numeric_limits<float>::max();
1930 float maxHighlightX = std::numeric_limits<float>::min();
1932 Vector2 highLightPosition; // The highlight position in decorator's coords.
1934 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1936 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1937 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1940 // Transform to decorator's (control) coords.
1941 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1943 lineRun += firstLineIndex;
1945 // The line height is the addition of the line ascender and the line descender.
1946 // However, the line descender has a negative value, hence the subtraction.
1947 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1949 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1951 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1952 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1953 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1955 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1956 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1957 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1959 // The number of quads of the selection box.
1960 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1961 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1963 // Count the actual number of quads.
1964 unsigned int actualNumberOfQuads = 0u;
1967 // Traverse the glyphs.
1968 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1970 const GlyphInfo& glyph = *( glyphsBuffer + index );
1971 const Vector2& position = *( positionsBuffer + index );
1973 if( splitStartGlyph )
1975 // 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.
1977 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1978 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1979 // Get the direction of the character.
1980 CharacterDirection isCurrentRightToLeft = false;
1981 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1983 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1986 // The end point could be in the middle of the ligature.
1987 // Calculate the number of characters selected.
1988 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1990 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1991 quad.y = selectionBoxInfo->lineOffset;
1992 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1993 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1995 // Store the min and max 'x' for each line.
1996 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1997 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1999 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2000 ++actualNumberOfQuads;
2002 splitStartGlyph = false;
2006 if( splitEndGlyph && ( index == glyphEnd ) )
2008 // 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.
2010 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2011 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2012 // Get the direction of the character.
2013 CharacterDirection isCurrentRightToLeft = false;
2014 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2016 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2019 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2021 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2022 quad.y = selectionBoxInfo->lineOffset;
2023 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2024 quad.w = quad.y + selectionBoxInfo->lineHeight;
2026 // Store the min and max 'x' for each line.
2027 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2028 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2030 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2032 ++actualNumberOfQuads;
2034 splitEndGlyph = false;
2038 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2039 quad.y = selectionBoxInfo->lineOffset;
2040 quad.z = quad.x + glyph.advance;
2041 quad.w = quad.y + selectionBoxInfo->lineHeight;
2043 // Store the min and max 'x' for each line.
2044 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2045 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2047 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2049 ++actualNumberOfQuads;
2051 // Whether to retrieve the next line.
2052 if( index == lastGlyphOfLine )
2055 if( lineIndex < firstLineIndex + numberOfLines )
2057 // Retrieve the next line.
2060 // Get the last glyph of the new line.
2061 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2063 // Keep the offset and height of the current selection box.
2064 const float currentLineOffset = selectionBoxInfo->lineOffset;
2065 const float currentLineHeight = selectionBoxInfo->lineHeight;
2067 // Get the selection box info for the next line.
2070 selectionBoxInfo->minX = MAX_FLOAT;
2071 selectionBoxInfo->maxX = MIN_FLOAT;
2073 // Update the line's vertical offset.
2074 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2076 // The line height is the addition of the line ascender and the line descender.
2077 // However, the line descender has a negative value, hence the subtraction.
2078 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2083 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2084 // The final width is calculated after 'boxifying' the selection.
2085 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2086 endIt = selectionBoxLinesInfo.End();
2090 const SelectionBoxInfo& info = *it;
2092 // Update the size of the highlighted text.
2093 highLightSize.height += info.lineHeight;
2094 minHighlightX = std::min( minHighlightX, info.minX );
2095 maxHighlightX = std::max( maxHighlightX, info.maxX );
2098 // Add extra geometry to 'boxify' the selection.
2100 if( 1u < numberOfLines )
2102 // Boxify the first line.
2103 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2104 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2106 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2107 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2112 quad.y = firstSelectionBoxLineInfo.lineOffset;
2113 quad.z = firstSelectionBoxLineInfo.minX;
2114 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2116 // Boxify at the beginning of the line.
2117 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2119 ++actualNumberOfQuads;
2121 // Update the size of the highlighted text.
2122 minHighlightX = 0.f;
2127 quad.x = firstSelectionBoxLineInfo.maxX;
2128 quad.y = firstSelectionBoxLineInfo.lineOffset;
2129 quad.z = mModel->mVisualModel->mControlSize.width;
2130 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2132 // Boxify at the end of the line.
2133 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2135 ++actualNumberOfQuads;
2137 // Update the size of the highlighted text.
2138 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2141 // Boxify the central lines.
2142 if( 2u < numberOfLines )
2144 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2145 endIt = selectionBoxLinesInfo.End() - 1u;
2149 const SelectionBoxInfo& info = *it;
2152 quad.y = info.lineOffset;
2154 quad.w = info.lineOffset + info.lineHeight;
2156 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2158 ++actualNumberOfQuads;
2161 quad.y = info.lineOffset;
2162 quad.z = mModel->mVisualModel->mControlSize.width;
2163 quad.w = info.lineOffset + info.lineHeight;
2165 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2167 ++actualNumberOfQuads;
2170 // Update the size of the highlighted text.
2171 minHighlightX = 0.f;
2172 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2175 // Boxify the last line.
2176 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2177 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2179 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2180 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2185 quad.y = lastSelectionBoxLineInfo.lineOffset;
2186 quad.z = lastSelectionBoxLineInfo.minX;
2187 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2189 // Boxify at the beginning of the line.
2190 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2192 ++actualNumberOfQuads;
2194 // Update the size of the highlighted text.
2195 minHighlightX = 0.f;
2200 quad.x = lastSelectionBoxLineInfo.maxX;
2201 quad.y = lastSelectionBoxLineInfo.lineOffset;
2202 quad.z = mModel->mVisualModel->mControlSize.width;
2203 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2205 // Boxify at the end of the line.
2206 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2208 ++actualNumberOfQuads;
2210 // Update the size of the highlighted text.
2211 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2215 // Set the actual number of quads.
2216 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2218 // Sets the highlight's size and position. In decorator's coords.
2219 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2220 highLightSize.width = maxHighlightX - minHighlightX;
2222 highLightPosition.x = minHighlightX;
2223 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2224 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2226 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2228 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2230 CursorInfo primaryCursorInfo;
2231 GetCursorPosition( mEventData->mLeftSelectionPosition,
2232 primaryCursorInfo );
2234 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2236 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2238 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2239 primaryCursorInfo.lineHeight );
2241 CursorInfo secondaryCursorInfo;
2242 GetCursorPosition( mEventData->mRightSelectionPosition,
2243 secondaryCursorInfo );
2245 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2247 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2248 secondaryPosition.x,
2249 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2250 secondaryCursorInfo.lineHeight );
2253 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2254 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2256 // Set the flag to update the decorator.
2257 mEventData->mDecoratorUpdated = true;
2260 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2262 if( NULL == mEventData )
2264 // Nothing to do if there is no text input.
2268 if( IsShowingPlaceholderText() )
2270 // Nothing to do if there is the place-holder text.
2274 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2275 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2276 if( ( 0 == numberOfGlyphs ) ||
2277 ( 0 == numberOfLines ) )
2279 // Nothing to do if there is no text.
2283 // Find which word was selected
2284 CharacterIndex selectionStart( 0 );
2285 CharacterIndex selectionEnd( 0 );
2286 CharacterIndex noTextHitIndex( 0 );
2287 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2288 mModel->mLogicalModel,
2295 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2297 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2299 ChangeState( EventData::SELECTING );
2301 mEventData->mLeftSelectionPosition = selectionStart;
2302 mEventData->mRightSelectionPosition = selectionEnd;
2304 mEventData->mUpdateLeftSelectionPosition = true;
2305 mEventData->mUpdateRightSelectionPosition = true;
2306 mEventData->mUpdateHighlightBox = true;
2308 // It may happen an IMF commit event arrives before the selection event
2309 // if the IMF manager is in pre-edit state. The commit event will set the
2310 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2311 // to false, the highlight box won't be updated.
2312 mEventData->mUpdateCursorPosition = false;
2314 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2316 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2318 // Nothing to select. i.e. a white space, out of bounds
2319 ChangeState( EventData::EDITING_WITH_POPUP );
2321 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2323 mEventData->mUpdateCursorPosition = true;
2324 mEventData->mUpdateGrabHandlePosition = true;
2325 mEventData->mScrollAfterUpdatePosition = true;
2326 mEventData->mUpdateInputStyle = true;
2328 else if( Controller::NoTextTap::NO_ACTION == action )
2330 // Nothing to select. i.e. a white space, out of bounds
2331 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2333 mEventData->mUpdateCursorPosition = true;
2334 mEventData->mUpdateGrabHandlePosition = true;
2335 mEventData->mScrollAfterUpdatePosition = true;
2336 mEventData->mUpdateInputStyle = true;
2340 void Controller::Impl::SetPopupButtons()
2343 * Sets the Popup buttons to be shown depending on State.
2345 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2347 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2350 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2352 if( EventData::SELECTING == mEventData->mState )
2354 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2356 if( !IsClipboardEmpty() )
2358 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2359 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2362 if( !mEventData->mAllTextSelected )
2364 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2367 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2369 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2371 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2374 if( !IsClipboardEmpty() )
2376 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2377 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2380 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2382 if ( !IsClipboardEmpty() )
2384 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2385 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2389 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2392 void Controller::Impl::ChangeState( EventData::State newState )
2394 if( NULL == mEventData )
2396 // Nothing to do if there is no text input.
2400 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2402 if( mEventData->mState != newState )
2404 mEventData->mPreviousState = mEventData->mState;
2405 mEventData->mState = newState;
2407 switch( mEventData->mState )
2409 case EventData::INACTIVE:
2411 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2412 mEventData->mDecorator->StopCursorBlink();
2413 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2414 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2415 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2416 mEventData->mDecorator->SetHighlightActive( false );
2417 mEventData->mDecorator->SetPopupActive( false );
2418 mEventData->mDecoratorUpdated = true;
2421 case EventData::INTERRUPTED:
2423 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2424 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2425 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2426 mEventData->mDecorator->SetHighlightActive( false );
2427 mEventData->mDecorator->SetPopupActive( false );
2428 mEventData->mDecoratorUpdated = true;
2431 case EventData::SELECTING:
2433 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2434 mEventData->mDecorator->StopCursorBlink();
2435 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2436 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2437 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2438 mEventData->mDecorator->SetHighlightActive( true );
2439 if( mEventData->mGrabHandlePopupEnabled )
2442 mEventData->mDecorator->SetPopupActive( true );
2444 mEventData->mDecoratorUpdated = true;
2447 case EventData::EDITING:
2449 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2450 if( mEventData->mCursorBlinkEnabled )
2452 mEventData->mDecorator->StartCursorBlink();
2454 // Grab handle is not shown until a tap is received whilst EDITING
2455 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2456 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2457 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2458 mEventData->mDecorator->SetHighlightActive( false );
2459 if( mEventData->mGrabHandlePopupEnabled )
2461 mEventData->mDecorator->SetPopupActive( false );
2463 mEventData->mDecoratorUpdated = true;
2466 case EventData::EDITING_WITH_POPUP:
2468 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2470 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2471 if( mEventData->mCursorBlinkEnabled )
2473 mEventData->mDecorator->StartCursorBlink();
2475 if( mEventData->mSelectionEnabled )
2477 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2478 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2479 mEventData->mDecorator->SetHighlightActive( false );
2483 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2485 if( mEventData->mGrabHandlePopupEnabled )
2488 mEventData->mDecorator->SetPopupActive( true );
2490 mEventData->mDecoratorUpdated = true;
2493 case EventData::EDITING_WITH_GRAB_HANDLE:
2495 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2497 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2498 if( mEventData->mCursorBlinkEnabled )
2500 mEventData->mDecorator->StartCursorBlink();
2502 // Grab handle is not shown until a tap is received whilst EDITING
2503 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2504 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2505 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2506 mEventData->mDecorator->SetHighlightActive( false );
2507 if( mEventData->mGrabHandlePopupEnabled )
2509 mEventData->mDecorator->SetPopupActive( false );
2511 mEventData->mDecoratorUpdated = true;
2514 case EventData::SELECTION_HANDLE_PANNING:
2516 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2517 mEventData->mDecorator->StopCursorBlink();
2518 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2519 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2520 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2521 mEventData->mDecorator->SetHighlightActive( true );
2522 if( mEventData->mGrabHandlePopupEnabled )
2524 mEventData->mDecorator->SetPopupActive( false );
2526 mEventData->mDecoratorUpdated = true;
2529 case EventData::GRAB_HANDLE_PANNING:
2531 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2533 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2534 if( mEventData->mCursorBlinkEnabled )
2536 mEventData->mDecorator->StartCursorBlink();
2538 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2539 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2540 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2541 mEventData->mDecorator->SetHighlightActive( false );
2542 if( mEventData->mGrabHandlePopupEnabled )
2544 mEventData->mDecorator->SetPopupActive( false );
2546 mEventData->mDecoratorUpdated = true;
2549 case EventData::EDITING_WITH_PASTE_POPUP:
2551 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2553 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2554 if( mEventData->mCursorBlinkEnabled )
2556 mEventData->mDecorator->StartCursorBlink();
2559 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2560 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2561 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2562 mEventData->mDecorator->SetHighlightActive( false );
2564 if( mEventData->mGrabHandlePopupEnabled )
2567 mEventData->mDecorator->SetPopupActive( true );
2569 mEventData->mDecoratorUpdated = true;
2572 case EventData::TEXT_PANNING:
2574 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2575 mEventData->mDecorator->StopCursorBlink();
2576 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2577 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2578 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2580 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2581 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2582 mEventData->mDecorator->SetHighlightActive( true );
2585 if( mEventData->mGrabHandlePopupEnabled )
2587 mEventData->mDecorator->SetPopupActive( false );
2590 mEventData->mDecoratorUpdated = true;
2597 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2598 CursorInfo& cursorInfo )
2600 if( !IsShowingRealText() )
2602 // Do not want to use the place-holder text to set the cursor position.
2604 // Use the line's height of the font's family set to set the cursor's size.
2605 // If there is no font's family set, use the default font.
2606 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2608 cursorInfo.lineOffset = 0.f;
2609 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2610 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2612 switch( mModel->mHorizontalAlignment )
2614 case Text::HorizontalAlignment::BEGIN :
2616 cursorInfo.primaryPosition.x = 0.f;
2619 case Text::HorizontalAlignment::CENTER:
2621 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2624 case Text::HorizontalAlignment::END:
2626 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2631 // Nothing else to do.
2635 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2636 GetCursorPositionParameters parameters;
2637 parameters.visualModel = mModel->mVisualModel;
2638 parameters.logicalModel = mModel->mLogicalModel;
2639 parameters.metrics = mMetrics;
2640 parameters.logical = logical;
2641 parameters.isMultiline = isMultiLine;
2643 Text::GetCursorPosition( parameters,
2648 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2650 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2651 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2653 if( 0.f > cursorInfo.primaryPosition.x )
2655 cursorInfo.primaryPosition.x = 0.f;
2658 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2659 if( cursorInfo.primaryPosition.x > edgeWidth )
2661 cursorInfo.primaryPosition.x = edgeWidth;
2666 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2668 if( NULL == mEventData )
2670 // Nothing to do if there is no text input.
2674 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2676 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2677 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2679 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2680 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2682 if( numberOfCharacters > 1u )
2684 const Script script = mModel->mLogicalModel->GetScript( index );
2685 if( HasLigatureMustBreak( script ) )
2687 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2688 numberOfCharacters = 1u;
2693 while( 0u == numberOfCharacters )
2696 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2700 if( index < mEventData->mPrimaryCursorPosition )
2702 cursorIndex -= numberOfCharacters;
2706 cursorIndex += numberOfCharacters;
2709 // Will update the cursor hook position.
2710 mEventData->mUpdateCursorHookPosition = true;
2715 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2717 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2718 if( NULL == mEventData )
2720 // Nothing to do if there is no text input.
2721 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2725 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2727 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2729 // Sets the cursor position.
2730 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2733 cursorInfo.primaryCursorHeight,
2734 cursorInfo.lineHeight );
2735 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2737 if( mEventData->mUpdateGrabHandlePosition )
2739 // Sets the grab handle position.
2740 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2742 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2743 cursorInfo.lineHeight );
2746 if( cursorInfo.isSecondaryCursor )
2748 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2749 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2750 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2751 cursorInfo.secondaryCursorHeight,
2752 cursorInfo.lineHeight );
2753 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2756 // Set which cursors are active according the state.
2757 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2759 if( cursorInfo.isSecondaryCursor )
2761 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2765 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2770 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2773 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2776 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2777 const CursorInfo& cursorInfo )
2779 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2780 ( RIGHT_SELECTION_HANDLE != handleType ) )
2785 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2787 // Sets the handle's position.
2788 mEventData->mDecorator->SetPosition( handleType,
2790 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2791 cursorInfo.lineHeight );
2793 // If selection handle at start of the text and other at end of the text then all text is selected.
2794 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2795 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2796 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2799 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2801 // Clamp between -space & -alignment offset.
2803 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2805 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2806 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2807 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2809 mEventData->mDecoratorUpdated = true;
2813 mModel->mScrollPosition.x = 0.f;
2817 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2819 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2821 // Nothing to do if the text is single line.
2825 // Clamp between -space & 0.
2826 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2828 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2829 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2830 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2832 mEventData->mDecoratorUpdated = true;
2836 mModel->mScrollPosition.y = 0.f;
2840 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2842 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2844 // position is in actor's coords.
2845 const float positionEndX = position.x + cursorWidth;
2846 const float positionEndY = position.y + lineHeight;
2848 // Transform the position to decorator coords.
2849 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2850 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2852 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2853 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2855 if( decoratorPositionBeginX < 0.f )
2857 mModel->mScrollPosition.x = -position.x;
2859 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2861 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2864 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2866 if( decoratorPositionBeginY < 0.f )
2868 mModel->mScrollPosition.y = -position.y;
2870 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2872 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2877 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2879 // Get the current cursor position in decorator coords.
2880 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2882 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2886 // Calculate the offset to match the cursor position before the character was deleted.
2887 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2889 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2890 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2892 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2893 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2897 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2898 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2900 // Makes the new cursor position visible if needed.
2901 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2904 void Controller::Impl::RequestRelayout()
2906 if( NULL != mControlInterface )
2908 mControlInterface->RequestTextRelayout();
2914 } // namespace Toolkit