2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
58 const uint32_t STAR = 0x2A;
71 EventData::EventData( DecoratorPtr decorator )
72 : mDecorator( decorator ),
74 mPlaceholderTextActive(),
75 mPlaceholderTextInactive(),
76 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
78 mInputStyleChangedQueue(),
79 mPreviousState( INACTIVE ),
81 mPrimaryCursorPosition( 0u ),
82 mLeftSelectionPosition( 0u ),
83 mRightSelectionPosition( 0u ),
84 mPreEditStartPosition( 0u ),
86 mCursorHookPositionX( 0.f ),
87 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
88 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
89 mIsShowingPlaceholderText( false ),
90 mPreEditFlag( false ),
91 mDecoratorUpdated( false ),
92 mCursorBlinkEnabled( true ),
93 mGrabHandleEnabled( true ),
94 mGrabHandlePopupEnabled( true ),
95 mSelectionEnabled( true ),
96 mUpdateCursorHookPosition( false ),
97 mUpdateCursorPosition( false ),
98 mUpdateGrabHandlePosition( false ),
99 mUpdateLeftSelectionPosition( false ),
100 mUpdateRightSelectionPosition( false ),
101 mIsLeftHandleSelected( false ),
102 mIsRightHandleSelected( false ),
103 mUpdateHighlightBox( false ),
104 mScrollAfterUpdatePosition( false ),
105 mScrollAfterDelete( false ),
106 mAllTextSelected( false ),
107 mUpdateInputStyle( false ),
108 mPasswordInput( false )
110 mImfManager = ImfManager::Get();
113 EventData::~EventData()
116 bool Controller::Impl::ProcessInputEvents()
118 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
119 if( NULL == mEventData )
121 // Nothing to do if there is no text input.
122 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
126 if( mEventData->mDecorator )
128 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
129 iter != mEventData->mEventQueue.end();
134 case Event::CURSOR_KEY_EVENT:
136 OnCursorKeyEvent( *iter );
139 case Event::TAP_EVENT:
144 case Event::LONG_PRESS_EVENT:
146 OnLongPressEvent( *iter );
149 case Event::PAN_EVENT:
154 case Event::GRAB_HANDLE_EVENT:
155 case Event::LEFT_SELECTION_HANDLE_EVENT:
156 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
158 OnHandleEvent( *iter );
163 OnSelectEvent( *iter );
166 case Event::SELECT_ALL:
175 if( mEventData->mUpdateCursorPosition ||
176 mEventData->mUpdateHighlightBox )
181 // The cursor must also be repositioned after inserts into the model
182 if( mEventData->mUpdateCursorPosition )
184 // Updates the cursor position and scrolls the text to make it visible.
185 CursorInfo cursorInfo;
186 // Calculate the cursor position from the new cursor index.
187 GetCursorPosition( mEventData->mPrimaryCursorPosition,
190 if( mEventData->mUpdateCursorHookPosition )
192 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
193 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
194 mEventData->mUpdateCursorHookPosition = false;
197 // Scroll first the text after delete ...
198 if( mEventData->mScrollAfterDelete )
200 ScrollTextToMatchCursor( cursorInfo );
203 // ... then, text can be scrolled to make the cursor visible.
204 if( mEventData->mScrollAfterUpdatePosition )
206 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
207 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
209 mEventData->mScrollAfterUpdatePosition = false;
210 mEventData->mScrollAfterDelete = false;
212 UpdateCursorPosition( cursorInfo );
214 mEventData->mDecoratorUpdated = true;
215 mEventData->mUpdateCursorPosition = false;
216 mEventData->mUpdateGrabHandlePosition = false;
220 CursorInfo leftHandleInfo;
221 CursorInfo rightHandleInfo;
223 if( mEventData->mUpdateHighlightBox )
225 GetCursorPosition( mEventData->mLeftSelectionPosition,
228 GetCursorPosition( mEventData->mRightSelectionPosition,
231 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
233 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
235 CursorInfo& infoLeft = leftHandleInfo;
237 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
238 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
240 CursorInfo& infoRight = rightHandleInfo;
242 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
243 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
247 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
249 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
250 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
255 if( mEventData->mUpdateLeftSelectionPosition )
257 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
261 mEventData->mDecoratorUpdated = true;
262 mEventData->mUpdateLeftSelectionPosition = false;
265 if( mEventData->mUpdateRightSelectionPosition )
267 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
271 mEventData->mDecoratorUpdated = true;
272 mEventData->mUpdateRightSelectionPosition = false;
275 if( mEventData->mUpdateHighlightBox )
277 RepositionSelectionHandles();
279 mEventData->mUpdateLeftSelectionPosition = false;
280 mEventData->mUpdateRightSelectionPosition = false;
281 mEventData->mUpdateHighlightBox = false;
282 mEventData->mIsLeftHandleSelected = false;
283 mEventData->mIsRightHandleSelected = false;
286 mEventData->mScrollAfterUpdatePosition = false;
289 if( mEventData->mUpdateInputStyle )
291 // Keep a copy of the current input style.
292 InputStyle currentInputStyle;
293 currentInputStyle.Copy( mEventData->mInputStyle );
295 // Set the default style first.
296 RetrieveDefaultInputStyle( mEventData->mInputStyle );
298 // Get the character index from the cursor index.
299 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
301 // Retrieve the style from the style runs stored in the logical model.
302 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
304 // Compare if the input style has changed.
305 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
307 if( hasInputStyleChanged )
309 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
310 // Queue the input style changed signal.
311 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
314 mEventData->mUpdateInputStyle = false;
317 mEventData->mEventQueue.clear();
319 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
321 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
322 mEventData->mDecoratorUpdated = false;
324 return decoratorUpdated;
327 void Controller::Impl::NotifyImfManager()
329 if( mEventData && mEventData->mImfManager )
331 CharacterIndex cursorPosition = GetLogicalCursorPosition();
333 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
335 // Update the cursor position by removing the initial white spaces.
336 if( cursorPosition < numberOfWhiteSpaces )
342 cursorPosition -= numberOfWhiteSpaces;
345 mEventData->mImfManager.SetCursorPosition( cursorPosition );
346 mEventData->mImfManager.NotifyCursorPosition();
350 void Controller::Impl::NotifyImfMultiLineStatus()
354 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
355 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
359 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
361 CharacterIndex cursorPosition = 0u;
365 if( ( EventData::SELECTING == mEventData->mState ) ||
366 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
368 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
372 cursorPosition = mEventData->mPrimaryCursorPosition;
376 return cursorPosition;
379 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
381 Length numberOfWhiteSpaces = 0u;
383 // Get the buffer to the text.
384 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
386 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
387 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
389 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
395 return numberOfWhiteSpaces;
398 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
400 // Get the total number of characters.
401 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
403 // Retrieve the text.
404 if( 0u != numberOfCharacters )
406 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
410 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
412 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
413 mTextUpdateInfo.mStartGlyphIndex = 0u;
414 mTextUpdateInfo.mStartLineIndex = 0u;
415 numberOfCharacters = 0u;
417 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
418 if( 0u == numberOfParagraphs )
420 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
421 numberOfCharacters = 0u;
423 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
425 // Nothing else to do if there are no paragraphs.
429 // Find the paragraphs to be updated.
430 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
431 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
433 // Text is being added at the end of the current text.
434 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
436 // Text is being added in a new paragraph after the last character of the text.
437 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
438 numberOfCharacters = 0u;
439 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
441 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
442 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
444 // Nothing else to do;
448 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
452 Length numberOfCharactersToUpdate = 0u;
453 if( mTextUpdateInfo.mFullRelayoutNeeded )
455 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
459 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
461 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
462 numberOfCharactersToUpdate,
463 paragraphsToBeUpdated );
466 if( 0u != paragraphsToBeUpdated.Count() )
468 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
469 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
470 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
472 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
473 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
475 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
476 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
477 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
478 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
480 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
481 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
483 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
487 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
491 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
492 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
495 void Controller::Impl::ClearFullModelData( OperationsMask operations )
497 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
499 mModel->mLogicalModel->mLineBreakInfo.Clear();
500 mModel->mLogicalModel->mParagraphInfo.Clear();
503 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
505 mModel->mLogicalModel->mLineBreakInfo.Clear();
508 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
510 mModel->mLogicalModel->mScriptRuns.Clear();
513 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
515 mModel->mLogicalModel->mFontRuns.Clear();
518 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
520 if( NO_OPERATION != ( BIDI_INFO & operations ) )
522 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
523 mModel->mLogicalModel->mCharacterDirections.Clear();
526 if( NO_OPERATION != ( REORDER & operations ) )
528 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
529 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
530 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
534 BidirectionalLineInfoRun& bidiLineInfo = *it;
536 free( bidiLineInfo.visualToLogicalMap );
537 bidiLineInfo.visualToLogicalMap = NULL;
539 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
543 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
545 mModel->mVisualModel->mGlyphs.Clear();
546 mModel->mVisualModel->mGlyphsToCharacters.Clear();
547 mModel->mVisualModel->mCharactersToGlyph.Clear();
548 mModel->mVisualModel->mCharactersPerGlyph.Clear();
549 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
550 mModel->mVisualModel->mGlyphPositions.Clear();
553 if( NO_OPERATION != ( LAYOUT & operations ) )
555 mModel->mVisualModel->mLines.Clear();
558 if( NO_OPERATION != ( COLOR & operations ) )
560 mModel->mVisualModel->mColorIndices.Clear();
564 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
566 const CharacterIndex endIndexPlusOne = endIndex + 1u;
568 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
570 // Clear the line break info.
571 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
573 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
574 lineBreakInfoBuffer + endIndexPlusOne );
576 // Clear the paragraphs.
577 ClearCharacterRuns( startIndex,
579 mModel->mLogicalModel->mParagraphInfo );
582 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
584 // Clear the word break info.
585 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
587 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
588 wordBreakInfoBuffer + endIndexPlusOne );
591 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
593 // Clear the scripts.
594 ClearCharacterRuns( startIndex,
596 mModel->mLogicalModel->mScriptRuns );
599 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
602 ClearCharacterRuns( startIndex,
604 mModel->mLogicalModel->mFontRuns );
607 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
609 if( NO_OPERATION != ( BIDI_INFO & operations ) )
611 // Clear the bidirectional paragraph info.
612 ClearCharacterRuns( startIndex,
614 mModel->mLogicalModel->mBidirectionalParagraphInfo );
616 // Clear the character's directions.
617 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
619 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
620 characterDirectionsBuffer + endIndexPlusOne );
623 if( NO_OPERATION != ( REORDER & operations ) )
625 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
626 uint32_t endRemoveIndex = startRemoveIndex;
627 ClearCharacterRuns( startIndex,
629 mModel->mLogicalModel->mBidirectionalLineInfo,
633 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
635 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
636 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
637 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
641 BidirectionalLineInfoRun& bidiLineInfo = *it;
643 free( bidiLineInfo.visualToLogicalMap );
644 bidiLineInfo.visualToLogicalMap = NULL;
647 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
648 bidirectionalLineInfoBuffer + endRemoveIndex );
653 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
655 const CharacterIndex endIndexPlusOne = endIndex + 1u;
656 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
658 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
659 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
660 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
662 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
663 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
665 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
667 // Update the character to glyph indices.
668 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
669 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
673 CharacterIndex& index = *it;
674 index -= numberOfGlyphsRemoved;
677 // Clear the character to glyph conversion table.
678 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
679 charactersToGlyphBuffer + endIndexPlusOne );
681 // Clear the glyphs per character table.
682 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
683 glyphsPerCharacterBuffer + endIndexPlusOne );
685 // Clear the glyphs buffer.
686 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
687 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
688 glyphsBuffer + endGlyphIndexPlusOne );
690 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
692 // Update the glyph to character indices.
693 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
694 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
698 CharacterIndex& index = *it;
699 index -= numberOfCharactersRemoved;
702 // Clear the glyphs to characters buffer.
703 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
704 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
706 // Clear the characters per glyph buffer.
707 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
708 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
709 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
711 // Clear the positions buffer.
712 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
713 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
714 positionsBuffer + endGlyphIndexPlusOne );
717 if( NO_OPERATION != ( LAYOUT & operations ) )
720 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
721 uint32_t endRemoveIndex = startRemoveIndex;
722 ClearCharacterRuns( startIndex,
724 mModel->mVisualModel->mLines,
728 // Will update the glyph runs.
729 startRemoveIndex = mModel->mVisualModel->mLines.Count();
730 endRemoveIndex = startRemoveIndex;
731 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
732 endGlyphIndexPlusOne - 1u,
733 mModel->mVisualModel->mLines,
737 // Set the line index from where to insert the new laid-out lines.
738 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
740 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
741 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
742 linesBuffer + endRemoveIndex );
745 if( NO_OPERATION != ( COLOR & operations ) )
747 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
749 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
750 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
751 colorIndexBuffer + endGlyphIndexPlusOne );
756 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
758 if( mTextUpdateInfo.mClearAll ||
759 ( ( 0u == startIndex ) &&
760 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
762 ClearFullModelData( operations );
766 // Clear the model data related with characters.
767 ClearCharacterModelData( startIndex, endIndex, operations );
769 // Clear the model data related with glyphs.
770 ClearGlyphModelData( startIndex, endIndex, operations );
773 // The estimated number of lines. Used to avoid reallocations when layouting.
774 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
776 mModel->mVisualModel->ClearCaches();
779 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
781 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
783 // Calculate the operations to be done.
784 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
786 if( NO_OPERATION == operations )
788 // Nothing to do if no operations are pending and required.
792 Vector<Character> utf32CharactersStar;
793 const Length characterCount = mModel->mLogicalModel->mText.Count();
794 const bool isPasswordInput = ( mEventData != NULL && mEventData->mPasswordInput &&
795 !mEventData->mIsShowingPlaceholderText && characterCount > 0 );
799 utf32CharactersStar.Resize( characterCount );
801 uint32_t* begin = utf32CharactersStar.Begin();
802 uint32_t* end = begin + characterCount;
803 while ( begin < end )
809 Vector<Character>& utf32Characters = isPasswordInput ? utf32CharactersStar : mModel->mLogicalModel->mText;
810 const Length numberOfCharacters = utf32Characters.Count();
812 // Index to the first character of the first paragraph to be updated.
813 CharacterIndex startIndex = 0u;
814 // Number of characters of the paragraphs to be removed.
815 Length paragraphCharacters = 0u;
817 CalculateTextUpdateIndices( paragraphCharacters );
818 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
820 if( mTextUpdateInfo.mClearAll ||
821 ( 0u != paragraphCharacters ) )
823 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
826 mTextUpdateInfo.mClearAll = false;
828 // Whether the model is updated.
829 bool updated = false;
831 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
832 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
834 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
836 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
837 // calculate the bidirectional info for each 'paragraph'.
838 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
839 // is not shaped together).
840 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
842 SetLineBreakInfo( utf32Characters,
844 requestedNumberOfCharacters,
847 // Create the paragraph info.
848 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
849 requestedNumberOfCharacters );
853 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
854 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
856 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
857 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
859 SetWordBreakInfo( utf32Characters,
861 requestedNumberOfCharacters,
866 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
867 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
869 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
870 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
872 if( getScripts || validateFonts )
874 // Validates the fonts assigned by the application or assigns default ones.
875 // It makes sure all the characters are going to be rendered by the correct font.
876 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
880 // Retrieves the scripts used in the text.
881 multilanguageSupport.SetScripts( utf32Characters,
883 requestedNumberOfCharacters,
889 // Validate the fonts set through the mark-up string.
890 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
892 // Get the default font's description.
893 TextAbstraction::FontDescription defaultFontDescription;
894 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
895 if( NULL != mFontDefaults )
897 defaultFontDescription = mFontDefaults->mFontDescription;
898 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
901 // Validates the fonts. If there is a character with no assigned font it sets a default one.
902 // After this call, fonts are validated.
903 multilanguageSupport.ValidateFonts( utf32Characters,
906 defaultFontDescription,
909 requestedNumberOfCharacters,
915 Vector<Character> mirroredUtf32Characters;
916 bool textMirrored = false;
917 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
918 if( NO_OPERATION != ( BIDI_INFO & operations ) )
920 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
921 bidirectionalInfo.Reserve( numberOfParagraphs );
923 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
924 SetBidirectionalInfo( utf32Characters,
928 requestedNumberOfCharacters,
931 if( 0u != bidirectionalInfo.Count() )
933 // Only set the character directions if there is right to left characters.
934 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
935 GetCharactersDirection( bidirectionalInfo,
938 requestedNumberOfCharacters,
941 // This paragraph has right to left text. Some characters may need to be mirrored.
942 // TODO: consider if the mirrored string can be stored as well.
944 textMirrored = GetMirroredText( utf32Characters,
948 requestedNumberOfCharacters,
949 mirroredUtf32Characters );
953 // There is no right to left characters. Clear the directions vector.
954 mModel->mLogicalModel->mCharacterDirections.Clear();
959 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
960 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
961 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
962 Vector<GlyphIndex> newParagraphGlyphs;
963 newParagraphGlyphs.Reserve( numberOfParagraphs );
965 const Length currentNumberOfGlyphs = glyphs.Count();
966 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
968 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
970 ShapeText( textToShape,
975 mTextUpdateInfo.mStartGlyphIndex,
976 requestedNumberOfCharacters,
978 glyphsToCharactersMap,
980 newParagraphGlyphs );
982 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
983 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
984 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
988 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
990 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
992 GlyphInfo* glyphsBuffer = glyphs.Begin();
993 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
995 // Update the width and advance of all new paragraph characters.
996 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
998 const GlyphIndex index = *it;
999 GlyphInfo& glyph = *( glyphsBuffer + index );
1001 glyph.xBearing = 0.f;
1003 glyph.advance = 0.f;
1008 if( NO_OPERATION != ( COLOR & operations ) )
1010 // Set the color runs in glyphs.
1011 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1012 mModel->mVisualModel->mCharactersToGlyph,
1013 mModel->mVisualModel->mGlyphsPerCharacter,
1015 mTextUpdateInfo.mStartGlyphIndex,
1016 requestedNumberOfCharacters,
1017 mModel->mVisualModel->mColors,
1018 mModel->mVisualModel->mColorIndices );
1023 if( ( NULL != mEventData ) &&
1024 mEventData->mPreEditFlag &&
1025 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1027 // Add the underline for the pre-edit text.
1028 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1029 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1031 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1032 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1033 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1034 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1036 GlyphRun underlineRun;
1037 underlineRun.glyphIndex = glyphStart;
1038 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1040 // TODO: At the moment the underline runs are only for pre-edit.
1041 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1044 // The estimated number of lines. Used to avoid reallocations when layouting.
1045 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1047 // Set the previous number of characters for the next time the text is updated.
1048 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1053 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1055 // Sets the default text's color.
1056 inputStyle.textColor = mTextColor;
1057 inputStyle.isDefaultColor = true;
1059 inputStyle.familyName.clear();
1060 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1061 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1062 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1063 inputStyle.size = 0.f;
1065 inputStyle.lineSpacing = 0.f;
1067 inputStyle.underlineProperties.clear();
1068 inputStyle.shadowProperties.clear();
1069 inputStyle.embossProperties.clear();
1070 inputStyle.outlineProperties.clear();
1072 inputStyle.isFamilyDefined = false;
1073 inputStyle.isWeightDefined = false;
1074 inputStyle.isWidthDefined = false;
1075 inputStyle.isSlantDefined = false;
1076 inputStyle.isSizeDefined = false;
1078 inputStyle.isLineSpacingDefined = false;
1080 inputStyle.isUnderlineDefined = false;
1081 inputStyle.isShadowDefined = false;
1082 inputStyle.isEmbossDefined = false;
1083 inputStyle.isOutlineDefined = false;
1085 // Sets the default font's family name, weight, width, slant and size.
1088 if( mFontDefaults->familyDefined )
1090 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1091 inputStyle.isFamilyDefined = true;
1094 if( mFontDefaults->weightDefined )
1096 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1097 inputStyle.isWeightDefined = true;
1100 if( mFontDefaults->widthDefined )
1102 inputStyle.width = mFontDefaults->mFontDescription.width;
1103 inputStyle.isWidthDefined = true;
1106 if( mFontDefaults->slantDefined )
1108 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1109 inputStyle.isSlantDefined = true;
1112 if( mFontDefaults->sizeDefined )
1114 inputStyle.size = mFontDefaults->mDefaultPointSize;
1115 inputStyle.isSizeDefined = true;
1120 float Controller::Impl::GetDefaultFontLineHeight()
1122 FontId defaultFontId = 0u;
1123 if( NULL == mFontDefaults )
1125 TextAbstraction::FontDescription fontDescription;
1126 defaultFontId = mFontClient.GetFontId( fontDescription );
1130 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1133 Text::FontMetrics fontMetrics;
1134 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1136 return( fontMetrics.ascender - fontMetrics.descender );
1139 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1141 if( NULL == mEventData )
1143 // Nothing to do if there is no text input.
1147 int keyCode = event.p1.mInt;
1149 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1151 if( mEventData->mPrimaryCursorPosition > 0u )
1153 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1156 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1158 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1160 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1163 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1165 // Get first the line index of the current cursor position index.
1166 CharacterIndex characterIndex = 0u;
1168 if( mEventData->mPrimaryCursorPosition > 0u )
1170 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1173 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1174 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1176 // Retrieve the cursor position info.
1177 CursorInfo cursorInfo;
1178 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1181 // Get the line above.
1182 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1184 // Get the next hit 'y' point.
1185 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1187 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1188 bool matchedCharacter = false;
1189 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1190 mModel->mLogicalModel,
1192 mEventData->mCursorHookPositionX,
1194 CharacterHitTest::TAP,
1197 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1199 // Get first the line index of the current cursor position index.
1200 CharacterIndex characterIndex = 0u;
1202 if( mEventData->mPrimaryCursorPosition > 0u )
1204 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1207 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1209 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1211 // Retrieve the cursor position info.
1212 CursorInfo cursorInfo;
1213 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1216 // Get the line below.
1217 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1219 // Get the next hit 'y' point.
1220 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1222 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1223 bool matchedCharacter = false;
1224 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1225 mModel->mLogicalModel,
1227 mEventData->mCursorHookPositionX,
1229 CharacterHitTest::TAP,
1234 mEventData->mUpdateCursorPosition = true;
1235 mEventData->mUpdateInputStyle = true;
1236 mEventData->mScrollAfterUpdatePosition = true;
1239 void Controller::Impl::OnTapEvent( const Event& event )
1241 if( NULL != mEventData )
1243 const unsigned int tapCount = event.p1.mUint;
1245 if( 1u == tapCount )
1247 if( IsShowingRealText() )
1249 // Convert from control's coords to text's coords.
1250 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1251 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1253 // Keep the tap 'x' position. Used to move the cursor.
1254 mEventData->mCursorHookPositionX = xPosition;
1256 // Whether to touch point hits on a glyph.
1257 bool matchedCharacter = false;
1258 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1259 mModel->mLogicalModel,
1263 CharacterHitTest::TAP,
1266 // When the cursor position is changing, delay cursor blinking
1267 mEventData->mDecorator->DelayCursorBlink();
1271 mEventData->mPrimaryCursorPosition = 0u;
1274 mEventData->mUpdateCursorPosition = true;
1275 mEventData->mUpdateGrabHandlePosition = true;
1276 mEventData->mScrollAfterUpdatePosition = true;
1277 mEventData->mUpdateInputStyle = true;
1279 // Notify the cursor position to the imf manager.
1280 if( mEventData->mImfManager )
1282 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1283 mEventData->mImfManager.NotifyCursorPosition();
1286 else if( 2u == tapCount )
1288 if( mEventData->mSelectionEnabled )
1290 // Convert from control's coords to text's coords.
1291 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1292 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1294 // Calculates the logical position from the x,y coords.
1295 RepositionSelectionHandles( xPosition,
1297 mEventData->mDoubleTapAction );
1303 void Controller::Impl::OnPanEvent( const Event& event )
1305 if( NULL == mEventData )
1307 // Nothing to do if there is no text input.
1311 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1312 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1314 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1316 // Nothing to do if scrolling is not enabled.
1320 const int state = event.p1.mInt;
1324 case Gesture::Started:
1326 // Will remove the cursor, handles or text's popup, ...
1327 ChangeState( EventData::TEXT_PANNING );
1330 case Gesture::Continuing:
1332 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1333 const Vector2 currentScroll = mModel->mScrollPosition;
1335 if( isHorizontalScrollEnabled )
1337 const float displacementX = event.p2.mFloat;
1338 mModel->mScrollPosition.x += displacementX;
1340 ClampHorizontalScroll( layoutSize );
1343 if( isVerticalScrollEnabled )
1345 const float displacementY = event.p3.mFloat;
1346 mModel->mScrollPosition.y += displacementY;
1348 ClampVerticalScroll( layoutSize );
1351 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1354 case Gesture::Finished:
1355 case Gesture::Cancelled: // FALLTHROUGH
1357 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1358 ChangeState( mEventData->mPreviousState );
1366 void Controller::Impl::OnLongPressEvent( const Event& event )
1368 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1370 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1372 ChangeState( EventData::EDITING_WITH_POPUP );
1373 mEventData->mDecoratorUpdated = true;
1374 mEventData->mUpdateInputStyle = true;
1378 if( mEventData->mSelectionEnabled )
1380 // Convert from control's coords to text's coords.
1381 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1382 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1384 // Calculates the logical position from the x,y coords.
1385 RepositionSelectionHandles( xPosition,
1387 mEventData->mLongPressAction );
1392 void Controller::Impl::OnHandleEvent( const Event& event )
1394 if( NULL == mEventData )
1396 // Nothing to do if there is no text input.
1400 const unsigned int state = event.p1.mUint;
1401 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1402 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1404 if( HANDLE_PRESSED == state )
1406 // Convert from decorator's coords to text's coords.
1407 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1408 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1410 // Need to calculate the handle's new position.
1411 bool matchedCharacter = false;
1412 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1413 mModel->mLogicalModel,
1417 CharacterHitTest::SCROLL,
1420 if( Event::GRAB_HANDLE_EVENT == event.type )
1422 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1424 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1426 // Updates the cursor position if the handle's new position is different than the current one.
1427 mEventData->mUpdateCursorPosition = true;
1428 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1429 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1430 mEventData->mPrimaryCursorPosition = handleNewPosition;
1433 // 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.
1434 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1436 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1438 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1440 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1441 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1443 // Updates the highlight box if the handle's new position is different than the current one.
1444 mEventData->mUpdateHighlightBox = true;
1445 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1446 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1447 mEventData->mLeftSelectionPosition = handleNewPosition;
1450 // 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.
1451 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1453 // Will define the order to scroll the text to match the handle position.
1454 mEventData->mIsLeftHandleSelected = true;
1455 mEventData->mIsRightHandleSelected = false;
1457 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1459 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1461 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1462 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1464 // Updates the highlight box if the handle's new position is different than the current one.
1465 mEventData->mUpdateHighlightBox = true;
1466 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1467 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1468 mEventData->mRightSelectionPosition = handleNewPosition;
1471 // 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.
1472 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1474 // Will define the order to scroll the text to match the handle position.
1475 mEventData->mIsLeftHandleSelected = false;
1476 mEventData->mIsRightHandleSelected = true;
1478 } // end ( HANDLE_PRESSED == state )
1479 else if( ( HANDLE_RELEASED == state ) ||
1480 handleStopScrolling )
1482 CharacterIndex handlePosition = 0u;
1483 if( handleStopScrolling || isSmoothHandlePanEnabled )
1485 // Convert from decorator's coords to text's coords.
1486 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1487 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1489 bool matchedCharacter = false;
1490 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1491 mModel->mLogicalModel,
1495 CharacterHitTest::SCROLL,
1499 if( Event::GRAB_HANDLE_EVENT == event.type )
1501 mEventData->mUpdateCursorPosition = true;
1502 mEventData->mUpdateGrabHandlePosition = true;
1503 mEventData->mUpdateInputStyle = true;
1505 if( !IsClipboardEmpty() )
1507 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1510 if( handleStopScrolling || isSmoothHandlePanEnabled )
1512 mEventData->mScrollAfterUpdatePosition = true;
1513 mEventData->mPrimaryCursorPosition = handlePosition;
1516 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1518 ChangeState( EventData::SELECTING );
1520 mEventData->mUpdateHighlightBox = true;
1521 mEventData->mUpdateLeftSelectionPosition = true;
1522 mEventData->mUpdateRightSelectionPosition = true;
1524 if( handleStopScrolling || isSmoothHandlePanEnabled )
1526 mEventData->mScrollAfterUpdatePosition = true;
1528 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1529 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1531 mEventData->mLeftSelectionPosition = handlePosition;
1535 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1537 ChangeState( EventData::SELECTING );
1539 mEventData->mUpdateHighlightBox = true;
1540 mEventData->mUpdateRightSelectionPosition = true;
1541 mEventData->mUpdateLeftSelectionPosition = true;
1543 if( handleStopScrolling || isSmoothHandlePanEnabled )
1545 mEventData->mScrollAfterUpdatePosition = true;
1546 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1547 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1549 mEventData->mRightSelectionPosition = handlePosition;
1554 mEventData->mDecoratorUpdated = true;
1555 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1556 else if( HANDLE_SCROLLING == state )
1558 const float xSpeed = event.p2.mFloat;
1559 const float ySpeed = event.p3.mFloat;
1560 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1561 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1563 mModel->mScrollPosition.x += xSpeed;
1564 mModel->mScrollPosition.y += ySpeed;
1566 ClampHorizontalScroll( layoutSize );
1567 ClampVerticalScroll( layoutSize );
1569 bool endOfScroll = false;
1570 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1572 // Notify the decorator there is no more text to scroll.
1573 // The decorator won't send more scroll events.
1574 mEventData->mDecorator->NotifyEndOfScroll();
1575 // Still need to set the position of the handle.
1579 // Set the position of the handle.
1580 const bool scrollRightDirection = xSpeed > 0.f;
1581 const bool scrollBottomDirection = ySpeed > 0.f;
1582 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1583 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1585 if( Event::GRAB_HANDLE_EVENT == event.type )
1587 ChangeState( EventData::GRAB_HANDLE_PANNING );
1589 // Get the grab handle position in decorator coords.
1590 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1592 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1594 // Position the grag handle close to either the left or right edge.
1595 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1598 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1600 position.x = mEventData->mCursorHookPositionX;
1602 // Position the grag handle close to either the top or bottom edge.
1603 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1606 // Get the new handle position.
1607 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1608 bool matchedCharacter = false;
1609 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1610 mModel->mLogicalModel,
1612 position.x - mModel->mScrollPosition.x,
1613 position.y - mModel->mScrollPosition.y,
1614 CharacterHitTest::SCROLL,
1617 if( mEventData->mPrimaryCursorPosition != handlePosition )
1619 mEventData->mUpdateCursorPosition = true;
1620 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1621 mEventData->mScrollAfterUpdatePosition = true;
1622 mEventData->mPrimaryCursorPosition = handlePosition;
1624 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1626 // Updates the decorator if the soft handle panning is enabled.
1627 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1629 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1631 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1633 // Get the selection handle position in decorator coords.
1634 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1636 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1638 // Position the selection handle close to either the left or right edge.
1639 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1642 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1644 position.x = mEventData->mCursorHookPositionX;
1646 // Position the grag handle close to either the top or bottom edge.
1647 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1650 // Get the new handle position.
1651 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1652 bool matchedCharacter = false;
1653 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1654 mModel->mLogicalModel,
1656 position.x - mModel->mScrollPosition.x,
1657 position.y - mModel->mScrollPosition.y,
1658 CharacterHitTest::SCROLL,
1661 if( leftSelectionHandleEvent )
1663 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1665 if( differentHandles || endOfScroll )
1667 mEventData->mUpdateHighlightBox = true;
1668 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1669 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1670 mEventData->mLeftSelectionPosition = handlePosition;
1675 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1676 if( differentHandles || endOfScroll )
1678 mEventData->mUpdateHighlightBox = true;
1679 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1680 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1681 mEventData->mRightSelectionPosition = handlePosition;
1685 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1687 RepositionSelectionHandles();
1689 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1692 mEventData->mDecoratorUpdated = true;
1693 } // end ( HANDLE_SCROLLING == state )
1696 void Controller::Impl::OnSelectEvent( const Event& event )
1698 if( NULL == mEventData )
1700 // Nothing to do if there is no text.
1704 if( mEventData->mSelectionEnabled )
1706 // Convert from control's coords to text's coords.
1707 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1708 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1710 // Calculates the logical position from the x,y coords.
1711 RepositionSelectionHandles( xPosition,
1713 Controller::NoTextTap::HIGHLIGHT );
1717 void Controller::Impl::OnSelectAllEvent()
1719 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1721 if( NULL == mEventData )
1723 // Nothing to do if there is no text.
1727 if( mEventData->mSelectionEnabled )
1729 ChangeState( EventData::SELECTING );
1731 mEventData->mLeftSelectionPosition = 0u;
1732 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1734 mEventData->mScrollAfterUpdatePosition = true;
1735 mEventData->mUpdateLeftSelectionPosition = true;
1736 mEventData->mUpdateRightSelectionPosition = true;
1737 mEventData->mUpdateHighlightBox = true;
1741 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1743 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1745 // Nothing to select if handles are in the same place.
1746 selectedText.clear();
1750 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1752 //Get start and end position of selection
1753 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1754 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1756 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1757 const Length numberOfCharacters = utf32Characters.Count();
1759 // Validate the start and end selection points
1760 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1762 //Get text as a UTF8 string
1763 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1765 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1767 // Keep a copy of the current input style.
1768 InputStyle currentInputStyle;
1769 currentInputStyle.Copy( mEventData->mInputStyle );
1771 // Set as input style the style of the first deleted character.
1772 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1774 // Compare if the input style has changed.
1775 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1777 if( hasInputStyleChanged )
1779 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1780 // Queue the input style changed signal.
1781 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1784 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1786 // Mark the paragraphs to be updated.
1787 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1788 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1790 // Delete text between handles
1791 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1792 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1793 utf32Characters.Erase( first, last );
1795 // Will show the cursor at the first character of the selection.
1796 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1800 // Will show the cursor at the last character of the selection.
1801 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1804 mEventData->mDecoratorUpdated = true;
1808 void Controller::Impl::ShowClipboard()
1812 mClipboard.ShowClipboard();
1816 void Controller::Impl::HideClipboard()
1818 if( mClipboard && mClipboardHideEnabled )
1820 mClipboard.HideClipboard();
1824 void Controller::Impl::SetClipboardHideEnable(bool enable)
1826 mClipboardHideEnabled = enable;
1829 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1831 //Send string to clipboard
1832 return ( mClipboard && mClipboard.SetItem( source ) );
1835 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1837 std::string selectedText;
1838 RetrieveSelection( selectedText, deleteAfterSending );
1839 CopyStringToClipboard( selectedText );
1840 ChangeState( EventData::EDITING );
1843 void Controller::Impl::RequestGetTextFromClipboard()
1847 mClipboard.RequestItem();
1851 void Controller::Impl::RepositionSelectionHandles()
1853 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1854 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1856 if( selectionStart == selectionEnd )
1858 // Nothing to select if handles are in the same place.
1862 mEventData->mDecorator->ClearHighlights();
1864 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1865 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1866 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1867 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1868 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1869 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1870 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1872 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1873 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1874 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1876 // Swap the indices if the start is greater than the end.
1877 const bool indicesSwapped = selectionStart > selectionEnd;
1879 // Tell the decorator to flip the selection handles if needed.
1880 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1882 if( indicesSwapped )
1884 std::swap( selectionStart, selectionEnd );
1887 // Get the indices to the first and last selected glyphs.
1888 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1889 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1890 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1891 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1893 // Get the lines where the glyphs are laid-out.
1894 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1896 LineIndex lineIndex = 0u;
1897 Length numberOfLines = 0u;
1898 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1899 1u + glyphEnd - glyphStart,
1902 const LineIndex firstLineIndex = lineIndex;
1904 // Create the structure to store some selection box info.
1905 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1906 selectionBoxLinesInfo.Resize( numberOfLines );
1908 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1909 selectionBoxInfo->minX = MAX_FLOAT;
1910 selectionBoxInfo->maxX = MIN_FLOAT;
1912 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1913 float minHighlightX = std::numeric_limits<float>::max();
1914 float maxHighlightX = std::numeric_limits<float>::min();
1916 Vector2 highLightPosition; // The highlight position in decorator's coords.
1918 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1920 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1921 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1924 // Transform to decorator's (control) coords.
1925 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1927 lineRun += firstLineIndex;
1929 // The line height is the addition of the line ascender and the line descender.
1930 // However, the line descender has a negative value, hence the subtraction.
1931 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1933 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1935 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1936 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1937 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1939 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1940 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1941 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1943 // The number of quads of the selection box.
1944 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1945 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1947 // Count the actual number of quads.
1948 unsigned int actualNumberOfQuads = 0u;
1951 // Traverse the glyphs.
1952 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1954 const GlyphInfo& glyph = *( glyphsBuffer + index );
1955 const Vector2& position = *( positionsBuffer + index );
1957 if( splitStartGlyph )
1959 // 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.
1961 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1962 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1963 // Get the direction of the character.
1964 CharacterDirection isCurrentRightToLeft = false;
1965 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1967 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1970 // The end point could be in the middle of the ligature.
1971 // Calculate the number of characters selected.
1972 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1974 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1975 quad.y = selectionBoxInfo->lineOffset;
1976 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1977 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1979 // Store the min and max 'x' for each line.
1980 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1981 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1983 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1984 ++actualNumberOfQuads;
1986 splitStartGlyph = false;
1990 if( splitEndGlyph && ( index == glyphEnd ) )
1992 // 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.
1994 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1995 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1996 // Get the direction of the character.
1997 CharacterDirection isCurrentRightToLeft = false;
1998 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2000 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2003 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2005 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2006 quad.y = selectionBoxInfo->lineOffset;
2007 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2008 quad.w = quad.y + selectionBoxInfo->lineHeight;
2010 // Store the min and max 'x' for each line.
2011 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2012 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2014 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2016 ++actualNumberOfQuads;
2018 splitEndGlyph = false;
2022 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2023 quad.y = selectionBoxInfo->lineOffset;
2024 quad.z = quad.x + glyph.advance;
2025 quad.w = quad.y + selectionBoxInfo->lineHeight;
2027 // Store the min and max 'x' for each line.
2028 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2029 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2031 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2033 ++actualNumberOfQuads;
2035 // Whether to retrieve the next line.
2036 if( index == lastGlyphOfLine )
2039 if( lineIndex < firstLineIndex + numberOfLines )
2041 // Retrieve the next line.
2044 // Get the last glyph of the new line.
2045 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2047 // Keep the offset and height of the current selection box.
2048 const float currentLineOffset = selectionBoxInfo->lineOffset;
2049 const float currentLineHeight = selectionBoxInfo->lineHeight;
2051 // Get the selection box info for the next line.
2054 selectionBoxInfo->minX = MAX_FLOAT;
2055 selectionBoxInfo->maxX = MIN_FLOAT;
2057 // Update the line's vertical offset.
2058 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2060 // The line height is the addition of the line ascender and the line descender.
2061 // However, the line descender has a negative value, hence the subtraction.
2062 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2067 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2068 // The final width is calculated after 'boxifying' the selection.
2069 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2070 endIt = selectionBoxLinesInfo.End();
2074 const SelectionBoxInfo& info = *it;
2076 // Update the size of the highlighted text.
2077 highLightSize.height += info.lineHeight;
2078 minHighlightX = std::min( minHighlightX, info.minX );
2079 maxHighlightX = std::max( maxHighlightX, info.maxX );
2082 // Add extra geometry to 'boxify' the selection.
2084 if( 1u < numberOfLines )
2086 // Boxify the first line.
2087 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2088 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2090 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2091 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2096 quad.y = firstSelectionBoxLineInfo.lineOffset;
2097 quad.z = firstSelectionBoxLineInfo.minX;
2098 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2100 // Boxify at the beginning of the line.
2101 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2103 ++actualNumberOfQuads;
2105 // Update the size of the highlighted text.
2106 minHighlightX = 0.f;
2111 quad.x = firstSelectionBoxLineInfo.maxX;
2112 quad.y = firstSelectionBoxLineInfo.lineOffset;
2113 quad.z = mModel->mVisualModel->mControlSize.width;
2114 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2116 // Boxify at the end of the line.
2117 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2119 ++actualNumberOfQuads;
2121 // Update the size of the highlighted text.
2122 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2125 // Boxify the central lines.
2126 if( 2u < numberOfLines )
2128 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2129 endIt = selectionBoxLinesInfo.End() - 1u;
2133 const SelectionBoxInfo& info = *it;
2136 quad.y = info.lineOffset;
2138 quad.w = info.lineOffset + info.lineHeight;
2140 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2142 ++actualNumberOfQuads;
2145 quad.y = info.lineOffset;
2146 quad.z = mModel->mVisualModel->mControlSize.width;
2147 quad.w = info.lineOffset + info.lineHeight;
2149 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2151 ++actualNumberOfQuads;
2154 // Update the size of the highlighted text.
2155 minHighlightX = 0.f;
2156 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2159 // Boxify the last line.
2160 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2161 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2163 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2164 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2169 quad.y = lastSelectionBoxLineInfo.lineOffset;
2170 quad.z = lastSelectionBoxLineInfo.minX;
2171 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2173 // Boxify at the beginning of the line.
2174 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2176 ++actualNumberOfQuads;
2178 // Update the size of the highlighted text.
2179 minHighlightX = 0.f;
2184 quad.x = lastSelectionBoxLineInfo.maxX;
2185 quad.y = lastSelectionBoxLineInfo.lineOffset;
2186 quad.z = mModel->mVisualModel->mControlSize.width;
2187 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2189 // Boxify at the end of the line.
2190 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2192 ++actualNumberOfQuads;
2194 // Update the size of the highlighted text.
2195 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2199 // Set the actual number of quads.
2200 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2202 // Sets the highlight's size and position. In decorator's coords.
2203 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2204 highLightSize.width = maxHighlightX - minHighlightX;
2206 highLightPosition.x = minHighlightX;
2207 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2208 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2210 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2212 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2214 CursorInfo primaryCursorInfo;
2215 GetCursorPosition( mEventData->mLeftSelectionPosition,
2216 primaryCursorInfo );
2218 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2220 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2222 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2223 primaryCursorInfo.lineHeight );
2225 CursorInfo secondaryCursorInfo;
2226 GetCursorPosition( mEventData->mRightSelectionPosition,
2227 secondaryCursorInfo );
2229 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2231 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2232 secondaryPosition.x,
2233 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2234 secondaryCursorInfo.lineHeight );
2237 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2238 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2240 // Set the flag to update the decorator.
2241 mEventData->mDecoratorUpdated = true;
2244 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2246 if( NULL == mEventData )
2248 // Nothing to do if there is no text input.
2252 if( IsShowingPlaceholderText() )
2254 // Nothing to do if there is the place-holder text.
2258 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2259 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2260 if( ( 0 == numberOfGlyphs ) ||
2261 ( 0 == numberOfLines ) )
2263 // Nothing to do if there is no text.
2267 // Find which word was selected
2268 CharacterIndex selectionStart( 0 );
2269 CharacterIndex selectionEnd( 0 );
2270 CharacterIndex noTextHitIndex( 0 );
2271 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2272 mModel->mLogicalModel,
2279 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2281 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2283 ChangeState( EventData::SELECTING );
2285 mEventData->mLeftSelectionPosition = selectionStart;
2286 mEventData->mRightSelectionPosition = selectionEnd;
2288 mEventData->mUpdateLeftSelectionPosition = true;
2289 mEventData->mUpdateRightSelectionPosition = true;
2290 mEventData->mUpdateHighlightBox = true;
2292 // It may happen an IMF commit event arrives before the selection event
2293 // if the IMF manager is in pre-edit state. The commit event will set the
2294 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2295 // to false, the highlight box won't be updated.
2296 mEventData->mUpdateCursorPosition = false;
2298 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2300 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2302 // Nothing to select. i.e. a white space, out of bounds
2303 ChangeState( EventData::EDITING_WITH_POPUP );
2305 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2307 mEventData->mUpdateCursorPosition = true;
2308 mEventData->mUpdateGrabHandlePosition = true;
2309 mEventData->mScrollAfterUpdatePosition = true;
2310 mEventData->mUpdateInputStyle = true;
2312 else if( Controller::NoTextTap::NO_ACTION == action )
2314 // Nothing to select. i.e. a white space, out of bounds
2315 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2317 mEventData->mUpdateCursorPosition = true;
2318 mEventData->mUpdateGrabHandlePosition = true;
2319 mEventData->mScrollAfterUpdatePosition = true;
2320 mEventData->mUpdateInputStyle = true;
2324 void Controller::Impl::SetPopupButtons()
2327 * Sets the Popup buttons to be shown depending on State.
2329 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2331 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2334 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2336 if( EventData::SELECTING == mEventData->mState )
2338 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2340 if( !IsClipboardEmpty() )
2342 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2343 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2346 if( !mEventData->mAllTextSelected )
2348 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2351 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2353 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2355 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2358 if( !IsClipboardEmpty() )
2360 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2361 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2364 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2366 if ( !IsClipboardEmpty() )
2368 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2369 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2373 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2376 void Controller::Impl::ChangeState( EventData::State newState )
2378 if( NULL == mEventData )
2380 // Nothing to do if there is no text input.
2384 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2386 if( mEventData->mState != newState )
2388 mEventData->mPreviousState = mEventData->mState;
2389 mEventData->mState = newState;
2391 switch( mEventData->mState )
2393 case EventData::INACTIVE:
2395 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2396 mEventData->mDecorator->StopCursorBlink();
2397 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2398 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2399 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2400 mEventData->mDecorator->SetHighlightActive( false );
2401 mEventData->mDecorator->SetPopupActive( false );
2402 mEventData->mDecoratorUpdated = true;
2405 case EventData::INTERRUPTED:
2407 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2408 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2409 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2410 mEventData->mDecorator->SetHighlightActive( false );
2411 mEventData->mDecorator->SetPopupActive( false );
2412 mEventData->mDecoratorUpdated = true;
2415 case EventData::SELECTING:
2417 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2418 mEventData->mDecorator->StopCursorBlink();
2419 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2420 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2421 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2422 mEventData->mDecorator->SetHighlightActive( true );
2423 if( mEventData->mGrabHandlePopupEnabled )
2426 mEventData->mDecorator->SetPopupActive( true );
2428 mEventData->mDecoratorUpdated = true;
2431 case EventData::EDITING:
2433 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2434 if( mEventData->mCursorBlinkEnabled )
2436 mEventData->mDecorator->StartCursorBlink();
2438 // Grab handle is not shown until a tap is received whilst EDITING
2439 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2440 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2441 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2442 mEventData->mDecorator->SetHighlightActive( false );
2443 if( mEventData->mGrabHandlePopupEnabled )
2445 mEventData->mDecorator->SetPopupActive( false );
2447 mEventData->mDecoratorUpdated = true;
2450 case EventData::EDITING_WITH_POPUP:
2452 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2454 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2455 if( mEventData->mCursorBlinkEnabled )
2457 mEventData->mDecorator->StartCursorBlink();
2459 if( mEventData->mSelectionEnabled )
2461 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2462 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2463 mEventData->mDecorator->SetHighlightActive( false );
2467 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2469 if( mEventData->mGrabHandlePopupEnabled )
2472 mEventData->mDecorator->SetPopupActive( true );
2474 mEventData->mDecoratorUpdated = true;
2477 case EventData::EDITING_WITH_GRAB_HANDLE:
2479 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2481 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2482 if( mEventData->mCursorBlinkEnabled )
2484 mEventData->mDecorator->StartCursorBlink();
2486 // Grab handle is not shown until a tap is received whilst EDITING
2487 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2488 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2489 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2490 mEventData->mDecorator->SetHighlightActive( false );
2491 if( mEventData->mGrabHandlePopupEnabled )
2493 mEventData->mDecorator->SetPopupActive( false );
2495 mEventData->mDecoratorUpdated = true;
2498 case EventData::SELECTION_HANDLE_PANNING:
2500 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2501 mEventData->mDecorator->StopCursorBlink();
2502 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2503 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2504 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2505 mEventData->mDecorator->SetHighlightActive( true );
2506 if( mEventData->mGrabHandlePopupEnabled )
2508 mEventData->mDecorator->SetPopupActive( false );
2510 mEventData->mDecoratorUpdated = true;
2513 case EventData::GRAB_HANDLE_PANNING:
2515 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2517 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2518 if( mEventData->mCursorBlinkEnabled )
2520 mEventData->mDecorator->StartCursorBlink();
2522 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2523 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2524 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2525 mEventData->mDecorator->SetHighlightActive( false );
2526 if( mEventData->mGrabHandlePopupEnabled )
2528 mEventData->mDecorator->SetPopupActive( false );
2530 mEventData->mDecoratorUpdated = true;
2533 case EventData::EDITING_WITH_PASTE_POPUP:
2535 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2537 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2538 if( mEventData->mCursorBlinkEnabled )
2540 mEventData->mDecorator->StartCursorBlink();
2543 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2544 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2545 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2546 mEventData->mDecorator->SetHighlightActive( false );
2548 if( mEventData->mGrabHandlePopupEnabled )
2551 mEventData->mDecorator->SetPopupActive( true );
2553 mEventData->mDecoratorUpdated = true;
2556 case EventData::TEXT_PANNING:
2558 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2559 mEventData->mDecorator->StopCursorBlink();
2560 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2561 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2562 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2564 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2565 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2566 mEventData->mDecorator->SetHighlightActive( true );
2569 if( mEventData->mGrabHandlePopupEnabled )
2571 mEventData->mDecorator->SetPopupActive( false );
2574 mEventData->mDecoratorUpdated = true;
2581 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2582 CursorInfo& cursorInfo )
2584 if( !IsShowingRealText() )
2586 // Do not want to use the place-holder text to set the cursor position.
2588 // Use the line's height of the font's family set to set the cursor's size.
2589 // If there is no font's family set, use the default font.
2590 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2592 cursorInfo.lineOffset = 0.f;
2593 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2594 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2596 switch( mModel->mHorizontalAlignment )
2598 case Layout::HORIZONTAL_ALIGN_BEGIN:
2600 cursorInfo.primaryPosition.x = 0.f;
2603 case Layout::HORIZONTAL_ALIGN_CENTER:
2605 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2608 case Layout::HORIZONTAL_ALIGN_END:
2610 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2615 // Nothing else to do.
2619 Text::GetCursorPosition( mModel->mVisualModel,
2620 mModel->mLogicalModel,
2625 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2627 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2629 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2630 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2632 if( 0.f > cursorInfo.primaryPosition.x )
2634 cursorInfo.primaryPosition.x = 0.f;
2637 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2638 if( cursorInfo.primaryPosition.x > edgeWidth )
2640 cursorInfo.primaryPosition.x = edgeWidth;
2645 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2647 if( NULL == mEventData )
2649 // Nothing to do if there is no text input.
2653 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2655 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2656 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2658 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2659 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2661 if( numberOfCharacters > 1u )
2663 const Script script = mModel->mLogicalModel->GetScript( index );
2664 if( HasLigatureMustBreak( script ) )
2666 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2667 numberOfCharacters = 1u;
2672 while( 0u == numberOfCharacters )
2675 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2679 if( index < mEventData->mPrimaryCursorPosition )
2681 cursorIndex -= numberOfCharacters;
2685 cursorIndex += numberOfCharacters;
2688 // Will update the cursor hook position.
2689 mEventData->mUpdateCursorHookPosition = true;
2694 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2696 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2697 if( NULL == mEventData )
2699 // Nothing to do if there is no text input.
2700 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2704 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2706 // Sets the cursor position.
2707 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2710 cursorInfo.primaryCursorHeight,
2711 cursorInfo.lineHeight );
2712 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2714 if( mEventData->mUpdateGrabHandlePosition )
2716 // Sets the grab handle position.
2717 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2719 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2720 cursorInfo.lineHeight );
2723 if( cursorInfo.isSecondaryCursor )
2725 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2726 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2727 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2728 cursorInfo.secondaryCursorHeight,
2729 cursorInfo.lineHeight );
2730 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2733 // Set which cursors are active according the state.
2734 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2736 if( cursorInfo.isSecondaryCursor )
2738 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2742 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2747 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2750 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2753 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2754 const CursorInfo& cursorInfo )
2756 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2757 ( RIGHT_SELECTION_HANDLE != handleType ) )
2762 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2764 // Sets the handle's position.
2765 mEventData->mDecorator->SetPosition( handleType,
2767 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2768 cursorInfo.lineHeight );
2770 // If selection handle at start of the text and other at end of the text then all text is selected.
2771 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2772 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2773 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2776 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2778 // Clamp between -space & -alignment offset.
2780 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2782 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2783 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2784 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2786 mEventData->mDecoratorUpdated = true;
2790 mModel->mScrollPosition.x = 0.f;
2794 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2796 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2798 // Nothing to do if the text is single line.
2802 // Clamp between -space & 0.
2803 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2805 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2806 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2807 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2809 mEventData->mDecoratorUpdated = true;
2813 mModel->mScrollPosition.y = 0.f;
2817 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2819 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2821 // position is in actor's coords.
2822 const float positionEndX = position.x + cursorWidth;
2823 const float positionEndY = position.y + lineHeight;
2825 // Transform the position to decorator coords.
2826 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2827 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2829 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2830 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2832 if( decoratorPositionBeginX < 0.f )
2834 mModel->mScrollPosition.x = -position.x;
2836 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2838 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2841 if( decoratorPositionBeginY < 0.f )
2843 mModel->mScrollPosition.y = -position.y;
2845 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2847 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2851 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2853 // Get the current cursor position in decorator coords.
2854 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2856 // Calculate the offset to match the cursor position before the character was deleted.
2857 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2858 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2860 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2861 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2863 // Makes the new cursor position visible if needed.
2864 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2867 void Controller::Impl::RequestRelayout()
2869 if( NULL != mControlInterface )
2871 mControlInterface->RequestTextRelayout();
2877 } // namespace Toolkit