2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
41 * @brief Struct used to calculate the selection box.
43 struct SelectionBoxInfo
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
70 EventData::EventData( DecoratorPtr decorator )
71 : mDecorator( decorator ),
73 mPlaceholderFont( NULL ),
75 mPlaceholderTextActive(),
76 mPlaceholderTextInactive(),
77 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
79 mInputStyleChangedQueue(),
80 mPreviousState( INACTIVE ),
82 mPrimaryCursorPosition( 0u ),
83 mLeftSelectionPosition( 0u ),
84 mRightSelectionPosition( 0u ),
85 mPreEditStartPosition( 0u ),
87 mCursorHookPositionX( 0.f ),
88 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
89 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
90 mIsShowingPlaceholderText( false ),
91 mPreEditFlag( false ),
92 mDecoratorUpdated( false ),
93 mCursorBlinkEnabled( true ),
94 mGrabHandleEnabled( true ),
95 mGrabHandlePopupEnabled( true ),
96 mSelectionEnabled( true ),
97 mUpdateCursorHookPosition( false ),
98 mUpdateCursorPosition( false ),
99 mUpdateGrabHandlePosition( false ),
100 mUpdateLeftSelectionPosition( false ),
101 mUpdateRightSelectionPosition( false ),
102 mIsLeftHandleSelected( false ),
103 mIsRightHandleSelected( false ),
104 mUpdateHighlightBox( false ),
105 mScrollAfterUpdatePosition( false ),
106 mScrollAfterDelete( false ),
107 mAllTextSelected( false ),
108 mUpdateInputStyle( false ),
109 mPasswordInput( false ),
110 mIsPlaceholderPixelSize( false )
112 mImfManager = ImfManager::Get();
115 EventData::~EventData()
118 bool Controller::Impl::ProcessInputEvents()
120 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
121 if( NULL == mEventData )
123 // Nothing to do if there is no text input.
124 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
128 if( mEventData->mDecorator )
130 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
131 iter != mEventData->mEventQueue.end();
136 case Event::CURSOR_KEY_EVENT:
138 OnCursorKeyEvent( *iter );
141 case Event::TAP_EVENT:
146 case Event::LONG_PRESS_EVENT:
148 OnLongPressEvent( *iter );
151 case Event::PAN_EVENT:
156 case Event::GRAB_HANDLE_EVENT:
157 case Event::LEFT_SELECTION_HANDLE_EVENT:
158 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
160 OnHandleEvent( *iter );
165 OnSelectEvent( *iter );
168 case Event::SELECT_ALL:
177 if( mEventData->mUpdateCursorPosition ||
178 mEventData->mUpdateHighlightBox )
183 // The cursor must also be repositioned after inserts into the model
184 if( mEventData->mUpdateCursorPosition )
186 // Updates the cursor position and scrolls the text to make it visible.
187 CursorInfo cursorInfo;
188 // Calculate the cursor position from the new cursor index.
189 GetCursorPosition( mEventData->mPrimaryCursorPosition,
192 if( mEventData->mUpdateCursorHookPosition )
194 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
195 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
196 mEventData->mUpdateCursorHookPosition = false;
199 // Scroll first the text after delete ...
200 if( mEventData->mScrollAfterDelete )
202 ScrollTextToMatchCursor( cursorInfo );
205 // ... then, text can be scrolled to make the cursor visible.
206 if( mEventData->mScrollAfterUpdatePosition )
208 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
209 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
211 mEventData->mScrollAfterUpdatePosition = false;
212 mEventData->mScrollAfterDelete = false;
214 UpdateCursorPosition( cursorInfo );
216 mEventData->mDecoratorUpdated = true;
217 mEventData->mUpdateCursorPosition = false;
218 mEventData->mUpdateGrabHandlePosition = false;
222 CursorInfo leftHandleInfo;
223 CursorInfo rightHandleInfo;
225 if( mEventData->mUpdateHighlightBox )
227 GetCursorPosition( mEventData->mLeftSelectionPosition,
230 GetCursorPosition( mEventData->mRightSelectionPosition,
233 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
235 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
237 CursorInfo& infoLeft = leftHandleInfo;
239 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
240 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
242 CursorInfo& infoRight = rightHandleInfo;
244 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
245 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
249 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
251 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
252 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
257 if( mEventData->mUpdateLeftSelectionPosition )
259 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
263 mEventData->mDecoratorUpdated = true;
264 mEventData->mUpdateLeftSelectionPosition = false;
267 if( mEventData->mUpdateRightSelectionPosition )
269 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
273 mEventData->mDecoratorUpdated = true;
274 mEventData->mUpdateRightSelectionPosition = false;
277 if( mEventData->mUpdateHighlightBox )
279 RepositionSelectionHandles();
281 mEventData->mUpdateLeftSelectionPosition = false;
282 mEventData->mUpdateRightSelectionPosition = false;
283 mEventData->mUpdateHighlightBox = false;
284 mEventData->mIsLeftHandleSelected = false;
285 mEventData->mIsRightHandleSelected = false;
288 mEventData->mScrollAfterUpdatePosition = false;
291 if( mEventData->mUpdateInputStyle )
293 // Keep a copy of the current input style.
294 InputStyle currentInputStyle;
295 currentInputStyle.Copy( mEventData->mInputStyle );
297 // Set the default style first.
298 RetrieveDefaultInputStyle( mEventData->mInputStyle );
300 // Get the character index from the cursor index.
301 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
303 // Retrieve the style from the style runs stored in the logical model.
304 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
306 // Compare if the input style has changed.
307 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
309 if( hasInputStyleChanged )
311 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
312 // Queue the input style changed signal.
313 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
316 mEventData->mUpdateInputStyle = false;
319 mEventData->mEventQueue.clear();
321 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
323 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
324 mEventData->mDecoratorUpdated = false;
326 return decoratorUpdated;
329 void Controller::Impl::NotifyImfManager()
331 if( mEventData && mEventData->mImfManager )
333 CharacterIndex cursorPosition = GetLogicalCursorPosition();
335 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
337 // Update the cursor position by removing the initial white spaces.
338 if( cursorPosition < numberOfWhiteSpaces )
344 cursorPosition -= numberOfWhiteSpaces;
347 mEventData->mImfManager.SetCursorPosition( cursorPosition );
348 mEventData->mImfManager.NotifyCursorPosition();
352 void Controller::Impl::NotifyImfMultiLineStatus()
356 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
357 mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
361 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
363 CharacterIndex cursorPosition = 0u;
367 if( ( EventData::SELECTING == mEventData->mState ) ||
368 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
370 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
374 cursorPosition = mEventData->mPrimaryCursorPosition;
378 return cursorPosition;
381 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
383 Length numberOfWhiteSpaces = 0u;
385 // Get the buffer to the text.
386 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
388 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
389 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
391 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
397 return numberOfWhiteSpaces;
400 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
402 // Get the total number of characters.
403 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
405 // Retrieve the text.
406 if( 0u != numberOfCharacters )
408 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
412 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
414 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
415 mTextUpdateInfo.mStartGlyphIndex = 0u;
416 mTextUpdateInfo.mStartLineIndex = 0u;
417 numberOfCharacters = 0u;
419 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
420 if( 0u == numberOfParagraphs )
422 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
423 numberOfCharacters = 0u;
425 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
427 // Nothing else to do if there are no paragraphs.
431 // Find the paragraphs to be updated.
432 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
433 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
435 // Text is being added at the end of the current text.
436 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
438 // Text is being added in a new paragraph after the last character of the text.
439 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
440 numberOfCharacters = 0u;
441 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
443 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
444 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
446 // Nothing else to do;
450 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
454 Length numberOfCharactersToUpdate = 0u;
455 if( mTextUpdateInfo.mFullRelayoutNeeded )
457 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
461 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
463 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
464 numberOfCharactersToUpdate,
465 paragraphsToBeUpdated );
468 if( 0u != paragraphsToBeUpdated.Count() )
470 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
471 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
472 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
474 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
475 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
477 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
478 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
479 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
480 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
482 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
483 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
485 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
489 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
493 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
494 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
497 void Controller::Impl::ClearFullModelData( OperationsMask operations )
499 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
501 mModel->mLogicalModel->mLineBreakInfo.Clear();
502 mModel->mLogicalModel->mParagraphInfo.Clear();
505 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
507 mModel->mLogicalModel->mLineBreakInfo.Clear();
510 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
512 mModel->mLogicalModel->mScriptRuns.Clear();
515 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
517 mModel->mLogicalModel->mFontRuns.Clear();
520 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
522 if( NO_OPERATION != ( BIDI_INFO & operations ) )
524 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
525 mModel->mLogicalModel->mCharacterDirections.Clear();
528 if( NO_OPERATION != ( REORDER & operations ) )
530 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
531 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
532 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
536 BidirectionalLineInfoRun& bidiLineInfo = *it;
538 free( bidiLineInfo.visualToLogicalMap );
539 bidiLineInfo.visualToLogicalMap = NULL;
541 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
545 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
547 mModel->mVisualModel->mGlyphs.Clear();
548 mModel->mVisualModel->mGlyphsToCharacters.Clear();
549 mModel->mVisualModel->mCharactersToGlyph.Clear();
550 mModel->mVisualModel->mCharactersPerGlyph.Clear();
551 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
552 mModel->mVisualModel->mGlyphPositions.Clear();
555 if( NO_OPERATION != ( LAYOUT & operations ) )
557 mModel->mVisualModel->mLines.Clear();
560 if( NO_OPERATION != ( COLOR & operations ) )
562 mModel->mVisualModel->mColorIndices.Clear();
566 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
568 const CharacterIndex endIndexPlusOne = endIndex + 1u;
570 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
572 // Clear the line break info.
573 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
575 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
576 lineBreakInfoBuffer + endIndexPlusOne );
578 // Clear the paragraphs.
579 ClearCharacterRuns( startIndex,
581 mModel->mLogicalModel->mParagraphInfo );
584 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
586 // Clear the word break info.
587 WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
589 mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
590 wordBreakInfoBuffer + endIndexPlusOne );
593 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
595 // Clear the scripts.
596 ClearCharacterRuns( startIndex,
598 mModel->mLogicalModel->mScriptRuns );
601 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
604 ClearCharacterRuns( startIndex,
606 mModel->mLogicalModel->mFontRuns );
609 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
611 if( NO_OPERATION != ( BIDI_INFO & operations ) )
613 // Clear the bidirectional paragraph info.
614 ClearCharacterRuns( startIndex,
616 mModel->mLogicalModel->mBidirectionalParagraphInfo );
618 // Clear the character's directions.
619 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
621 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
622 characterDirectionsBuffer + endIndexPlusOne );
625 if( NO_OPERATION != ( REORDER & operations ) )
627 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
628 uint32_t endRemoveIndex = startRemoveIndex;
629 ClearCharacterRuns( startIndex,
631 mModel->mLogicalModel->mBidirectionalLineInfo,
635 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
637 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
638 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
639 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
643 BidirectionalLineInfoRun& bidiLineInfo = *it;
645 free( bidiLineInfo.visualToLogicalMap );
646 bidiLineInfo.visualToLogicalMap = NULL;
649 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
650 bidirectionalLineInfoBuffer + endRemoveIndex );
655 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
657 const CharacterIndex endIndexPlusOne = endIndex + 1u;
658 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
660 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
661 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
662 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
664 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
665 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
667 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
669 // Update the character to glyph indices.
670 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
671 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
675 CharacterIndex& index = *it;
676 index -= numberOfGlyphsRemoved;
679 // Clear the character to glyph conversion table.
680 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
681 charactersToGlyphBuffer + endIndexPlusOne );
683 // Clear the glyphs per character table.
684 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
685 glyphsPerCharacterBuffer + endIndexPlusOne );
687 // Clear the glyphs buffer.
688 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
689 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
690 glyphsBuffer + endGlyphIndexPlusOne );
692 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
694 // Update the glyph to character indices.
695 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
696 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
700 CharacterIndex& index = *it;
701 index -= numberOfCharactersRemoved;
704 // Clear the glyphs to characters buffer.
705 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
706 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
708 // Clear the characters per glyph buffer.
709 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
710 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
711 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
713 // Clear the positions buffer.
714 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
715 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
716 positionsBuffer + endGlyphIndexPlusOne );
719 if( NO_OPERATION != ( LAYOUT & operations ) )
722 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
723 uint32_t endRemoveIndex = startRemoveIndex;
724 ClearCharacterRuns( startIndex,
726 mModel->mVisualModel->mLines,
730 // Will update the glyph runs.
731 startRemoveIndex = mModel->mVisualModel->mLines.Count();
732 endRemoveIndex = startRemoveIndex;
733 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
734 endGlyphIndexPlusOne - 1u,
735 mModel->mVisualModel->mLines,
739 // Set the line index from where to insert the new laid-out lines.
740 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
742 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
743 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
744 linesBuffer + endRemoveIndex );
747 if( NO_OPERATION != ( COLOR & operations ) )
749 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
751 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
752 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
753 colorIndexBuffer + endGlyphIndexPlusOne );
758 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
760 if( mTextUpdateInfo.mClearAll ||
761 ( ( 0u == startIndex ) &&
762 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
764 ClearFullModelData( operations );
768 // Clear the model data related with characters.
769 ClearCharacterModelData( startIndex, endIndex, operations );
771 // Clear the model data related with glyphs.
772 ClearGlyphModelData( startIndex, endIndex, operations );
775 // The estimated number of lines. Used to avoid reallocations when layouting.
776 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
778 mModel->mVisualModel->ClearCaches();
781 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
783 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
785 // Calculate the operations to be done.
786 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
788 if( NO_OPERATION == operations )
790 // Nothing to do if no operations are pending and required.
794 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
795 Vector<Character> displayCharacters;
796 bool useHiddenText = false;
797 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
799 mHiddenInput->Substitute( srcCharacters,displayCharacters );
800 useHiddenText = true;
803 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
804 const Length numberOfCharacters = utf32Characters.Count();
806 // Index to the first character of the first paragraph to be updated.
807 CharacterIndex startIndex = 0u;
808 // Number of characters of the paragraphs to be removed.
809 Length paragraphCharacters = 0u;
811 CalculateTextUpdateIndices( paragraphCharacters );
812 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
814 if( mTextUpdateInfo.mClearAll ||
815 ( 0u != paragraphCharacters ) )
817 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
820 mTextUpdateInfo.mClearAll = false;
822 // Whether the model is updated.
823 bool updated = false;
825 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
826 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
828 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
830 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
831 // calculate the bidirectional info for each 'paragraph'.
832 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
833 // is not shaped together).
834 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
836 SetLineBreakInfo( utf32Characters,
838 requestedNumberOfCharacters,
841 // Create the paragraph info.
842 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
843 requestedNumberOfCharacters );
847 Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
848 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
850 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
851 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
853 SetWordBreakInfo( utf32Characters,
855 requestedNumberOfCharacters,
860 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
861 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
863 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
864 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
866 if( getScripts || validateFonts )
868 // Validates the fonts assigned by the application or assigns default ones.
869 // It makes sure all the characters are going to be rendered by the correct font.
870 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
874 // Retrieves the scripts used in the text.
875 multilanguageSupport.SetScripts( utf32Characters,
877 requestedNumberOfCharacters,
883 // Validate the fonts set through the mark-up string.
884 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
886 // Get the default font's description.
887 TextAbstraction::FontDescription defaultFontDescription;
888 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
890 if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) )
892 // If the placeholder font is set specifically, only placeholder font is changed.
893 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
894 if( mEventData->mPlaceholderFont->sizeDefined )
896 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
899 else if( NULL != mFontDefaults )
901 // Set the normal font and the placeholder font.
902 defaultFontDescription = mFontDefaults->mFontDescription;
903 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
906 // Validates the fonts. If there is a character with no assigned font it sets a default one.
907 // After this call, fonts are validated.
908 multilanguageSupport.ValidateFonts( utf32Characters,
911 defaultFontDescription,
914 requestedNumberOfCharacters,
920 Vector<Character> mirroredUtf32Characters;
921 bool textMirrored = false;
922 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
923 if( NO_OPERATION != ( BIDI_INFO & operations ) )
925 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
926 bidirectionalInfo.Reserve( numberOfParagraphs );
928 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
929 SetBidirectionalInfo( utf32Characters,
933 requestedNumberOfCharacters,
936 if( 0u != bidirectionalInfo.Count() )
938 // Only set the character directions if there is right to left characters.
939 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
940 GetCharactersDirection( bidirectionalInfo,
943 requestedNumberOfCharacters,
946 // This paragraph has right to left text. Some characters may need to be mirrored.
947 // TODO: consider if the mirrored string can be stored as well.
949 textMirrored = GetMirroredText( utf32Characters,
953 requestedNumberOfCharacters,
954 mirroredUtf32Characters );
958 // There is no right to left characters. Clear the directions vector.
959 mModel->mLogicalModel->mCharacterDirections.Clear();
964 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
965 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
966 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
967 Vector<GlyphIndex> newParagraphGlyphs;
968 newParagraphGlyphs.Reserve( numberOfParagraphs );
970 const Length currentNumberOfGlyphs = glyphs.Count();
971 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
973 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
975 ShapeText( textToShape,
980 mTextUpdateInfo.mStartGlyphIndex,
981 requestedNumberOfCharacters,
983 glyphsToCharactersMap,
985 newParagraphGlyphs );
987 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
988 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
989 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
993 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
995 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
997 GlyphInfo* glyphsBuffer = glyphs.Begin();
998 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1000 // Update the width and advance of all new paragraph characters.
1001 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1003 const GlyphIndex index = *it;
1004 GlyphInfo& glyph = *( glyphsBuffer + index );
1006 glyph.xBearing = 0.f;
1008 glyph.advance = 0.f;
1013 if( NO_OPERATION != ( COLOR & operations ) )
1015 // Set the color runs in glyphs.
1016 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1017 mModel->mVisualModel->mCharactersToGlyph,
1018 mModel->mVisualModel->mGlyphsPerCharacter,
1020 mTextUpdateInfo.mStartGlyphIndex,
1021 requestedNumberOfCharacters,
1022 mModel->mVisualModel->mColors,
1023 mModel->mVisualModel->mColorIndices );
1028 if( ( NULL != mEventData ) &&
1029 mEventData->mPreEditFlag &&
1030 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1032 // Add the underline for the pre-edit text.
1033 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1034 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1036 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1037 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1038 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1039 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1041 GlyphRun underlineRun;
1042 underlineRun.glyphIndex = glyphStart;
1043 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1045 // TODO: At the moment the underline runs are only for pre-edit.
1046 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1049 // The estimated number of lines. Used to avoid reallocations when layouting.
1050 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1052 // Set the previous number of characters for the next time the text is updated.
1053 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1058 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1060 // Sets the default text's color.
1061 inputStyle.textColor = mTextColor;
1062 inputStyle.isDefaultColor = true;
1064 inputStyle.familyName.clear();
1065 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1066 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1067 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1068 inputStyle.size = 0.f;
1070 inputStyle.lineSpacing = 0.f;
1072 inputStyle.underlineProperties.clear();
1073 inputStyle.shadowProperties.clear();
1074 inputStyle.embossProperties.clear();
1075 inputStyle.outlineProperties.clear();
1077 inputStyle.isFamilyDefined = false;
1078 inputStyle.isWeightDefined = false;
1079 inputStyle.isWidthDefined = false;
1080 inputStyle.isSlantDefined = false;
1081 inputStyle.isSizeDefined = false;
1083 inputStyle.isLineSpacingDefined = false;
1085 inputStyle.isUnderlineDefined = false;
1086 inputStyle.isShadowDefined = false;
1087 inputStyle.isEmbossDefined = false;
1088 inputStyle.isOutlineDefined = false;
1090 // Sets the default font's family name, weight, width, slant and size.
1093 if( mFontDefaults->familyDefined )
1095 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1096 inputStyle.isFamilyDefined = true;
1099 if( mFontDefaults->weightDefined )
1101 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1102 inputStyle.isWeightDefined = true;
1105 if( mFontDefaults->widthDefined )
1107 inputStyle.width = mFontDefaults->mFontDescription.width;
1108 inputStyle.isWidthDefined = true;
1111 if( mFontDefaults->slantDefined )
1113 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1114 inputStyle.isSlantDefined = true;
1117 if( mFontDefaults->sizeDefined )
1119 inputStyle.size = mFontDefaults->mDefaultPointSize;
1120 inputStyle.isSizeDefined = true;
1125 float Controller::Impl::GetDefaultFontLineHeight()
1127 FontId defaultFontId = 0u;
1128 if( NULL == mFontDefaults )
1130 TextAbstraction::FontDescription fontDescription;
1131 defaultFontId = mFontClient.GetFontId( fontDescription );
1135 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1138 Text::FontMetrics fontMetrics;
1139 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1141 return( fontMetrics.ascender - fontMetrics.descender );
1144 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1146 if( NULL == mEventData )
1148 // Nothing to do if there is no text input.
1152 int keyCode = event.p1.mInt;
1154 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1156 if( mEventData->mPrimaryCursorPosition > 0u )
1158 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1161 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1163 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1165 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1168 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1170 // Get first the line index of the current cursor position index.
1171 CharacterIndex characterIndex = 0u;
1173 if( mEventData->mPrimaryCursorPosition > 0u )
1175 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1178 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1179 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1181 // Retrieve the cursor position info.
1182 CursorInfo cursorInfo;
1183 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1186 // Get the line above.
1187 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1189 // Get the next hit 'y' point.
1190 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1192 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1193 bool matchedCharacter = false;
1194 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1195 mModel->mLogicalModel,
1197 mEventData->mCursorHookPositionX,
1199 CharacterHitTest::TAP,
1202 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1204 // Get first the line index of the current cursor position index.
1205 CharacterIndex characterIndex = 0u;
1207 if( mEventData->mPrimaryCursorPosition > 0u )
1209 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1212 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1214 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1216 // Retrieve the cursor position info.
1217 CursorInfo cursorInfo;
1218 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1221 // Get the line below.
1222 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1224 // Get the next hit 'y' point.
1225 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1227 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1228 bool matchedCharacter = false;
1229 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1230 mModel->mLogicalModel,
1232 mEventData->mCursorHookPositionX,
1234 CharacterHitTest::TAP,
1239 mEventData->mUpdateCursorPosition = true;
1240 mEventData->mUpdateInputStyle = true;
1241 mEventData->mScrollAfterUpdatePosition = true;
1244 void Controller::Impl::OnTapEvent( const Event& event )
1246 if( NULL != mEventData )
1248 const unsigned int tapCount = event.p1.mUint;
1250 if( 1u == tapCount )
1252 if( IsShowingRealText() )
1254 // Convert from control's coords to text's coords.
1255 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1256 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1258 // Keep the tap 'x' position. Used to move the cursor.
1259 mEventData->mCursorHookPositionX = xPosition;
1261 // Whether to touch point hits on a glyph.
1262 bool matchedCharacter = false;
1263 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1264 mModel->mLogicalModel,
1268 CharacterHitTest::TAP,
1271 // When the cursor position is changing, delay cursor blinking
1272 mEventData->mDecorator->DelayCursorBlink();
1276 mEventData->mPrimaryCursorPosition = 0u;
1279 mEventData->mUpdateCursorPosition = true;
1280 mEventData->mUpdateGrabHandlePosition = true;
1281 mEventData->mScrollAfterUpdatePosition = true;
1282 mEventData->mUpdateInputStyle = true;
1284 // Notify the cursor position to the imf manager.
1285 if( mEventData->mImfManager )
1287 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1288 mEventData->mImfManager.NotifyCursorPosition();
1291 else if( 2u == tapCount )
1293 if( mEventData->mSelectionEnabled )
1295 // Convert from control's coords to text's coords.
1296 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1297 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1299 // Calculates the logical position from the x,y coords.
1300 RepositionSelectionHandles( xPosition,
1302 mEventData->mDoubleTapAction );
1308 void Controller::Impl::OnPanEvent( const Event& event )
1310 if( NULL == mEventData )
1312 // Nothing to do if there is no text input.
1316 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1317 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1319 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1321 // Nothing to do if scrolling is not enabled.
1325 const int state = event.p1.mInt;
1329 case Gesture::Started:
1331 // Will remove the cursor, handles or text's popup, ...
1332 ChangeState( EventData::TEXT_PANNING );
1335 case Gesture::Continuing:
1337 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1338 const Vector2 currentScroll = mModel->mScrollPosition;
1340 if( isHorizontalScrollEnabled )
1342 const float displacementX = event.p2.mFloat;
1343 mModel->mScrollPosition.x += displacementX;
1345 ClampHorizontalScroll( layoutSize );
1348 if( isVerticalScrollEnabled )
1350 const float displacementY = event.p3.mFloat;
1351 mModel->mScrollPosition.y += displacementY;
1353 ClampVerticalScroll( layoutSize );
1356 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1359 case Gesture::Finished:
1360 case Gesture::Cancelled: // FALLTHROUGH
1362 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1363 ChangeState( mEventData->mPreviousState );
1371 void Controller::Impl::OnLongPressEvent( const Event& event )
1373 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1375 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1377 ChangeState( EventData::EDITING_WITH_POPUP );
1378 mEventData->mDecoratorUpdated = true;
1379 mEventData->mUpdateInputStyle = true;
1383 if( mEventData->mSelectionEnabled )
1385 // Convert from control's coords to text's coords.
1386 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1387 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1389 // Calculates the logical position from the x,y coords.
1390 RepositionSelectionHandles( xPosition,
1392 mEventData->mLongPressAction );
1397 void Controller::Impl::OnHandleEvent( const Event& event )
1399 if( NULL == mEventData )
1401 // Nothing to do if there is no text input.
1405 const unsigned int state = event.p1.mUint;
1406 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1407 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1409 if( HANDLE_PRESSED == state )
1411 // Convert from decorator's coords to text's coords.
1412 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1413 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1415 // Need to calculate the handle's new position.
1416 bool matchedCharacter = false;
1417 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1418 mModel->mLogicalModel,
1422 CharacterHitTest::SCROLL,
1425 if( Event::GRAB_HANDLE_EVENT == event.type )
1427 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1429 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1431 // Updates the cursor position if the handle's new position is different than the current one.
1432 mEventData->mUpdateCursorPosition = true;
1433 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1434 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1435 mEventData->mPrimaryCursorPosition = handleNewPosition;
1438 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1439 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1441 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1443 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1445 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1446 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1448 // Updates the highlight box if the handle's new position is different than the current one.
1449 mEventData->mUpdateHighlightBox = true;
1450 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1451 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1452 mEventData->mLeftSelectionPosition = handleNewPosition;
1455 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1456 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1458 // Will define the order to scroll the text to match the handle position.
1459 mEventData->mIsLeftHandleSelected = true;
1460 mEventData->mIsRightHandleSelected = false;
1462 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1464 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1466 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1467 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1469 // Updates the highlight box if the handle's new position is different than the current one.
1470 mEventData->mUpdateHighlightBox = true;
1471 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1472 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1473 mEventData->mRightSelectionPosition = handleNewPosition;
1476 // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1477 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1479 // Will define the order to scroll the text to match the handle position.
1480 mEventData->mIsLeftHandleSelected = false;
1481 mEventData->mIsRightHandleSelected = true;
1483 } // end ( HANDLE_PRESSED == state )
1484 else if( ( HANDLE_RELEASED == state ) ||
1485 handleStopScrolling )
1487 CharacterIndex handlePosition = 0u;
1488 if( handleStopScrolling || isSmoothHandlePanEnabled )
1490 // Convert from decorator's coords to text's coords.
1491 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1492 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1494 bool matchedCharacter = false;
1495 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1496 mModel->mLogicalModel,
1500 CharacterHitTest::SCROLL,
1504 if( Event::GRAB_HANDLE_EVENT == event.type )
1506 mEventData->mUpdateCursorPosition = true;
1507 mEventData->mUpdateGrabHandlePosition = true;
1508 mEventData->mUpdateInputStyle = true;
1510 if( !IsClipboardEmpty() )
1512 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1515 if( handleStopScrolling || isSmoothHandlePanEnabled )
1517 mEventData->mScrollAfterUpdatePosition = true;
1518 mEventData->mPrimaryCursorPosition = handlePosition;
1521 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1523 ChangeState( EventData::SELECTING );
1525 mEventData->mUpdateHighlightBox = true;
1526 mEventData->mUpdateLeftSelectionPosition = true;
1527 mEventData->mUpdateRightSelectionPosition = true;
1529 if( handleStopScrolling || isSmoothHandlePanEnabled )
1531 mEventData->mScrollAfterUpdatePosition = true;
1533 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1534 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1536 mEventData->mLeftSelectionPosition = handlePosition;
1540 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1542 ChangeState( EventData::SELECTING );
1544 mEventData->mUpdateHighlightBox = true;
1545 mEventData->mUpdateRightSelectionPosition = true;
1546 mEventData->mUpdateLeftSelectionPosition = true;
1548 if( handleStopScrolling || isSmoothHandlePanEnabled )
1550 mEventData->mScrollAfterUpdatePosition = true;
1551 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1552 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1554 mEventData->mRightSelectionPosition = handlePosition;
1559 mEventData->mDecoratorUpdated = true;
1560 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1561 else if( HANDLE_SCROLLING == state )
1563 const float xSpeed = event.p2.mFloat;
1564 const float ySpeed = event.p3.mFloat;
1565 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1566 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1568 mModel->mScrollPosition.x += xSpeed;
1569 mModel->mScrollPosition.y += ySpeed;
1571 ClampHorizontalScroll( layoutSize );
1572 ClampVerticalScroll( layoutSize );
1574 bool endOfScroll = false;
1575 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1577 // Notify the decorator there is no more text to scroll.
1578 // The decorator won't send more scroll events.
1579 mEventData->mDecorator->NotifyEndOfScroll();
1580 // Still need to set the position of the handle.
1584 // Set the position of the handle.
1585 const bool scrollRightDirection = xSpeed > 0.f;
1586 const bool scrollBottomDirection = ySpeed > 0.f;
1587 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1588 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1590 if( Event::GRAB_HANDLE_EVENT == event.type )
1592 ChangeState( EventData::GRAB_HANDLE_PANNING );
1594 // Get the grab handle position in decorator coords.
1595 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1597 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1599 // Position the grag handle close to either the left or right edge.
1600 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1603 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1605 position.x = mEventData->mCursorHookPositionX;
1607 // Position the grag handle close to either the top or bottom edge.
1608 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1611 // Get the new handle position.
1612 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1613 bool matchedCharacter = false;
1614 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1615 mModel->mLogicalModel,
1617 position.x - mModel->mScrollPosition.x,
1618 position.y - mModel->mScrollPosition.y,
1619 CharacterHitTest::SCROLL,
1622 if( mEventData->mPrimaryCursorPosition != handlePosition )
1624 mEventData->mUpdateCursorPosition = true;
1625 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1626 mEventData->mScrollAfterUpdatePosition = true;
1627 mEventData->mPrimaryCursorPosition = handlePosition;
1629 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1631 // Updates the decorator if the soft handle panning is enabled.
1632 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1634 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1636 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1638 // Get the selection handle position in decorator coords.
1639 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1641 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1643 // Position the selection handle close to either the left or right edge.
1644 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1647 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1649 position.x = mEventData->mCursorHookPositionX;
1651 // Position the grag handle close to either the top or bottom edge.
1652 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1655 // Get the new handle position.
1656 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1657 bool matchedCharacter = false;
1658 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1659 mModel->mLogicalModel,
1661 position.x - mModel->mScrollPosition.x,
1662 position.y - mModel->mScrollPosition.y,
1663 CharacterHitTest::SCROLL,
1666 if( leftSelectionHandleEvent )
1668 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1670 if( differentHandles || endOfScroll )
1672 mEventData->mUpdateHighlightBox = true;
1673 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1674 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1675 mEventData->mLeftSelectionPosition = handlePosition;
1680 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1681 if( differentHandles || endOfScroll )
1683 mEventData->mUpdateHighlightBox = true;
1684 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1685 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1686 mEventData->mRightSelectionPosition = handlePosition;
1690 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1692 RepositionSelectionHandles();
1694 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1697 mEventData->mDecoratorUpdated = true;
1698 } // end ( HANDLE_SCROLLING == state )
1701 void Controller::Impl::OnSelectEvent( const Event& event )
1703 if( NULL == mEventData )
1705 // Nothing to do if there is no text.
1709 if( mEventData->mSelectionEnabled )
1711 // Convert from control's coords to text's coords.
1712 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1713 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1715 // Calculates the logical position from the x,y coords.
1716 RepositionSelectionHandles( xPosition,
1718 Controller::NoTextTap::HIGHLIGHT );
1722 void Controller::Impl::OnSelectAllEvent()
1724 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1726 if( NULL == mEventData )
1728 // Nothing to do if there is no text.
1732 if( mEventData->mSelectionEnabled )
1734 ChangeState( EventData::SELECTING );
1736 mEventData->mLeftSelectionPosition = 0u;
1737 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1739 mEventData->mScrollAfterUpdatePosition = true;
1740 mEventData->mUpdateLeftSelectionPosition = true;
1741 mEventData->mUpdateRightSelectionPosition = true;
1742 mEventData->mUpdateHighlightBox = true;
1746 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1748 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1750 // Nothing to select if handles are in the same place.
1751 selectedText.clear();
1755 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1757 //Get start and end position of selection
1758 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1759 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1761 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1762 const Length numberOfCharacters = utf32Characters.Count();
1764 // Validate the start and end selection points
1765 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1767 //Get text as a UTF8 string
1768 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1770 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1772 // Keep a copy of the current input style.
1773 InputStyle currentInputStyle;
1774 currentInputStyle.Copy( mEventData->mInputStyle );
1776 // Set as input style the style of the first deleted character.
1777 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1779 // Compare if the input style has changed.
1780 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1782 if( hasInputStyleChanged )
1784 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1785 // Queue the input style changed signal.
1786 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1789 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1791 // Mark the paragraphs to be updated.
1792 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
1794 mTextUpdateInfo.mCharacterIndex = 0;
1795 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1796 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1797 mTextUpdateInfo.mClearAll = true;
1801 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1802 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1805 // Delete text between handles
1806 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1807 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1808 utf32Characters.Erase( first, last );
1810 // Will show the cursor at the first character of the selection.
1811 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1815 // Will show the cursor at the last character of the selection.
1816 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1819 mEventData->mDecoratorUpdated = true;
1823 void Controller::Impl::ShowClipboard()
1827 mClipboard.ShowClipboard();
1831 void Controller::Impl::HideClipboard()
1833 if( mClipboard && mClipboardHideEnabled )
1835 mClipboard.HideClipboard();
1839 void Controller::Impl::SetClipboardHideEnable(bool enable)
1841 mClipboardHideEnabled = enable;
1844 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1846 //Send string to clipboard
1847 return ( mClipboard && mClipboard.SetItem( source ) );
1850 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1852 std::string selectedText;
1853 RetrieveSelection( selectedText, deleteAfterSending );
1854 CopyStringToClipboard( selectedText );
1855 ChangeState( EventData::EDITING );
1858 void Controller::Impl::RequestGetTextFromClipboard()
1862 mClipboard.RequestItem();
1866 void Controller::Impl::RepositionSelectionHandles()
1868 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1869 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1871 if( selectionStart == selectionEnd )
1873 // Nothing to select if handles are in the same place.
1877 mEventData->mDecorator->ClearHighlights();
1879 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1880 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1881 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1882 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1883 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1884 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1885 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1887 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1888 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1889 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1891 // Swap the indices if the start is greater than the end.
1892 const bool indicesSwapped = selectionStart > selectionEnd;
1894 // Tell the decorator to flip the selection handles if needed.
1895 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1897 if( indicesSwapped )
1899 std::swap( selectionStart, selectionEnd );
1902 // Get the indices to the first and last selected glyphs.
1903 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1904 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1905 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1906 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1908 // Get the lines where the glyphs are laid-out.
1909 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1911 LineIndex lineIndex = 0u;
1912 Length numberOfLines = 0u;
1913 mModel->mVisualModel->GetNumberOfLines( glyphStart,
1914 1u + glyphEnd - glyphStart,
1917 const LineIndex firstLineIndex = lineIndex;
1919 // Create the structure to store some selection box info.
1920 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1921 selectionBoxLinesInfo.Resize( numberOfLines );
1923 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1924 selectionBoxInfo->minX = MAX_FLOAT;
1925 selectionBoxInfo->maxX = MIN_FLOAT;
1927 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1928 float minHighlightX = std::numeric_limits<float>::max();
1929 float maxHighlightX = std::numeric_limits<float>::min();
1931 Vector2 highLightPosition; // The highlight position in decorator's coords.
1933 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1935 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1936 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1939 // Transform to decorator's (control) coords.
1940 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1942 lineRun += firstLineIndex;
1944 // The line height is the addition of the line ascender and the line descender.
1945 // However, the line descender has a negative value, hence the subtraction.
1946 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1948 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1950 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1951 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1952 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1954 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1955 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1956 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1958 // The number of quads of the selection box.
1959 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1960 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1962 // Count the actual number of quads.
1963 unsigned int actualNumberOfQuads = 0u;
1966 // Traverse the glyphs.
1967 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1969 const GlyphInfo& glyph = *( glyphsBuffer + index );
1970 const Vector2& position = *( positionsBuffer + index );
1972 if( splitStartGlyph )
1974 // 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.
1976 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1977 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1978 // Get the direction of the character.
1979 CharacterDirection isCurrentRightToLeft = false;
1980 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1982 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1985 // The end point could be in the middle of the ligature.
1986 // Calculate the number of characters selected.
1987 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1989 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1990 quad.y = selectionBoxInfo->lineOffset;
1991 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1992 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1994 // Store the min and max 'x' for each line.
1995 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1996 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1998 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1999 ++actualNumberOfQuads;
2001 splitStartGlyph = false;
2005 if( splitEndGlyph && ( index == glyphEnd ) )
2007 // 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.
2009 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2010 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2011 // Get the direction of the character.
2012 CharacterDirection isCurrentRightToLeft = false;
2013 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2015 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2018 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2020 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2021 quad.y = selectionBoxInfo->lineOffset;
2022 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2023 quad.w = quad.y + selectionBoxInfo->lineHeight;
2025 // Store the min and max 'x' for each line.
2026 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2027 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2029 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2031 ++actualNumberOfQuads;
2033 splitEndGlyph = false;
2037 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2038 quad.y = selectionBoxInfo->lineOffset;
2039 quad.z = quad.x + glyph.advance;
2040 quad.w = quad.y + selectionBoxInfo->lineHeight;
2042 // Store the min and max 'x' for each line.
2043 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2044 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2046 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2048 ++actualNumberOfQuads;
2050 // Whether to retrieve the next line.
2051 if( index == lastGlyphOfLine )
2054 if( lineIndex < firstLineIndex + numberOfLines )
2056 // Retrieve the next line.
2059 // Get the last glyph of the new line.
2060 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2062 // Keep the offset and height of the current selection box.
2063 const float currentLineOffset = selectionBoxInfo->lineOffset;
2064 const float currentLineHeight = selectionBoxInfo->lineHeight;
2066 // Get the selection box info for the next line.
2069 selectionBoxInfo->minX = MAX_FLOAT;
2070 selectionBoxInfo->maxX = MIN_FLOAT;
2072 // Update the line's vertical offset.
2073 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2075 // The line height is the addition of the line ascender and the line descender.
2076 // However, the line descender has a negative value, hence the subtraction.
2077 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2082 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2083 // The final width is calculated after 'boxifying' the selection.
2084 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2085 endIt = selectionBoxLinesInfo.End();
2089 const SelectionBoxInfo& info = *it;
2091 // Update the size of the highlighted text.
2092 highLightSize.height += info.lineHeight;
2093 minHighlightX = std::min( minHighlightX, info.minX );
2094 maxHighlightX = std::max( maxHighlightX, info.maxX );
2097 // Add extra geometry to 'boxify' the selection.
2099 if( 1u < numberOfLines )
2101 // Boxify the first line.
2102 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2103 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2105 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2106 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2111 quad.y = firstSelectionBoxLineInfo.lineOffset;
2112 quad.z = firstSelectionBoxLineInfo.minX;
2113 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2115 // Boxify at the beginning of the line.
2116 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2118 ++actualNumberOfQuads;
2120 // Update the size of the highlighted text.
2121 minHighlightX = 0.f;
2126 quad.x = firstSelectionBoxLineInfo.maxX;
2127 quad.y = firstSelectionBoxLineInfo.lineOffset;
2128 quad.z = mModel->mVisualModel->mControlSize.width;
2129 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2131 // Boxify at the end of the line.
2132 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2134 ++actualNumberOfQuads;
2136 // Update the size of the highlighted text.
2137 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2140 // Boxify the central lines.
2141 if( 2u < numberOfLines )
2143 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2144 endIt = selectionBoxLinesInfo.End() - 1u;
2148 const SelectionBoxInfo& info = *it;
2151 quad.y = info.lineOffset;
2153 quad.w = info.lineOffset + info.lineHeight;
2155 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2157 ++actualNumberOfQuads;
2160 quad.y = info.lineOffset;
2161 quad.z = mModel->mVisualModel->mControlSize.width;
2162 quad.w = info.lineOffset + info.lineHeight;
2164 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2166 ++actualNumberOfQuads;
2169 // Update the size of the highlighted text.
2170 minHighlightX = 0.f;
2171 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2174 // Boxify the last line.
2175 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2176 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2178 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2179 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2184 quad.y = lastSelectionBoxLineInfo.lineOffset;
2185 quad.z = lastSelectionBoxLineInfo.minX;
2186 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2188 // Boxify at the beginning of the line.
2189 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2191 ++actualNumberOfQuads;
2193 // Update the size of the highlighted text.
2194 minHighlightX = 0.f;
2199 quad.x = lastSelectionBoxLineInfo.maxX;
2200 quad.y = lastSelectionBoxLineInfo.lineOffset;
2201 quad.z = mModel->mVisualModel->mControlSize.width;
2202 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2204 // Boxify at the end of the line.
2205 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2207 ++actualNumberOfQuads;
2209 // Update the size of the highlighted text.
2210 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2214 // Set the actual number of quads.
2215 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2217 // Sets the highlight's size and position. In decorator's coords.
2218 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2219 highLightSize.width = maxHighlightX - minHighlightX;
2221 highLightPosition.x = minHighlightX;
2222 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2223 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2225 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2227 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2229 CursorInfo primaryCursorInfo;
2230 GetCursorPosition( mEventData->mLeftSelectionPosition,
2231 primaryCursorInfo );
2233 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2235 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2237 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2238 primaryCursorInfo.lineHeight );
2240 CursorInfo secondaryCursorInfo;
2241 GetCursorPosition( mEventData->mRightSelectionPosition,
2242 secondaryCursorInfo );
2244 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2246 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2247 secondaryPosition.x,
2248 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2249 secondaryCursorInfo.lineHeight );
2252 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2253 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2255 // Set the flag to update the decorator.
2256 mEventData->mDecoratorUpdated = true;
2259 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2261 if( NULL == mEventData )
2263 // Nothing to do if there is no text input.
2267 if( IsShowingPlaceholderText() )
2269 // Nothing to do if there is the place-holder text.
2273 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2274 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2275 if( ( 0 == numberOfGlyphs ) ||
2276 ( 0 == numberOfLines ) )
2278 // Nothing to do if there is no text.
2282 // Find which word was selected
2283 CharacterIndex selectionStart( 0 );
2284 CharacterIndex selectionEnd( 0 );
2285 CharacterIndex noTextHitIndex( 0 );
2286 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2287 mModel->mLogicalModel,
2294 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2296 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2298 ChangeState( EventData::SELECTING );
2300 mEventData->mLeftSelectionPosition = selectionStart;
2301 mEventData->mRightSelectionPosition = selectionEnd;
2303 mEventData->mUpdateLeftSelectionPosition = true;
2304 mEventData->mUpdateRightSelectionPosition = true;
2305 mEventData->mUpdateHighlightBox = true;
2307 // It may happen an IMF commit event arrives before the selection event
2308 // if the IMF manager is in pre-edit state. The commit event will set the
2309 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2310 // to false, the highlight box won't be updated.
2311 mEventData->mUpdateCursorPosition = false;
2313 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2315 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2317 // Nothing to select. i.e. a white space, out of bounds
2318 ChangeState( EventData::EDITING_WITH_POPUP );
2320 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2322 mEventData->mUpdateCursorPosition = true;
2323 mEventData->mUpdateGrabHandlePosition = true;
2324 mEventData->mScrollAfterUpdatePosition = true;
2325 mEventData->mUpdateInputStyle = true;
2327 else if( Controller::NoTextTap::NO_ACTION == action )
2329 // Nothing to select. i.e. a white space, out of bounds
2330 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2332 mEventData->mUpdateCursorPosition = true;
2333 mEventData->mUpdateGrabHandlePosition = true;
2334 mEventData->mScrollAfterUpdatePosition = true;
2335 mEventData->mUpdateInputStyle = true;
2339 void Controller::Impl::SetPopupButtons()
2342 * Sets the Popup buttons to be shown depending on State.
2344 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2346 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2349 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2351 if( EventData::SELECTING == mEventData->mState )
2353 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2355 if( !IsClipboardEmpty() )
2357 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2358 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2361 if( !mEventData->mAllTextSelected )
2363 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2366 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2368 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2370 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2373 if( !IsClipboardEmpty() )
2375 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2376 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2379 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2381 if ( !IsClipboardEmpty() )
2383 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2384 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2388 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2391 void Controller::Impl::ChangeState( EventData::State newState )
2393 if( NULL == mEventData )
2395 // Nothing to do if there is no text input.
2399 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2401 if( mEventData->mState != newState )
2403 mEventData->mPreviousState = mEventData->mState;
2404 mEventData->mState = newState;
2406 switch( mEventData->mState )
2408 case EventData::INACTIVE:
2410 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2411 mEventData->mDecorator->StopCursorBlink();
2412 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2413 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2414 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2415 mEventData->mDecorator->SetHighlightActive( false );
2416 mEventData->mDecorator->SetPopupActive( false );
2417 mEventData->mDecoratorUpdated = true;
2420 case EventData::INTERRUPTED:
2422 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2423 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2424 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2425 mEventData->mDecorator->SetHighlightActive( false );
2426 mEventData->mDecorator->SetPopupActive( false );
2427 mEventData->mDecoratorUpdated = true;
2430 case EventData::SELECTING:
2432 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2433 mEventData->mDecorator->StopCursorBlink();
2434 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2435 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2436 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2437 mEventData->mDecorator->SetHighlightActive( true );
2438 if( mEventData->mGrabHandlePopupEnabled )
2441 mEventData->mDecorator->SetPopupActive( true );
2443 mEventData->mDecoratorUpdated = true;
2446 case EventData::EDITING:
2448 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2449 if( mEventData->mCursorBlinkEnabled )
2451 mEventData->mDecorator->StartCursorBlink();
2453 // Grab handle is not shown until a tap is received whilst EDITING
2454 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2455 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2456 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2457 mEventData->mDecorator->SetHighlightActive( false );
2458 if( mEventData->mGrabHandlePopupEnabled )
2460 mEventData->mDecorator->SetPopupActive( false );
2462 mEventData->mDecoratorUpdated = true;
2465 case EventData::EDITING_WITH_POPUP:
2467 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2469 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2470 if( mEventData->mCursorBlinkEnabled )
2472 mEventData->mDecorator->StartCursorBlink();
2474 if( mEventData->mSelectionEnabled )
2476 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2477 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2478 mEventData->mDecorator->SetHighlightActive( false );
2482 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2484 if( mEventData->mGrabHandlePopupEnabled )
2487 mEventData->mDecorator->SetPopupActive( true );
2489 mEventData->mDecoratorUpdated = true;
2492 case EventData::EDITING_WITH_GRAB_HANDLE:
2494 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2496 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2497 if( mEventData->mCursorBlinkEnabled )
2499 mEventData->mDecorator->StartCursorBlink();
2501 // Grab handle is not shown until a tap is received whilst EDITING
2502 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2503 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2504 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2505 mEventData->mDecorator->SetHighlightActive( false );
2506 if( mEventData->mGrabHandlePopupEnabled )
2508 mEventData->mDecorator->SetPopupActive( false );
2510 mEventData->mDecoratorUpdated = true;
2513 case EventData::SELECTION_HANDLE_PANNING:
2515 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2516 mEventData->mDecorator->StopCursorBlink();
2517 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2518 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2519 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2520 mEventData->mDecorator->SetHighlightActive( true );
2521 if( mEventData->mGrabHandlePopupEnabled )
2523 mEventData->mDecorator->SetPopupActive( false );
2525 mEventData->mDecoratorUpdated = true;
2528 case EventData::GRAB_HANDLE_PANNING:
2530 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2532 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2533 if( mEventData->mCursorBlinkEnabled )
2535 mEventData->mDecorator->StartCursorBlink();
2537 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2538 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2539 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2540 mEventData->mDecorator->SetHighlightActive( false );
2541 if( mEventData->mGrabHandlePopupEnabled )
2543 mEventData->mDecorator->SetPopupActive( false );
2545 mEventData->mDecoratorUpdated = true;
2548 case EventData::EDITING_WITH_PASTE_POPUP:
2550 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2552 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2553 if( mEventData->mCursorBlinkEnabled )
2555 mEventData->mDecorator->StartCursorBlink();
2558 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2559 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2560 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2561 mEventData->mDecorator->SetHighlightActive( false );
2563 if( mEventData->mGrabHandlePopupEnabled )
2566 mEventData->mDecorator->SetPopupActive( true );
2568 mEventData->mDecoratorUpdated = true;
2571 case EventData::TEXT_PANNING:
2573 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2574 mEventData->mDecorator->StopCursorBlink();
2575 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2576 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2577 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2579 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2580 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2581 mEventData->mDecorator->SetHighlightActive( true );
2584 if( mEventData->mGrabHandlePopupEnabled )
2586 mEventData->mDecorator->SetPopupActive( false );
2589 mEventData->mDecoratorUpdated = true;
2596 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2597 CursorInfo& cursorInfo )
2599 if( !IsShowingRealText() )
2601 // Do not want to use the place-holder text to set the cursor position.
2603 // Use the line's height of the font's family set to set the cursor's size.
2604 // If there is no font's family set, use the default font.
2605 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2607 cursorInfo.lineOffset = 0.f;
2608 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2609 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2611 switch( mModel->mHorizontalAlignment )
2613 case Layout::HORIZONTAL_ALIGN_BEGIN:
2615 cursorInfo.primaryPosition.x = 0.f;
2618 case Layout::HORIZONTAL_ALIGN_CENTER:
2620 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2623 case Layout::HORIZONTAL_ALIGN_END:
2625 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2630 // Nothing else to do.
2634 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2635 GetCursorPositionParameters parameters;
2636 parameters.visualModel = mModel->mVisualModel;
2637 parameters.logicalModel = mModel->mLogicalModel;
2638 parameters.metrics = mMetrics;
2639 parameters.logical = logical;
2640 parameters.isMultiline = isMultiLine;
2642 Text::GetCursorPosition( parameters,
2647 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2649 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2650 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2652 if( 0.f > cursorInfo.primaryPosition.x )
2654 cursorInfo.primaryPosition.x = 0.f;
2657 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2658 if( cursorInfo.primaryPosition.x > edgeWidth )
2660 cursorInfo.primaryPosition.x = edgeWidth;
2665 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2667 if( NULL == mEventData )
2669 // Nothing to do if there is no text input.
2673 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2675 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2676 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2678 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2679 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2681 if( numberOfCharacters > 1u )
2683 const Script script = mModel->mLogicalModel->GetScript( index );
2684 if( HasLigatureMustBreak( script ) )
2686 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2687 numberOfCharacters = 1u;
2692 while( 0u == numberOfCharacters )
2695 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2699 if( index < mEventData->mPrimaryCursorPosition )
2701 cursorIndex -= numberOfCharacters;
2705 cursorIndex += numberOfCharacters;
2708 // Will update the cursor hook position.
2709 mEventData->mUpdateCursorHookPosition = true;
2714 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2716 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2717 if( NULL == mEventData )
2719 // Nothing to do if there is no text input.
2720 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2724 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2726 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
2728 // Sets the cursor position.
2729 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2732 cursorInfo.primaryCursorHeight,
2733 cursorInfo.lineHeight );
2734 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2736 if( mEventData->mUpdateGrabHandlePosition )
2738 // Sets the grab handle position.
2739 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2741 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2742 cursorInfo.lineHeight );
2745 if( cursorInfo.isSecondaryCursor )
2747 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2748 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2749 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2750 cursorInfo.secondaryCursorHeight,
2751 cursorInfo.lineHeight );
2752 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2755 // Set which cursors are active according the state.
2756 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2758 if( cursorInfo.isSecondaryCursor )
2760 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2764 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2769 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2772 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2775 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2776 const CursorInfo& cursorInfo )
2778 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2779 ( RIGHT_SELECTION_HANDLE != handleType ) )
2784 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2786 // Sets the handle's position.
2787 mEventData->mDecorator->SetPosition( handleType,
2789 cursorInfo.lineOffset + mModel->mScrollPosition.y,
2790 cursorInfo.lineHeight );
2792 // If selection handle at start of the text and other at end of the text then all text is selected.
2793 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2794 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2795 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2798 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2800 // Clamp between -space & -alignment offset.
2802 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2804 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2805 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2806 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2808 mEventData->mDecoratorUpdated = true;
2812 mModel->mScrollPosition.x = 0.f;
2816 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2818 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2820 // Nothing to do if the text is single line.
2824 // Clamp between -space & 0.
2825 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2827 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2828 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2829 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2831 mEventData->mDecoratorUpdated = true;
2835 mModel->mScrollPosition.y = 0.f;
2839 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2841 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2843 // position is in actor's coords.
2844 const float positionEndX = position.x + cursorWidth;
2845 const float positionEndY = position.y + lineHeight;
2847 // Transform the position to decorator coords.
2848 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2849 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2851 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2852 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2854 if( decoratorPositionBeginX < 0.f )
2856 mModel->mScrollPosition.x = -position.x;
2858 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2860 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2863 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2865 if( decoratorPositionBeginY < 0.f )
2867 mModel->mScrollPosition.y = -position.y;
2869 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2871 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2876 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2878 // Get the current cursor position in decorator coords.
2879 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2881 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
2885 // Calculate the offset to match the cursor position before the character was deleted.
2886 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2888 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2889 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
2891 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
2892 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2896 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2897 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2899 // Makes the new cursor position visible if needed.
2900 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2903 void Controller::Impl::RequestRelayout()
2905 if( NULL != mControlInterface )
2907 mControlInterface->RequestTextRelayout();
2913 } // namespace Toolkit