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 ),
75 mPlaceholderTextActive(),
76 mPlaceholderTextInactive(),
77 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
79 mInputStyleChangedQueue(),
80 mPreviousState( INACTIVE ),
82 mPrimaryCursorPosition( 0u ),
83 mLeftSelectionPosition( 0u ),
84 mRightSelectionPosition( 0u ),
85 mPreEditStartPosition( 0u ),
87 mCursorHookPositionX( 0.f ),
88 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
89 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
90 mIsShowingPlaceholderText( false ),
91 mPreEditFlag( false ),
92 mDecoratorUpdated( false ),
93 mCursorBlinkEnabled( true ),
94 mGrabHandleEnabled( true ),
95 mGrabHandlePopupEnabled( true ),
96 mSelectionEnabled( true ),
97 mUpdateCursorHookPosition( false ),
98 mUpdateCursorPosition( false ),
99 mUpdateGrabHandlePosition( false ),
100 mUpdateLeftSelectionPosition( false ),
101 mUpdateRightSelectionPosition( false ),
102 mIsLeftHandleSelected( false ),
103 mIsRightHandleSelected( false ),
104 mUpdateHighlightBox( false ),
105 mScrollAfterUpdatePosition( false ),
106 mScrollAfterDelete( false ),
107 mAllTextSelected( false ),
108 mUpdateInputStyle( false ),
109 mPasswordInput( false ),
110 mIsPlaceholderPixelSize( false )
112 mImfManager = ImfManager::Get();
115 EventData::~EventData()
118 bool Controller::Impl::ProcessInputEvents()
120 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
121 if( NULL == mEventData )
123 // Nothing to do if there is no text input.
124 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
128 if( mEventData->mDecorator )
130 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
131 iter != mEventData->mEventQueue.end();
136 case Event::CURSOR_KEY_EVENT:
138 OnCursorKeyEvent( *iter );
141 case Event::TAP_EVENT:
146 case Event::LONG_PRESS_EVENT:
148 OnLongPressEvent( *iter );
151 case Event::PAN_EVENT:
156 case Event::GRAB_HANDLE_EVENT:
157 case Event::LEFT_SELECTION_HANDLE_EVENT:
158 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
160 OnHandleEvent( *iter );
165 OnSelectEvent( *iter );
168 case Event::SELECT_ALL:
177 if( mEventData->mUpdateCursorPosition ||
178 mEventData->mUpdateHighlightBox )
183 // The cursor must also be repositioned after inserts into the model
184 if( mEventData->mUpdateCursorPosition )
186 // Updates the cursor position and scrolls the text to make it visible.
187 CursorInfo cursorInfo;
188 // Calculate the cursor position from the new cursor index.
189 GetCursorPosition( mEventData->mPrimaryCursorPosition,
192 if( mEventData->mUpdateCursorHookPosition )
194 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
195 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
196 mEventData->mUpdateCursorHookPosition = false;
199 // Scroll first the text after delete ...
200 if( mEventData->mScrollAfterDelete )
202 ScrollTextToMatchCursor( cursorInfo );
205 // ... then, text can be scrolled to make the cursor visible.
206 if( mEventData->mScrollAfterUpdatePosition )
208 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
209 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
211 mEventData->mScrollAfterUpdatePosition = false;
212 mEventData->mScrollAfterDelete = false;
214 UpdateCursorPosition( cursorInfo );
216 mEventData->mDecoratorUpdated = true;
217 mEventData->mUpdateCursorPosition = false;
218 mEventData->mUpdateGrabHandlePosition = false;
222 CursorInfo leftHandleInfo;
223 CursorInfo rightHandleInfo;
225 if( mEventData->mUpdateHighlightBox )
227 GetCursorPosition( mEventData->mLeftSelectionPosition,
230 GetCursorPosition( mEventData->mRightSelectionPosition,
233 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
235 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
237 CursorInfo& infoLeft = leftHandleInfo;
239 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
240 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
242 CursorInfo& infoRight = rightHandleInfo;
244 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
245 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
249 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
251 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
252 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
257 if( mEventData->mUpdateLeftSelectionPosition )
259 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
263 mEventData->mDecoratorUpdated = true;
264 mEventData->mUpdateLeftSelectionPosition = false;
267 if( mEventData->mUpdateRightSelectionPosition )
269 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
273 mEventData->mDecoratorUpdated = true;
274 mEventData->mUpdateRightSelectionPosition = false;
277 if( mEventData->mUpdateHighlightBox )
279 RepositionSelectionHandles();
281 mEventData->mUpdateLeftSelectionPosition = false;
282 mEventData->mUpdateRightSelectionPosition = false;
283 mEventData->mUpdateHighlightBox = false;
284 mEventData->mIsLeftHandleSelected = false;
285 mEventData->mIsRightHandleSelected = false;
288 mEventData->mScrollAfterUpdatePosition = false;
291 if( mEventData->mUpdateInputStyle )
293 // Keep a copy of the current input style.
294 InputStyle currentInputStyle;
295 currentInputStyle.Copy( mEventData->mInputStyle );
297 // Set the default style first.
298 RetrieveDefaultInputStyle( mEventData->mInputStyle );
300 // Get the character index from the cursor index.
301 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
303 // Retrieve the style from the style runs stored in the logical model.
304 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
306 // Compare if the input style has changed.
307 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
309 if( hasInputStyleChanged )
311 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
312 // Queue the input style changed signal.
313 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
316 mEventData->mUpdateInputStyle = false;
319 mEventData->mEventQueue.clear();
321 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
323 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
324 mEventData->mDecoratorUpdated = false;
326 return decoratorUpdated;
329 void Controller::Impl::NotifyImfManager()
331 if( mEventData && mEventData->mImfManager )
333 CharacterIndex cursorPosition = GetLogicalCursorPosition();
335 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
337 // Update the cursor position by removing the initial white spaces.
338 if( cursorPosition < numberOfWhiteSpaces )
344 cursorPosition -= numberOfWhiteSpaces;
347 mEventData->mImfManager.SetCursorPosition( cursorPosition );
348 mEventData->mImfManager.NotifyCursorPosition();
352 void Controller::Impl::NotifyImfMultiLineStatus()
356 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
357 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
361 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
363 CharacterIndex cursorPosition = 0u;
367 if( ( EventData::SELECTING == mEventData->mState ) ||
368 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
370 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
374 cursorPosition = mEventData->mPrimaryCursorPosition;
378 return cursorPosition;
381 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
383 Length numberOfWhiteSpaces = 0u;
385 // Get the buffer to the text.
386 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
388 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
389 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
391 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
397 return numberOfWhiteSpaces;
400 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
402 // Get the total number of characters.
403 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
405 // Retrieve the text.
406 if( 0u != numberOfCharacters )
408 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
412 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
414 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
415 mTextUpdateInfo.mStartGlyphIndex = 0u;
416 mTextUpdateInfo.mStartLineIndex = 0u;
417 numberOfCharacters = 0u;
419 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
420 if( 0u == numberOfParagraphs )
422 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
423 numberOfCharacters = 0u;
425 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
427 // Nothing else to do if there are no paragraphs.
431 // Find the paragraphs to be updated.
432 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
433 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
435 // Text is being added at the end of the current text.
436 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
438 // Text is being added in a new paragraph after the last character of the text.
439 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
440 numberOfCharacters = 0u;
441 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
443 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
444 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
446 // Nothing else to do;
450 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
454 Length numberOfCharactersToUpdate = 0u;
455 if( mTextUpdateInfo.mFullRelayoutNeeded )
457 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
461 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
463 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
464 numberOfCharactersToUpdate,
465 paragraphsToBeUpdated );
468 if( 0u != paragraphsToBeUpdated.Count() )
470 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
471 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
472 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
474 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
475 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
477 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
478 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
479 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
480 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
482 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
483 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
485 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
489 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
493 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
494 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
497 void Controller::Impl::ClearFullModelData( OperationsMask operations )
499 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
501 mModel->mLogicalModel->mLineBreakInfo.Clear();
502 mModel->mLogicalModel->mParagraphInfo.Clear();
505 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
507 mModel->mLogicalModel->mLineBreakInfo.Clear();
510 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
512 mModel->mLogicalModel->mScriptRuns.Clear();
515 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
517 mModel->mLogicalModel->mFontRuns.Clear();
520 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
522 if( NO_OPERATION != ( BIDI_INFO & operations ) )
524 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
525 mModel->mLogicalModel->mCharacterDirections.Clear();
528 if( NO_OPERATION != ( REORDER & operations ) )
530 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
531 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
532 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
536 BidirectionalLineInfoRun& bidiLineInfo = *it;
538 free( bidiLineInfo.visualToLogicalMap );
539 bidiLineInfo.visualToLogicalMap = NULL;
541 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
545 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
547 mModel->mVisualModel->mGlyphs.Clear();
548 mModel->mVisualModel->mGlyphsToCharacters.Clear();
549 mModel->mVisualModel->mCharactersToGlyph.Clear();
550 mModel->mVisualModel->mCharactersPerGlyph.Clear();
551 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
552 mModel->mVisualModel->mGlyphPositions.Clear();
555 if( NO_OPERATION != ( LAYOUT & operations ) )
557 mModel->mVisualModel->mLines.Clear();
560 if( NO_OPERATION != ( COLOR & operations ) )
562 mModel->mVisualModel->mColorIndices.Clear();
566 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
568 const CharacterIndex endIndexPlusOne = endIndex + 1u;
570 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
572 // Clear the line break info.
573 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
575 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
576 lineBreakInfoBuffer + endIndexPlusOne );
578 // Clear the paragraphs.
579 ClearCharacterRuns( startIndex,
581 mModel->mLogicalModel->mParagraphInfo );
584 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
586 // Clear the word break info.
587 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
589 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
590 wordBreakInfoBuffer + endIndexPlusOne );
593 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
595 // Clear the scripts.
596 ClearCharacterRuns( startIndex,
598 mModel->mLogicalModel->mScriptRuns );
601 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
604 ClearCharacterRuns( startIndex,
606 mModel->mLogicalModel->mFontRuns );
609 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
611 if( NO_OPERATION != ( BIDI_INFO & operations ) )
613 // Clear the bidirectional paragraph info.
614 ClearCharacterRuns( startIndex,
616 mModel->mLogicalModel->mBidirectionalParagraphInfo );
618 // Clear the character's directions.
619 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
621 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
622 characterDirectionsBuffer + endIndexPlusOne );
625 if( NO_OPERATION != ( REORDER & operations ) )
627 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
628 uint32_t endRemoveIndex = startRemoveIndex;
629 ClearCharacterRuns( startIndex,
631 mModel->mLogicalModel->mBidirectionalLineInfo,
635 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
637 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
638 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
639 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
643 BidirectionalLineInfoRun& bidiLineInfo = *it;
645 free( bidiLineInfo.visualToLogicalMap );
646 bidiLineInfo.visualToLogicalMap = NULL;
649 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
650 bidirectionalLineInfoBuffer + endRemoveIndex );
655 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
657 const CharacterIndex endIndexPlusOne = endIndex + 1u;
658 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
660 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
661 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
662 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
664 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
665 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
667 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
669 // Update the character to glyph indices.
670 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
671 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
675 CharacterIndex& index = *it;
676 index -= numberOfGlyphsRemoved;
679 // Clear the character to glyph conversion table.
680 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
681 charactersToGlyphBuffer + endIndexPlusOne );
683 // Clear the glyphs per character table.
684 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
685 glyphsPerCharacterBuffer + endIndexPlusOne );
687 // Clear the glyphs buffer.
688 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
689 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
690 glyphsBuffer + endGlyphIndexPlusOne );
692 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
694 // Update the glyph to character indices.
695 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
696 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
700 CharacterIndex& index = *it;
701 index -= numberOfCharactersRemoved;
704 // Clear the glyphs to characters buffer.
705 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
706 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
708 // Clear the characters per glyph buffer.
709 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
710 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
711 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
713 // Clear the positions buffer.
714 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
715 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
716 positionsBuffer + endGlyphIndexPlusOne );
719 if( NO_OPERATION != ( LAYOUT & operations ) )
722 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
723 uint32_t endRemoveIndex = startRemoveIndex;
724 ClearCharacterRuns( startIndex,
726 mModel->mVisualModel->mLines,
730 // Will update the glyph runs.
731 startRemoveIndex = mModel->mVisualModel->mLines.Count();
732 endRemoveIndex = startRemoveIndex;
733 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
734 endGlyphIndexPlusOne - 1u,
735 mModel->mVisualModel->mLines,
739 // Set the line index from where to insert the new laid-out lines.
740 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
742 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
743 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
744 linesBuffer + endRemoveIndex );
747 if( NO_OPERATION != ( COLOR & operations ) )
749 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
751 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
752 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
753 colorIndexBuffer + endGlyphIndexPlusOne );
758 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
760 if( mTextUpdateInfo.mClearAll ||
761 ( ( 0u == startIndex ) &&
762 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
764 ClearFullModelData( operations );
768 // Clear the model data related with characters.
769 ClearCharacterModelData( startIndex, endIndex, operations );
771 // Clear the model data related with glyphs.
772 ClearGlyphModelData( startIndex, endIndex, operations );
775 // The estimated number of lines. Used to avoid reallocations when layouting.
776 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
778 mModel->mVisualModel->ClearCaches();
781 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
783 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
785 // Calculate the operations to be done.
786 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
788 if( NO_OPERATION == operations )
790 // Nothing to do if no operations are pending and required.
794 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
795 Vector<Character> displayCharacters;
796 bool useHiddenText = false;
797 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
799 mHiddenInput->Substitute( srcCharacters,displayCharacters );
800 useHiddenText = true;
803 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
804 const Length numberOfCharacters = utf32Characters.Count();
806 // Index to the first character of the first paragraph to be updated.
807 CharacterIndex startIndex = 0u;
808 // Number of characters of the paragraphs to be removed.
809 Length paragraphCharacters = 0u;
811 CalculateTextUpdateIndices( paragraphCharacters );
812 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
814 if( mTextUpdateInfo.mClearAll ||
815 ( 0u != paragraphCharacters ) )
817 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
820 mTextUpdateInfo.mClearAll = false;
822 // Whether the model is updated.
823 bool updated = false;
825 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
826 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
828 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
830 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
831 // calculate the bidirectional info for each 'paragraph'.
832 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
833 // is not shaped together).
834 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
836 SetLineBreakInfo( utf32Characters,
838 requestedNumberOfCharacters,
841 // Create the paragraph info.
842 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
843 requestedNumberOfCharacters );
847 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
848 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
850 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
851 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
853 SetWordBreakInfo( utf32Characters,
855 requestedNumberOfCharacters,
860 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
861 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
863 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
864 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
866 if( getScripts || validateFonts )
868 // Validates the fonts assigned by the application or assigns default ones.
869 // It makes sure all the characters are going to be rendered by the correct font.
870 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
874 // Retrieves the scripts used in the text.
875 multilanguageSupport.SetScripts( utf32Characters,
877 requestedNumberOfCharacters,
883 // Validate the fonts set through the mark-up string.
884 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
886 // Get the default font's description.
887 TextAbstraction::FontDescription defaultFontDescription;
888 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
890 if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) )
892 // If the placeholder font is set specifically, only placeholder font is changed.
893 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
894 if( mEventData->mPlaceholderFont->sizeDefined )
896 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
899 else if( NULL != mFontDefaults )
901 // Set the normal font and the placeholder font.
902 defaultFontDescription = mFontDefaults->mFontDescription;
903 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
906 // Validates the fonts. If there is a character with no assigned font it sets a default one.
907 // After this call, fonts are validated.
908 multilanguageSupport.ValidateFonts( utf32Characters,
911 defaultFontDescription,
914 requestedNumberOfCharacters,
920 Vector<Character> mirroredUtf32Characters;
921 bool textMirrored = false;
922 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
923 if( NO_OPERATION != ( BIDI_INFO & operations ) )
925 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
926 bidirectionalInfo.Reserve( numberOfParagraphs );
928 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
929 SetBidirectionalInfo( utf32Characters,
933 requestedNumberOfCharacters,
936 if( 0u != bidirectionalInfo.Count() )
938 // Only set the character directions if there is right to left characters.
939 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
940 GetCharactersDirection( bidirectionalInfo,
943 requestedNumberOfCharacters,
946 // This paragraph has right to left text. Some characters may need to be mirrored.
947 // TODO: consider if the mirrored string can be stored as well.
949 textMirrored = GetMirroredText( utf32Characters,
953 requestedNumberOfCharacters,
954 mirroredUtf32Characters );
958 // There is no right to left characters. Clear the directions vector.
959 mModel->mLogicalModel->mCharacterDirections.Clear();
964 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
965 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
966 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
967 Vector<GlyphIndex> newParagraphGlyphs;
968 newParagraphGlyphs.Reserve( numberOfParagraphs );
970 const Length currentNumberOfGlyphs = glyphs.Count();
971 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
973 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
975 ShapeText( textToShape,
980 mTextUpdateInfo.mStartGlyphIndex,
981 requestedNumberOfCharacters,
983 glyphsToCharactersMap,
985 newParagraphGlyphs );
987 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
988 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
989 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
993 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
995 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
997 GlyphInfo* glyphsBuffer = glyphs.Begin();
998 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1000 // Update the width and advance of all new paragraph characters.
1001 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1003 const GlyphIndex index = *it;
1004 GlyphInfo& glyph = *( glyphsBuffer + index );
1006 glyph.xBearing = 0.f;
1008 glyph.advance = 0.f;
1013 if( NO_OPERATION != ( COLOR & operations ) )
1015 // Set the color runs in glyphs.
1016 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1017 mModel->mVisualModel->mCharactersToGlyph,
1018 mModel->mVisualModel->mGlyphsPerCharacter,
1020 mTextUpdateInfo.mStartGlyphIndex,
1021 requestedNumberOfCharacters,
1022 mModel->mVisualModel->mColors,
1023 mModel->mVisualModel->mColorIndices );
1028 if( ( NULL != mEventData ) &&
1029 mEventData->mPreEditFlag &&
1030 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1032 // Add the underline for the pre-edit text.
1033 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1034 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1036 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1037 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1038 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1039 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1041 GlyphRun underlineRun;
1042 underlineRun.glyphIndex = glyphStart;
1043 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1045 // TODO: At the moment the underline runs are only for pre-edit.
1046 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1049 // The estimated number of lines. Used to avoid reallocations when layouting.
1050 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1052 // Set the previous number of characters for the next time the text is updated.
1053 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1058 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1060 // Sets the default text's color.
1061 inputStyle.textColor = mTextColor;
1062 inputStyle.isDefaultColor = true;
1064 inputStyle.familyName.clear();
1065 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1066 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1067 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1068 inputStyle.size = 0.f;
1070 inputStyle.lineSpacing = 0.f;
1072 inputStyle.underlineProperties.clear();
1073 inputStyle.shadowProperties.clear();
1074 inputStyle.embossProperties.clear();
1075 inputStyle.outlineProperties.clear();
1077 inputStyle.isFamilyDefined = false;
1078 inputStyle.isWeightDefined = false;
1079 inputStyle.isWidthDefined = false;
1080 inputStyle.isSlantDefined = false;
1081 inputStyle.isSizeDefined = false;
1083 inputStyle.isLineSpacingDefined = false;
1085 inputStyle.isUnderlineDefined = false;
1086 inputStyle.isShadowDefined = false;
1087 inputStyle.isEmbossDefined = false;
1088 inputStyle.isOutlineDefined = false;
1090 // Sets the default font's family name, weight, width, slant and size.
1093 if( mFontDefaults->familyDefined )
1095 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1096 inputStyle.isFamilyDefined = true;
1099 if( mFontDefaults->weightDefined )
1101 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1102 inputStyle.isWeightDefined = true;
1105 if( mFontDefaults->widthDefined )
1107 inputStyle.width = mFontDefaults->mFontDescription.width;
1108 inputStyle.isWidthDefined = true;
1111 if( mFontDefaults->slantDefined )
1113 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1114 inputStyle.isSlantDefined = true;
1117 if( mFontDefaults->sizeDefined )
1119 inputStyle.size = mFontDefaults->mDefaultPointSize;
1120 inputStyle.isSizeDefined = true;
1125 float Controller::Impl::GetDefaultFontLineHeight()
1127 FontId defaultFontId = 0u;
1128 if( NULL == mFontDefaults )
1130 TextAbstraction::FontDescription fontDescription;
1131 defaultFontId = mFontClient.GetFontId( fontDescription );
1135 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1138 Text::FontMetrics fontMetrics;
1139 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1141 return( fontMetrics.ascender - fontMetrics.descender );
1144 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1146 if( NULL == mEventData )
1148 // Nothing to do if there is no text input.
1152 int keyCode = event.p1.mInt;
1154 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1156 if( mEventData->mPrimaryCursorPosition > 0u )
1158 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1161 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1163 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1165 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1168 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1170 // Get first the line index of the current cursor position index.
1171 CharacterIndex characterIndex = 0u;
1173 if( mEventData->mPrimaryCursorPosition > 0u )
1175 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1178 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1179 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1181 // Retrieve the cursor position info.
1182 CursorInfo cursorInfo;
1183 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1186 // Get the line above.
1187 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1189 // Get the next hit 'y' point.
1190 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1192 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1193 bool matchedCharacter = false;
1194 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1195 mModel->mLogicalModel,
1197 mEventData->mCursorHookPositionX,
1199 CharacterHitTest::TAP,
1202 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1204 // Get first the line index of the current cursor position index.
1205 CharacterIndex characterIndex = 0u;
1207 if( mEventData->mPrimaryCursorPosition > 0u )
1209 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1212 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1214 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1216 // Retrieve the cursor position info.
1217 CursorInfo cursorInfo;
1218 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1221 // Get the line below.
1222 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1224 // Get the next hit 'y' point.
1225 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1227 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1228 bool matchedCharacter = false;
1229 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1230 mModel->mLogicalModel,
1232 mEventData->mCursorHookPositionX,
1234 CharacterHitTest::TAP,
1239 mEventData->mUpdateCursorPosition = true;
1240 mEventData->mUpdateInputStyle = true;
1241 mEventData->mScrollAfterUpdatePosition = true;
1244 void Controller::Impl::OnTapEvent( const Event& event )
1246 if( NULL != mEventData )
1248 const unsigned int tapCount = event.p1.mUint;
1250 if( 1u == tapCount )
1252 if( IsShowingRealText() )
1254 // Convert from control's coords to text's coords.
1255 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1256 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1258 // Keep the tap 'x' position. Used to move the cursor.
1259 mEventData->mCursorHookPositionX = xPosition;
1261 // Whether to touch point hits on a glyph.
1262 bool matchedCharacter = false;
1263 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1264 mModel->mLogicalModel,
1268 CharacterHitTest::TAP,
1271 // When the cursor position is changing, delay cursor blinking
1272 mEventData->mDecorator->DelayCursorBlink();
1276 mEventData->mPrimaryCursorPosition = 0u;
1279 mEventData->mUpdateCursorPosition = true;
1280 mEventData->mUpdateGrabHandlePosition = true;
1281 mEventData->mScrollAfterUpdatePosition = true;
1282 mEventData->mUpdateInputStyle = true;
1284 // Notify the cursor position to the imf manager.
1285 if( mEventData->mImfManager )
1287 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1288 mEventData->mImfManager.NotifyCursorPosition();
1291 else if( 2u == tapCount )
1293 if( mEventData->mSelectionEnabled )
1295 // Convert from control's coords to text's coords.
1296 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1297 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1299 // Calculates the logical position from the x,y coords.
1300 RepositionSelectionHandles( xPosition,
1302 mEventData->mDoubleTapAction );
1308 void Controller::Impl::OnPanEvent( const Event& event )
1310 if( NULL == mEventData )
1312 // Nothing to do if there is no text input.
1316 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1317 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1319 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1321 // Nothing to do if scrolling is not enabled.
1325 const int state = event.p1.mInt;
1329 case Gesture::Started:
1331 // Will remove the cursor, handles or text's popup, ...
1332 ChangeState( EventData::TEXT_PANNING );
1335 case Gesture::Continuing:
1337 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1338 const Vector2 currentScroll = mModel->mScrollPosition;
1340 if( isHorizontalScrollEnabled )
1342 const float displacementX = event.p2.mFloat;
1343 mModel->mScrollPosition.x += displacementX;
1345 ClampHorizontalScroll( layoutSize );
1348 if( isVerticalScrollEnabled )
1350 const float displacementY = event.p3.mFloat;
1351 mModel->mScrollPosition.y += displacementY;
1353 ClampVerticalScroll( layoutSize );
1356 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1359 case Gesture::Finished:
1360 case Gesture::Cancelled: // FALLTHROUGH
1362 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1363 ChangeState( mEventData->mPreviousState );
1371 void Controller::Impl::OnLongPressEvent( const Event& event )
1373 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1375 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1377 ChangeState( EventData::EDITING_WITH_POPUP );
1378 mEventData->mDecoratorUpdated = true;
1379 mEventData->mUpdateInputStyle = true;
1383 if( mEventData->mSelectionEnabled )
1385 // Convert from control's coords to text's coords.
1386 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1387 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1389 // Calculates the logical position from the x,y coords.
1390 RepositionSelectionHandles( xPosition,
1392 mEventData->mLongPressAction );
1397 void Controller::Impl::OnHandleEvent( const Event& event )
1399 if( NULL == mEventData )
1401 // Nothing to do if there is no text input.
1405 const unsigned int state = event.p1.mUint;
1406 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1407 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1409 if( HANDLE_PRESSED == state )
1411 // Convert from decorator's coords to text's coords.
1412 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1413 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1415 // Need to calculate the handle's new position.
1416 bool matchedCharacter = false;
1417 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1418 mModel->mLogicalModel,
1422 CharacterHitTest::SCROLL,
1425 if( Event::GRAB_HANDLE_EVENT == event.type )
1427 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1429 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1431 // Updates the cursor position if the handle's new position is different than the current one.
1432 mEventData->mUpdateCursorPosition = true;
1433 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1434 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1435 mEventData->mPrimaryCursorPosition = handleNewPosition;
1438 // 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.
1439 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1441 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1443 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1445 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1446 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1448 // Updates the highlight box if the handle's new position is different than the current one.
1449 mEventData->mUpdateHighlightBox = true;
1450 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1451 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1452 mEventData->mLeftSelectionPosition = handleNewPosition;
1455 // 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.
1456 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1458 // Will define the order to scroll the text to match the handle position.
1459 mEventData->mIsLeftHandleSelected = true;
1460 mEventData->mIsRightHandleSelected = false;
1462 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1464 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1466 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1467 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1469 // Updates the highlight box if the handle's new position is different than the current one.
1470 mEventData->mUpdateHighlightBox = true;
1471 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1472 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1473 mEventData->mRightSelectionPosition = handleNewPosition;
1476 // 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.
1477 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1479 // Will define the order to scroll the text to match the handle position.
1480 mEventData->mIsLeftHandleSelected = false;
1481 mEventData->mIsRightHandleSelected = true;
1483 } // end ( HANDLE_PRESSED == state )
1484 else if( ( HANDLE_RELEASED == state ) ||
1485 handleStopScrolling )
1487 CharacterIndex handlePosition = 0u;
1488 if( handleStopScrolling || isSmoothHandlePanEnabled )
1490 // Convert from decorator's coords to text's coords.
1491 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1492 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1494 bool matchedCharacter = false;
1495 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1496 mModel->mLogicalModel,
1500 CharacterHitTest::SCROLL,
1504 if( Event::GRAB_HANDLE_EVENT == event.type )
1506 mEventData->mUpdateCursorPosition = true;
1507 mEventData->mUpdateGrabHandlePosition = true;
1508 mEventData->mUpdateInputStyle = true;
1510 if( !IsClipboardEmpty() )
1512 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1515 if( handleStopScrolling || isSmoothHandlePanEnabled )
1517 mEventData->mScrollAfterUpdatePosition = true;
1518 mEventData->mPrimaryCursorPosition = handlePosition;
1521 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1523 ChangeState( EventData::SELECTING );
1525 mEventData->mUpdateHighlightBox = true;
1526 mEventData->mUpdateLeftSelectionPosition = true;
1527 mEventData->mUpdateRightSelectionPosition = true;
1529 if( handleStopScrolling || isSmoothHandlePanEnabled )
1531 mEventData->mScrollAfterUpdatePosition = true;
1533 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1534 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1536 mEventData->mLeftSelectionPosition = handlePosition;
1540 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1542 ChangeState( EventData::SELECTING );
1544 mEventData->mUpdateHighlightBox = true;
1545 mEventData->mUpdateRightSelectionPosition = true;
1546 mEventData->mUpdateLeftSelectionPosition = true;
1548 if( handleStopScrolling || isSmoothHandlePanEnabled )
1550 mEventData->mScrollAfterUpdatePosition = true;
1551 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1552 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1554 mEventData->mRightSelectionPosition = handlePosition;
1559 mEventData->mDecoratorUpdated = true;
1560 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1561 else if( HANDLE_SCROLLING == state )
1563 const float xSpeed = event.p2.mFloat;
1564 const float ySpeed = event.p3.mFloat;
1565 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1566 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1568 mModel->mScrollPosition.x += xSpeed;
1569 mModel->mScrollPosition.y += ySpeed;
1571 ClampHorizontalScroll( layoutSize );
1572 ClampVerticalScroll( layoutSize );
1574 bool endOfScroll = false;
1575 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1577 // Notify the decorator there is no more text to scroll.
1578 // The decorator won't send more scroll events.
1579 mEventData->mDecorator->NotifyEndOfScroll();
1580 // Still need to set the position of the handle.
1584 // Set the position of the handle.
1585 const bool scrollRightDirection = xSpeed > 0.f;
1586 const bool scrollBottomDirection = ySpeed > 0.f;
1587 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1588 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1590 if( Event::GRAB_HANDLE_EVENT == event.type )
1592 ChangeState( EventData::GRAB_HANDLE_PANNING );
1594 // Get the grab handle position in decorator coords.
1595 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1597 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1599 // Position the grag handle close to either the left or right edge.
1600 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1603 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1605 position.x = mEventData->mCursorHookPositionX;
1607 // Position the grag handle close to either the top or bottom edge.
1608 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1611 // Get the new handle position.
1612 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1613 bool matchedCharacter = false;
1614 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1615 mModel->mLogicalModel,
1617 position.x - mModel->mScrollPosition.x,
1618 position.y - mModel->mScrollPosition.y,
1619 CharacterHitTest::SCROLL,
1622 if( mEventData->mPrimaryCursorPosition != handlePosition )
1624 mEventData->mUpdateCursorPosition = true;
1625 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1626 mEventData->mScrollAfterUpdatePosition = true;
1627 mEventData->mPrimaryCursorPosition = handlePosition;
1629 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1631 // Updates the decorator if the soft handle panning is enabled.
1632 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1634 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1636 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1638 // Get the selection handle position in decorator coords.
1639 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1641 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1643 // Position the selection handle close to either the left or right edge.
1644 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1647 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1649 position.x = mEventData->mCursorHookPositionX;
1651 // Position the grag handle close to either the top or bottom edge.
1652 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1655 // Get the new handle position.
1656 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1657 bool matchedCharacter = false;
1658 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1659 mModel->mLogicalModel,
1661 position.x - mModel->mScrollPosition.x,
1662 position.y - mModel->mScrollPosition.y,
1663 CharacterHitTest::SCROLL,
1666 if( leftSelectionHandleEvent )
1668 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1670 if( differentHandles || endOfScroll )
1672 mEventData->mUpdateHighlightBox = true;
1673 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1674 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1675 mEventData->mLeftSelectionPosition = handlePosition;
1680 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1681 if( differentHandles || endOfScroll )
1683 mEventData->mUpdateHighlightBox = true;
1684 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1685 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1686 mEventData->mRightSelectionPosition = handlePosition;
1690 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1692 RepositionSelectionHandles();
1694 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1697 mEventData->mDecoratorUpdated = true;
1698 } // end ( HANDLE_SCROLLING == state )
1701 void Controller::Impl::OnSelectEvent( const Event& event )
1703 if( NULL == mEventData )
1705 // Nothing to do if there is no text.
1709 if( mEventData->mSelectionEnabled )
1711 // Convert from control's coords to text's coords.
1712 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1713 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1715 // Calculates the logical position from the x,y coords.
1716 RepositionSelectionHandles( xPosition,
1718 Controller::NoTextTap::HIGHLIGHT );
1722 void Controller::Impl::OnSelectAllEvent()
1724 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1726 if( NULL == mEventData )
1728 // Nothing to do if there is no text.
1732 if( mEventData->mSelectionEnabled )
1734 ChangeState( EventData::SELECTING );
1736 mEventData->mLeftSelectionPosition = 0u;
1737 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1739 mEventData->mScrollAfterUpdatePosition = true;
1740 mEventData->mUpdateLeftSelectionPosition = true;
1741 mEventData->mUpdateRightSelectionPosition = true;
1742 mEventData->mUpdateHighlightBox = true;
1746 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1748 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1750 // Nothing to select if handles are in the same place.
1751 selectedText.clear();
1755 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1757 //Get start and end position of selection
1758 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1759 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1761 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1762 const Length numberOfCharacters = utf32Characters.Count();
1764 // Validate the start and end selection points
1765 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1767 //Get text as a UTF8 string
1768 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1770 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1772 // Keep a copy of the current input style.
1773 InputStyle currentInputStyle;
1774 currentInputStyle.Copy( mEventData->mInputStyle );
1776 // Set as input style the style of the first deleted character.
1777 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1779 // Compare if the input style has changed.
1780 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1782 if( hasInputStyleChanged )
1784 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1785 // Queue the input style changed signal.
1786 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1789 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1791 // Mark the paragraphs to be updated.
1792 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1793 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1795 // Delete text between handles
1796 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1797 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1798 utf32Characters.Erase( first, last );
1800 // Will show the cursor at the first character of the selection.
1801 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1805 // Will show the cursor at the last character of the selection.
1806 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1809 mEventData->mDecoratorUpdated = true;
1813 void Controller::Impl::ShowClipboard()
1817 mClipboard.ShowClipboard();
1821 void Controller::Impl::HideClipboard()
1823 if( mClipboard && mClipboardHideEnabled )
1825 mClipboard.HideClipboard();
1829 void Controller::Impl::SetClipboardHideEnable(bool enable)
1831 mClipboardHideEnabled = enable;
1834 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1836 //Send string to clipboard
1837 return ( mClipboard && mClipboard.SetItem( source ) );
1840 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1842 std::string selectedText;
1843 RetrieveSelection( selectedText, deleteAfterSending );
1844 CopyStringToClipboard( selectedText );
1845 ChangeState( EventData::EDITING );
1848 void Controller::Impl::RequestGetTextFromClipboard()
1852 mClipboard.RequestItem();
1856 void Controller::Impl::RepositionSelectionHandles()
1858 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1859 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1861 if( selectionStart == selectionEnd )
1863 // Nothing to select if handles are in the same place.
1867 mEventData->mDecorator->ClearHighlights();
1869 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1870 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1871 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1872 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1873 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1874 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1875 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1877 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1878 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1879 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1881 // Swap the indices if the start is greater than the end.
1882 const bool indicesSwapped = selectionStart > selectionEnd;
1884 // Tell the decorator to flip the selection handles if needed.
1885 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1887 if( indicesSwapped )
1889 std::swap( selectionStart, selectionEnd );
1892 // Get the indices to the first and last selected glyphs.
1893 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1894 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1895 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1896 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1898 // Get the lines where the glyphs are laid-out.
1899 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1901 LineIndex lineIndex = 0u;
1902 Length numberOfLines = 0u;
1903 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1904 1u + glyphEnd - glyphStart,
1907 const LineIndex firstLineIndex = lineIndex;
1909 // Create the structure to store some selection box info.
1910 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1911 selectionBoxLinesInfo.Resize( numberOfLines );
1913 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1914 selectionBoxInfo->minX = MAX_FLOAT;
1915 selectionBoxInfo->maxX = MIN_FLOAT;
1917 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1918 float minHighlightX = std::numeric_limits<float>::max();
1919 float maxHighlightX = std::numeric_limits<float>::min();
1921 Vector2 highLightPosition; // The highlight position in decorator's coords.
1923 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1925 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1926 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1929 // Transform to decorator's (control) coords.
1930 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1932 lineRun += firstLineIndex;
1934 // The line height is the addition of the line ascender and the line descender.
1935 // However, the line descender has a negative value, hence the subtraction.
1936 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1938 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1940 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1941 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1942 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1944 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1945 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1946 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1948 // The number of quads of the selection box.
1949 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1950 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1952 // Count the actual number of quads.
1953 unsigned int actualNumberOfQuads = 0u;
1956 // Traverse the glyphs.
1957 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1959 const GlyphInfo& glyph = *( glyphsBuffer + index );
1960 const Vector2& position = *( positionsBuffer + index );
1962 if( splitStartGlyph )
1964 // 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.
1966 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1967 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1968 // Get the direction of the character.
1969 CharacterDirection isCurrentRightToLeft = false;
1970 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1972 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1975 // The end point could be in the middle of the ligature.
1976 // Calculate the number of characters selected.
1977 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1979 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1980 quad.y = selectionBoxInfo->lineOffset;
1981 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1982 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1984 // Store the min and max 'x' for each line.
1985 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1986 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1988 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1989 ++actualNumberOfQuads;
1991 splitStartGlyph = false;
1995 if( splitEndGlyph && ( index == glyphEnd ) )
1997 // 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.
1999 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2000 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2001 // Get the direction of the character.
2002 CharacterDirection isCurrentRightToLeft = false;
2003 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2005 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2008 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2010 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2011 quad.y = selectionBoxInfo->lineOffset;
2012 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2013 quad.w = quad.y + selectionBoxInfo->lineHeight;
2015 // Store the min and max 'x' for each line.
2016 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2017 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2019 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2021 ++actualNumberOfQuads;
2023 splitEndGlyph = false;
2027 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2028 quad.y = selectionBoxInfo->lineOffset;
2029 quad.z = quad.x + glyph.advance;
2030 quad.w = quad.y + selectionBoxInfo->lineHeight;
2032 // Store the min and max 'x' for each line.
2033 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2034 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2036 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2038 ++actualNumberOfQuads;
2040 // Whether to retrieve the next line.
2041 if( index == lastGlyphOfLine )
2044 if( lineIndex < firstLineIndex + numberOfLines )
2046 // Retrieve the next line.
2049 // Get the last glyph of the new line.
2050 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2052 // Keep the offset and height of the current selection box.
2053 const float currentLineOffset = selectionBoxInfo->lineOffset;
2054 const float currentLineHeight = selectionBoxInfo->lineHeight;
2056 // Get the selection box info for the next line.
2059 selectionBoxInfo->minX = MAX_FLOAT;
2060 selectionBoxInfo->maxX = MIN_FLOAT;
2062 // Update the line's vertical offset.
2063 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2065 // The line height is the addition of the line ascender and the line descender.
2066 // However, the line descender has a negative value, hence the subtraction.
2067 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2072 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2073 // The final width is calculated after 'boxifying' the selection.
2074 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2075 endIt = selectionBoxLinesInfo.End();
2079 const SelectionBoxInfo& info = *it;
2081 // Update the size of the highlighted text.
2082 highLightSize.height += info.lineHeight;
2083 minHighlightX = std::min( minHighlightX, info.minX );
2084 maxHighlightX = std::max( maxHighlightX, info.maxX );
2087 // Add extra geometry to 'boxify' the selection.
2089 if( 1u < numberOfLines )
2091 // Boxify the first line.
2092 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2093 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2095 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2096 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2101 quad.y = firstSelectionBoxLineInfo.lineOffset;
2102 quad.z = firstSelectionBoxLineInfo.minX;
2103 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2105 // Boxify at the beginning of the line.
2106 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2108 ++actualNumberOfQuads;
2110 // Update the size of the highlighted text.
2111 minHighlightX = 0.f;
2116 quad.x = firstSelectionBoxLineInfo.maxX;
2117 quad.y = firstSelectionBoxLineInfo.lineOffset;
2118 quad.z = mModel->mVisualModel->mControlSize.width;
2119 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2121 // Boxify at the end of the line.
2122 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2124 ++actualNumberOfQuads;
2126 // Update the size of the highlighted text.
2127 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2130 // Boxify the central lines.
2131 if( 2u < numberOfLines )
2133 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2134 endIt = selectionBoxLinesInfo.End() - 1u;
2138 const SelectionBoxInfo& info = *it;
2141 quad.y = info.lineOffset;
2143 quad.w = info.lineOffset + info.lineHeight;
2145 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2147 ++actualNumberOfQuads;
2150 quad.y = info.lineOffset;
2151 quad.z = mModel->mVisualModel->mControlSize.width;
2152 quad.w = info.lineOffset + info.lineHeight;
2154 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2156 ++actualNumberOfQuads;
2159 // Update the size of the highlighted text.
2160 minHighlightX = 0.f;
2161 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2164 // Boxify the last line.
2165 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2166 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2168 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2169 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2174 quad.y = lastSelectionBoxLineInfo.lineOffset;
2175 quad.z = lastSelectionBoxLineInfo.minX;
2176 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2178 // Boxify at the beginning of the line.
2179 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2181 ++actualNumberOfQuads;
2183 // Update the size of the highlighted text.
2184 minHighlightX = 0.f;
2189 quad.x = lastSelectionBoxLineInfo.maxX;
2190 quad.y = lastSelectionBoxLineInfo.lineOffset;
2191 quad.z = mModel->mVisualModel->mControlSize.width;
2192 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2194 // Boxify at the end of the line.
2195 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2197 ++actualNumberOfQuads;
2199 // Update the size of the highlighted text.
2200 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2204 // Set the actual number of quads.
2205 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2207 // Sets the highlight's size and position. In decorator's coords.
2208 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2209 highLightSize.width = maxHighlightX - minHighlightX;
2211 highLightPosition.x = minHighlightX;
2212 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2213 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2215 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2217 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2219 CursorInfo primaryCursorInfo;
2220 GetCursorPosition( mEventData->mLeftSelectionPosition,
2221 primaryCursorInfo );
2223 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2225 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2227 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2228 primaryCursorInfo.lineHeight );
2230 CursorInfo secondaryCursorInfo;
2231 GetCursorPosition( mEventData->mRightSelectionPosition,
2232 secondaryCursorInfo );
2234 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2236 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2237 secondaryPosition.x,
2238 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2239 secondaryCursorInfo.lineHeight );
2242 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2243 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2245 // Set the flag to update the decorator.
2246 mEventData->mDecoratorUpdated = true;
2249 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2251 if( NULL == mEventData )
2253 // Nothing to do if there is no text input.
2257 if( IsShowingPlaceholderText() )
2259 // Nothing to do if there is the place-holder text.
2263 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2264 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2265 if( ( 0 == numberOfGlyphs ) ||
2266 ( 0 == numberOfLines ) )
2268 // Nothing to do if there is no text.
2272 // Find which word was selected
2273 CharacterIndex selectionStart( 0 );
2274 CharacterIndex selectionEnd( 0 );
2275 CharacterIndex noTextHitIndex( 0 );
2276 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2277 mModel->mLogicalModel,
2284 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2286 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2288 ChangeState( EventData::SELECTING );
2290 mEventData->mLeftSelectionPosition = selectionStart;
2291 mEventData->mRightSelectionPosition = selectionEnd;
2293 mEventData->mUpdateLeftSelectionPosition = true;
2294 mEventData->mUpdateRightSelectionPosition = true;
2295 mEventData->mUpdateHighlightBox = true;
2297 // It may happen an IMF commit event arrives before the selection event
2298 // if the IMF manager is in pre-edit state. The commit event will set the
2299 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2300 // to false, the highlight box won't be updated.
2301 mEventData->mUpdateCursorPosition = false;
2303 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2305 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2307 // Nothing to select. i.e. a white space, out of bounds
2308 ChangeState( EventData::EDITING_WITH_POPUP );
2310 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2312 mEventData->mUpdateCursorPosition = true;
2313 mEventData->mUpdateGrabHandlePosition = true;
2314 mEventData->mScrollAfterUpdatePosition = true;
2315 mEventData->mUpdateInputStyle = true;
2317 else if( Controller::NoTextTap::NO_ACTION == action )
2319 // Nothing to select. i.e. a white space, out of bounds
2320 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2322 mEventData->mUpdateCursorPosition = true;
2323 mEventData->mUpdateGrabHandlePosition = true;
2324 mEventData->mScrollAfterUpdatePosition = true;
2325 mEventData->mUpdateInputStyle = true;
2329 void Controller::Impl::SetPopupButtons()
2332 * Sets the Popup buttons to be shown depending on State.
2334 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2336 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2339 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2341 if( EventData::SELECTING == mEventData->mState )
2343 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2345 if( !IsClipboardEmpty() )
2347 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2348 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2351 if( !mEventData->mAllTextSelected )
2353 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2356 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2358 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2360 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2363 if( !IsClipboardEmpty() )
2365 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2366 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2369 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2371 if ( !IsClipboardEmpty() )
2373 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2374 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2378 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2381 void Controller::Impl::ChangeState( EventData::State newState )
2383 if( NULL == mEventData )
2385 // Nothing to do if there is no text input.
2389 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2391 if( mEventData->mState != newState )
2393 mEventData->mPreviousState = mEventData->mState;
2394 mEventData->mState = newState;
2396 switch( mEventData->mState )
2398 case EventData::INACTIVE:
2400 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2401 mEventData->mDecorator->StopCursorBlink();
2402 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2403 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2404 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2405 mEventData->mDecorator->SetHighlightActive( false );
2406 mEventData->mDecorator->SetPopupActive( false );
2407 mEventData->mDecoratorUpdated = true;
2410 case EventData::INTERRUPTED:
2412 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2413 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2414 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2415 mEventData->mDecorator->SetHighlightActive( false );
2416 mEventData->mDecorator->SetPopupActive( false );
2417 mEventData->mDecoratorUpdated = true;
2420 case EventData::SELECTING:
2422 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2423 mEventData->mDecorator->StopCursorBlink();
2424 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2425 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2426 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2427 mEventData->mDecorator->SetHighlightActive( true );
2428 if( mEventData->mGrabHandlePopupEnabled )
2431 mEventData->mDecorator->SetPopupActive( true );
2433 mEventData->mDecoratorUpdated = true;
2436 case EventData::EDITING:
2438 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2439 if( mEventData->mCursorBlinkEnabled )
2441 mEventData->mDecorator->StartCursorBlink();
2443 // Grab handle is not shown until a tap is received whilst EDITING
2444 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2445 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2446 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2447 mEventData->mDecorator->SetHighlightActive( false );
2448 if( mEventData->mGrabHandlePopupEnabled )
2450 mEventData->mDecorator->SetPopupActive( false );
2452 mEventData->mDecoratorUpdated = true;
2455 case EventData::EDITING_WITH_POPUP:
2457 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2459 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2460 if( mEventData->mCursorBlinkEnabled )
2462 mEventData->mDecorator->StartCursorBlink();
2464 if( mEventData->mSelectionEnabled )
2466 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2467 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2468 mEventData->mDecorator->SetHighlightActive( false );
2472 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2474 if( mEventData->mGrabHandlePopupEnabled )
2477 mEventData->mDecorator->SetPopupActive( true );
2479 mEventData->mDecoratorUpdated = true;
2482 case EventData::EDITING_WITH_GRAB_HANDLE:
2484 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2486 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2487 if( mEventData->mCursorBlinkEnabled )
2489 mEventData->mDecorator->StartCursorBlink();
2491 // Grab handle is not shown until a tap is received whilst EDITING
2492 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2493 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2494 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2495 mEventData->mDecorator->SetHighlightActive( false );
2496 if( mEventData->mGrabHandlePopupEnabled )
2498 mEventData->mDecorator->SetPopupActive( false );
2500 mEventData->mDecoratorUpdated = true;
2503 case EventData::SELECTION_HANDLE_PANNING:
2505 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2506 mEventData->mDecorator->StopCursorBlink();
2507 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2508 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2509 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2510 mEventData->mDecorator->SetHighlightActive( true );
2511 if( mEventData->mGrabHandlePopupEnabled )
2513 mEventData->mDecorator->SetPopupActive( false );
2515 mEventData->mDecoratorUpdated = true;
2518 case EventData::GRAB_HANDLE_PANNING:
2520 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2522 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2523 if( mEventData->mCursorBlinkEnabled )
2525 mEventData->mDecorator->StartCursorBlink();
2527 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2528 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2529 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2530 mEventData->mDecorator->SetHighlightActive( false );
2531 if( mEventData->mGrabHandlePopupEnabled )
2533 mEventData->mDecorator->SetPopupActive( false );
2535 mEventData->mDecoratorUpdated = true;
2538 case EventData::EDITING_WITH_PASTE_POPUP:
2540 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2542 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2543 if( mEventData->mCursorBlinkEnabled )
2545 mEventData->mDecorator->StartCursorBlink();
2548 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2549 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2550 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2551 mEventData->mDecorator->SetHighlightActive( false );
2553 if( mEventData->mGrabHandlePopupEnabled )
2556 mEventData->mDecorator->SetPopupActive( true );
2558 mEventData->mDecoratorUpdated = true;
2561 case EventData::TEXT_PANNING:
2563 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2564 mEventData->mDecorator->StopCursorBlink();
2565 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2566 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2567 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2569 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2570 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2571 mEventData->mDecorator->SetHighlightActive( true );
2574 if( mEventData->mGrabHandlePopupEnabled )
2576 mEventData->mDecorator->SetPopupActive( false );
2579 mEventData->mDecoratorUpdated = true;
2586 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2587 CursorInfo& cursorInfo )
2589 if( !IsShowingRealText() )
2591 // Do not want to use the place-holder text to set the cursor position.
2593 // Use the line's height of the font's family set to set the cursor's size.
2594 // If there is no font's family set, use the default font.
2595 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2597 cursorInfo.lineOffset = 0.f;
2598 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2599 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2601 switch( mModel->mHorizontalAlignment )
2603 case Layout::HORIZONTAL_ALIGN_BEGIN:
2605 cursorInfo.primaryPosition.x = 0.f;
2608 case Layout::HORIZONTAL_ALIGN_CENTER:
2610 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2613 case Layout::HORIZONTAL_ALIGN_END:
2615 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2620 // Nothing else to do.
2624 Text::GetCursorPosition( mModel->mVisualModel,
2625 mModel->mLogicalModel,
2630 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2632 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2634 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2635 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2637 if( 0.f > cursorInfo.primaryPosition.x )
2639 cursorInfo.primaryPosition.x = 0.f;
2642 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2643 if( cursorInfo.primaryPosition.x > edgeWidth )
2645 cursorInfo.primaryPosition.x = edgeWidth;
2650 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2652 if( NULL == mEventData )
2654 // Nothing to do if there is no text input.
2658 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2660 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2661 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2663 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2664 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2666 if( numberOfCharacters > 1u )
2668 const Script script = mModel->mLogicalModel->GetScript( index );
2669 if( HasLigatureMustBreak( script ) )
2671 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2672 numberOfCharacters = 1u;
2677 while( 0u == numberOfCharacters )
2680 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2684 if( index < mEventData->mPrimaryCursorPosition )
2686 cursorIndex -= numberOfCharacters;
2690 cursorIndex += numberOfCharacters;
2693 // Will update the cursor hook position.
2694 mEventData->mUpdateCursorHookPosition = true;
2699 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2701 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2702 if( NULL == mEventData )
2704 // Nothing to do if there is no text input.
2705 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2709 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2711 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2713 // Sets the cursor position.
2714 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2717 cursorInfo.primaryCursorHeight,
2718 cursorInfo.lineHeight );
2719 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2721 if( mEventData->mUpdateGrabHandlePosition )
2723 // Sets the grab handle position.
2724 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2726 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2727 cursorInfo.lineHeight );
2730 if( cursorInfo.isSecondaryCursor )
2732 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2733 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2734 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2735 cursorInfo.secondaryCursorHeight,
2736 cursorInfo.lineHeight );
2737 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2740 // Set which cursors are active according the state.
2741 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2743 if( cursorInfo.isSecondaryCursor )
2745 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2749 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2754 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2757 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2760 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2761 const CursorInfo& cursorInfo )
2763 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2764 ( RIGHT_SELECTION_HANDLE != handleType ) )
2769 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2771 // Sets the handle's position.
2772 mEventData->mDecorator->SetPosition( handleType,
2774 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2775 cursorInfo.lineHeight );
2777 // If selection handle at start of the text and other at end of the text then all text is selected.
2778 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2779 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2780 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2783 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2785 // Clamp between -space & -alignment offset.
2787 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2789 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2790 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2791 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2793 mEventData->mDecoratorUpdated = true;
2797 mModel->mScrollPosition.x = 0.f;
2801 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2803 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2805 // Nothing to do if the text is single line.
2809 // Clamp between -space & 0.
2810 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2812 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2813 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2814 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2816 mEventData->mDecoratorUpdated = true;
2820 mModel->mScrollPosition.y = 0.f;
2824 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2826 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2828 // position is in actor's coords.
2829 const float positionEndX = position.x + cursorWidth;
2830 const float positionEndY = position.y + lineHeight;
2832 // Transform the position to decorator coords.
2833 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2834 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2836 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2837 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2839 if( decoratorPositionBeginX < 0.f )
2841 mModel->mScrollPosition.x = -position.x;
2843 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2845 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2848 if( decoratorPositionBeginY < 0.f )
2850 mModel->mScrollPosition.y = -position.y;
2852 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2854 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2858 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2860 // Get the current cursor position in decorator coords.
2861 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2863 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2867 // Calculate the offset to match the cursor position before the character was deleted.
2868 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2870 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2871 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2873 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2874 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2878 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2879 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2881 // Makes the new cursor position visible if needed.
2882 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2885 void Controller::Impl::RequestRelayout()
2887 if( NULL != mControlInterface )
2889 mControlInterface->RequestTextRelayout();
2895 } // namespace Toolkit