2 * Copyright (c) 2016 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-run-container.h>
40 * @brief Struct used to calculate the selection box.
42 struct SelectionBoxInfo
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
54 const float MAX_FLOAT = std::numeric_limits<float>::max();
55 const float MIN_FLOAT = std::numeric_limits<float>::min();
56 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
69 EventData::EventData( DecoratorPtr decorator )
70 : mDecorator( decorator ),
72 mPlaceholderTextActive(),
73 mPlaceholderTextInactive(),
74 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
76 mInputStyleChangedQueue(),
78 mPrimaryCursorPosition( 0u ),
79 mLeftSelectionPosition( 0u ),
80 mRightSelectionPosition( 0u ),
81 mPreEditStartPosition( 0u ),
83 mCursorHookPositionX( 0.f ),
84 mIsShowingPlaceholderText( false ),
85 mPreEditFlag( false ),
86 mDecoratorUpdated( false ),
87 mCursorBlinkEnabled( true ),
88 mGrabHandleEnabled( true ),
89 mGrabHandlePopupEnabled( true ),
90 mSelectionEnabled( true ),
91 mUpdateCursorPosition( false ),
92 mUpdateGrabHandlePosition( false ),
93 mUpdateLeftSelectionPosition( false ),
94 mUpdateRightSelectionPosition( false ),
95 mIsLeftHandleSelected( false ),
96 mUpdateHighlightBox( false ),
97 mScrollAfterUpdatePosition( false ),
98 mScrollAfterDelete( false ),
99 mAllTextSelected( false ),
100 mUpdateInputStyle( false )
102 mImfManager = ImfManager::Get();
105 EventData::~EventData()
108 bool Controller::Impl::ProcessInputEvents()
110 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
111 if( NULL == mEventData )
113 // Nothing to do if there is no text input.
114 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
118 if( mEventData->mDecorator )
120 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
121 iter != mEventData->mEventQueue.end();
126 case Event::CURSOR_KEY_EVENT:
128 OnCursorKeyEvent( *iter );
131 case Event::TAP_EVENT:
136 case Event::LONG_PRESS_EVENT:
138 OnLongPressEvent( *iter );
141 case Event::PAN_EVENT:
146 case Event::GRAB_HANDLE_EVENT:
147 case Event::LEFT_SELECTION_HANDLE_EVENT:
148 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
150 OnHandleEvent( *iter );
155 OnSelectEvent( *iter );
158 case Event::SELECT_ALL:
167 if( mEventData->mUpdateCursorPosition ||
168 mEventData->mUpdateHighlightBox )
173 // The cursor must also be repositioned after inserts into the model
174 if( mEventData->mUpdateCursorPosition )
176 // Updates the cursor position and scrolls the text to make it visible.
177 CursorInfo cursorInfo;
178 // Calculate the cursor position from the new cursor index.
179 GetCursorPosition( mEventData->mPrimaryCursorPosition,
182 if( mEventData->mUpdateCursorHookPosition )
184 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
185 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
186 mEventData->mUpdateCursorHookPosition = false;
189 // Scroll first the text after delete ...
190 if( mEventData->mScrollAfterDelete )
192 ScrollTextToMatchCursor( cursorInfo );
195 // ... then, text can be scrolled to make the cursor visible.
196 if( mEventData->mScrollAfterUpdatePosition )
198 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
199 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
201 mEventData->mScrollAfterUpdatePosition = false;
202 mEventData->mScrollAfterDelete = false;
204 UpdateCursorPosition( cursorInfo );
206 mEventData->mDecoratorUpdated = true;
207 mEventData->mUpdateCursorPosition = false;
208 mEventData->mUpdateGrabHandlePosition = false;
212 CursorInfo leftHandleInfo;
213 CursorInfo rightHandleInfo;
215 if( mEventData->mUpdateHighlightBox )
217 GetCursorPosition( mEventData->mLeftSelectionPosition,
220 GetCursorPosition( mEventData->mRightSelectionPosition,
223 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
225 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
227 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
228 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
232 if( mEventData->mUpdateLeftSelectionPosition )
234 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
238 mEventData->mDecoratorUpdated = true;
239 mEventData->mUpdateLeftSelectionPosition = false;
242 if( mEventData->mUpdateRightSelectionPosition )
244 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
248 mEventData->mDecoratorUpdated = true;
249 mEventData->mUpdateRightSelectionPosition = false;
252 if( mEventData->mUpdateHighlightBox )
254 RepositionSelectionHandles();
256 mEventData->mUpdateLeftSelectionPosition = false;
257 mEventData->mUpdateRightSelectionPosition = false;
258 mEventData->mUpdateHighlightBox = false;
261 mEventData->mScrollAfterUpdatePosition = false;
264 if( mEventData->mUpdateInputStyle )
266 // Keep a copy of the current input style.
267 InputStyle currentInputStyle;
268 currentInputStyle.Copy( mEventData->mInputStyle );
270 // Set the default style first.
271 RetrieveDefaultInputStyle( mEventData->mInputStyle );
273 // Get the character index from the cursor index.
274 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
276 // Retrieve the style from the style runs stored in the logical model.
277 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
279 // Compare if the input style has changed.
280 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
282 if( hasInputStyleChanged )
284 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
285 // Queue the input style changed signal.
286 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
289 mEventData->mUpdateInputStyle = false;
292 mEventData->mEventQueue.clear();
294 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
296 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
297 mEventData->mDecoratorUpdated = false;
299 return decoratorUpdated;
302 void Controller::Impl::NotifyImfManager()
304 if( mEventData && mEventData->mImfManager )
306 CharacterIndex cursorPosition = GetLogicalCursorPosition();
308 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
310 // Update the cursor position by removing the initial white spaces.
311 if( cursorPosition < numberOfWhiteSpaces )
317 cursorPosition -= numberOfWhiteSpaces;
320 mEventData->mImfManager.SetCursorPosition( cursorPosition );
321 mEventData->mImfManager.NotifyCursorPosition();
325 void Controller::Impl::NotifyImfMultiLineStatus()
329 LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
330 mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
334 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
336 CharacterIndex cursorPosition = 0u;
340 if( ( EventData::SELECTING == mEventData->mState ) ||
341 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
343 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
347 cursorPosition = mEventData->mPrimaryCursorPosition;
351 return cursorPosition;
354 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
356 Length numberOfWhiteSpaces = 0u;
358 // Get the buffer to the text.
359 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
361 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
362 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
364 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
370 return numberOfWhiteSpaces;
373 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
375 // Get the total number of characters.
376 Length numberOfCharacters = mLogicalModel->mText.Count();
378 // Retrieve the text.
379 if( 0u != numberOfCharacters )
381 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
385 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
387 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
388 mTextUpdateInfo.mStartGlyphIndex = 0u;
389 mTextUpdateInfo.mStartLineIndex = 0u;
390 numberOfCharacters = 0u;
392 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
393 if( 0u == numberOfParagraphs )
395 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
396 numberOfCharacters = 0u;
398 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
400 // Nothing else to do if there are no paragraphs.
404 // Find the paragraphs to be updated.
405 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
406 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
408 // Text is being added at the end of the current text.
409 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
411 // Text is being added in a new paragraph after the last character of the text.
412 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
413 numberOfCharacters = 0u;
414 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
416 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
417 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
419 // Nothing else to do;
423 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
427 Length numberOfCharactersToUpdate = 0u;
428 if( mTextUpdateInfo.mFullRelayoutNeeded )
430 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
434 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
436 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
437 numberOfCharactersToUpdate,
438 paragraphsToBeUpdated );
441 if( 0u != paragraphsToBeUpdated.Count() )
443 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
444 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
445 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
447 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
448 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
450 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
451 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
452 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
453 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
455 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
456 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
458 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
462 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
466 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
467 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
470 void Controller::Impl::ClearFullModelData( OperationsMask operations )
472 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
474 mLogicalModel->mLineBreakInfo.Clear();
475 mLogicalModel->mParagraphInfo.Clear();
478 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
480 mLogicalModel->mLineBreakInfo.Clear();
483 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
485 mLogicalModel->mScriptRuns.Clear();
488 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
490 mLogicalModel->mFontRuns.Clear();
493 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
495 if( NO_OPERATION != ( BIDI_INFO & operations ) )
497 mLogicalModel->mBidirectionalParagraphInfo.Clear();
498 mLogicalModel->mCharacterDirections.Clear();
501 if( NO_OPERATION != ( REORDER & operations ) )
503 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
504 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
505 endIt = mLogicalModel->mBidirectionalLineInfo.End();
509 BidirectionalLineInfoRun& bidiLineInfo = *it;
511 free( bidiLineInfo.visualToLogicalMap );
512 bidiLineInfo.visualToLogicalMap = NULL;
514 mLogicalModel->mBidirectionalLineInfo.Clear();
518 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
520 mVisualModel->mGlyphs.Clear();
521 mVisualModel->mGlyphsToCharacters.Clear();
522 mVisualModel->mCharactersToGlyph.Clear();
523 mVisualModel->mCharactersPerGlyph.Clear();
524 mVisualModel->mGlyphsPerCharacter.Clear();
525 mVisualModel->mGlyphPositions.Clear();
528 if( NO_OPERATION != ( LAYOUT & operations ) )
530 mVisualModel->mLines.Clear();
533 if( NO_OPERATION != ( COLOR & operations ) )
535 mVisualModel->mColorIndices.Clear();
539 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
541 const CharacterIndex endIndexPlusOne = endIndex + 1u;
543 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
545 // Clear the line break info.
546 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
548 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
549 lineBreakInfoBuffer + endIndexPlusOne );
551 // Clear the paragraphs.
552 ClearCharacterRuns( startIndex,
554 mLogicalModel->mParagraphInfo );
557 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
559 // Clear the word break info.
560 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
562 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
563 wordBreakInfoBuffer + endIndexPlusOne );
566 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
568 // Clear the scripts.
569 ClearCharacterRuns( startIndex,
571 mLogicalModel->mScriptRuns );
574 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
577 ClearCharacterRuns( startIndex,
579 mLogicalModel->mFontRuns );
582 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
584 if( NO_OPERATION != ( BIDI_INFO & operations ) )
586 // Clear the bidirectional paragraph info.
587 ClearCharacterRuns( startIndex,
589 mLogicalModel->mBidirectionalParagraphInfo );
591 // Clear the character's directions.
592 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
594 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
595 characterDirectionsBuffer + endIndexPlusOne );
598 if( NO_OPERATION != ( REORDER & operations ) )
600 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
601 uint32_t endRemoveIndex = startRemoveIndex;
602 ClearCharacterRuns( startIndex,
604 mLogicalModel->mBidirectionalLineInfo,
608 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
610 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
611 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
612 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
616 BidirectionalLineInfoRun& bidiLineInfo = *it;
618 free( bidiLineInfo.visualToLogicalMap );
619 bidiLineInfo.visualToLogicalMap = NULL;
622 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
623 bidirectionalLineInfoBuffer + endRemoveIndex );
628 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
630 const CharacterIndex endIndexPlusOne = endIndex + 1u;
631 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
633 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
634 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
635 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
637 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
638 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
640 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
642 // Update the character to glyph indices.
643 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
644 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
648 CharacterIndex& index = *it;
649 index -= numberOfGlyphsRemoved;
652 // Clear the character to glyph conversion table.
653 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
654 charactersToGlyphBuffer + endIndexPlusOne );
656 // Clear the glyphs per character table.
657 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
658 glyphsPerCharacterBuffer + endIndexPlusOne );
660 // Clear the glyphs buffer.
661 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
662 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
663 glyphsBuffer + endGlyphIndexPlusOne );
665 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
667 // Update the glyph to character indices.
668 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
669 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
673 CharacterIndex& index = *it;
674 index -= numberOfCharactersRemoved;
677 // Clear the glyphs to characters buffer.
678 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
679 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
681 // Clear the characters per glyph buffer.
682 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
683 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
684 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
686 // Clear the positions buffer.
687 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
688 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
689 positionsBuffer + endGlyphIndexPlusOne );
692 if( NO_OPERATION != ( LAYOUT & operations ) )
695 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
696 uint32_t endRemoveIndex = startRemoveIndex;
697 ClearCharacterRuns( startIndex,
699 mVisualModel->mLines,
703 // Will update the glyph runs.
704 startRemoveIndex = mVisualModel->mLines.Count();
705 endRemoveIndex = startRemoveIndex;
706 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
707 endGlyphIndexPlusOne - 1u,
708 mVisualModel->mLines,
712 // Set the line index from where to insert the new laid-out lines.
713 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
715 LineRun* linesBuffer = mVisualModel->mLines.Begin();
716 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
717 linesBuffer + endRemoveIndex );
720 if( NO_OPERATION != ( COLOR & operations ) )
722 if( 0u != mVisualModel->mColorIndices.Count() )
724 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
725 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
726 colorIndexBuffer + endGlyphIndexPlusOne );
731 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
733 if( mTextUpdateInfo.mClearAll ||
734 ( ( 0u == startIndex ) &&
735 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
737 ClearFullModelData( operations );
741 // Clear the model data related with characters.
742 ClearCharacterModelData( startIndex, endIndex, operations );
744 // Clear the model data related with glyphs.
745 ClearGlyphModelData( startIndex, endIndex, operations );
748 // The estimated number of lines. Used to avoid reallocations when layouting.
749 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
751 mVisualModel->ClearCaches();
754 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
756 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
758 // Calculate the operations to be done.
759 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
761 if( NO_OPERATION == operations )
763 // Nothing to do if no operations are pending and required.
767 Vector<Character>& utf32Characters = mLogicalModel->mText;
769 const Length numberOfCharacters = utf32Characters.Count();
771 // Index to the first character of the first paragraph to be updated.
772 CharacterIndex startIndex = 0u;
773 // Number of characters of the paragraphs to be removed.
774 Length paragraphCharacters = 0u;
776 CalculateTextUpdateIndices( paragraphCharacters );
777 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
779 if( mTextUpdateInfo.mClearAll ||
780 ( 0u != paragraphCharacters ) )
782 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
785 mTextUpdateInfo.mClearAll = false;
787 // Whether the model is updated.
788 bool updated = false;
790 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
791 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
793 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
795 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
796 // calculate the bidirectional info for each 'paragraph'.
797 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
798 // is not shaped together).
799 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
801 SetLineBreakInfo( utf32Characters,
803 requestedNumberOfCharacters,
806 // Create the paragraph info.
807 mLogicalModel->CreateParagraphInfo( startIndex,
808 requestedNumberOfCharacters );
812 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
813 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
815 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
816 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
818 SetWordBreakInfo( utf32Characters,
820 requestedNumberOfCharacters,
825 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
826 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
828 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
829 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
831 if( getScripts || validateFonts )
833 // Validates the fonts assigned by the application or assigns default ones.
834 // It makes sure all the characters are going to be rendered by the correct font.
835 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
839 // Retrieves the scripts used in the text.
840 multilanguageSupport.SetScripts( utf32Characters,
842 requestedNumberOfCharacters,
848 // Validate the fonts set through the mark-up string.
849 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
851 // Get the default font's description.
852 TextAbstraction::FontDescription defaultFontDescription;
853 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
854 if( NULL != mFontDefaults )
856 defaultFontDescription = mFontDefaults->mFontDescription;
857 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
860 // Validates the fonts. If there is a character with no assigned font it sets a default one.
861 // After this call, fonts are validated.
862 multilanguageSupport.ValidateFonts( utf32Characters,
865 defaultFontDescription,
868 requestedNumberOfCharacters,
874 Vector<Character> mirroredUtf32Characters;
875 bool textMirrored = false;
876 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
877 if( NO_OPERATION != ( BIDI_INFO & operations ) )
879 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
880 bidirectionalInfo.Reserve( numberOfParagraphs );
882 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
883 SetBidirectionalInfo( utf32Characters,
887 requestedNumberOfCharacters,
890 if( 0u != bidirectionalInfo.Count() )
892 // Only set the character directions if there is right to left characters.
893 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
894 GetCharactersDirection( bidirectionalInfo,
897 requestedNumberOfCharacters,
900 // This paragraph has right to left text. Some characters may need to be mirrored.
901 // TODO: consider if the mirrored string can be stored as well.
903 textMirrored = GetMirroredText( utf32Characters,
907 requestedNumberOfCharacters,
908 mirroredUtf32Characters );
912 // There is no right to left characters. Clear the directions vector.
913 mLogicalModel->mCharacterDirections.Clear();
918 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
919 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
920 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
921 Vector<GlyphIndex> newParagraphGlyphs;
922 newParagraphGlyphs.Reserve( numberOfParagraphs );
924 const Length currentNumberOfGlyphs = glyphs.Count();
925 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
927 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
929 ShapeText( textToShape,
934 mTextUpdateInfo.mStartGlyphIndex,
935 requestedNumberOfCharacters,
937 glyphsToCharactersMap,
939 newParagraphGlyphs );
941 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
942 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
943 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
947 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
949 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
951 GlyphInfo* glyphsBuffer = glyphs.Begin();
952 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
954 // Update the width and advance of all new paragraph characters.
955 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
957 const GlyphIndex index = *it;
958 GlyphInfo& glyph = *( glyphsBuffer + index );
960 glyph.xBearing = 0.f;
967 if( NO_OPERATION != ( COLOR & operations ) )
969 // Set the color runs in glyphs.
970 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
971 mVisualModel->mCharactersToGlyph,
972 mVisualModel->mGlyphsPerCharacter,
974 mTextUpdateInfo.mStartGlyphIndex,
975 requestedNumberOfCharacters,
976 mVisualModel->mColors,
977 mVisualModel->mColorIndices );
982 if( ( NULL != mEventData ) &&
983 mEventData->mPreEditFlag &&
984 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
986 // Add the underline for the pre-edit text.
987 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
988 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
990 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
991 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
992 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
993 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
995 GlyphRun underlineRun;
996 underlineRun.glyphIndex = glyphStart;
997 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
999 // TODO: At the moment the underline runs are only for pre-edit.
1000 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1003 // The estimated number of lines. Used to avoid reallocations when layouting.
1004 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
1006 // Set the previous number of characters for the next time the text is updated.
1007 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1012 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1014 // Sets the default text's color.
1015 inputStyle.textColor = mTextColor;
1016 inputStyle.isDefaultColor = true;
1018 inputStyle.familyName.clear();
1019 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1020 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1021 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1022 inputStyle.size = 0.f;
1024 inputStyle.lineSpacing = 0.f;
1026 inputStyle.underlineProperties.clear();
1027 inputStyle.shadowProperties.clear();
1028 inputStyle.embossProperties.clear();
1029 inputStyle.outlineProperties.clear();
1031 inputStyle.isFamilyDefined = false;
1032 inputStyle.isWeightDefined = false;
1033 inputStyle.isWidthDefined = false;
1034 inputStyle.isSlantDefined = false;
1035 inputStyle.isSizeDefined = false;
1037 inputStyle.isLineSpacingDefined = false;
1039 inputStyle.isUnderlineDefined = false;
1040 inputStyle.isShadowDefined = false;
1041 inputStyle.isEmbossDefined = false;
1042 inputStyle.isOutlineDefined = false;
1044 // Sets the default font's family name, weight, width, slant and size.
1047 if( mFontDefaults->familyDefined )
1049 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1050 inputStyle.isFamilyDefined = true;
1053 if( mFontDefaults->weightDefined )
1055 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1056 inputStyle.isWeightDefined = true;
1059 if( mFontDefaults->widthDefined )
1061 inputStyle.width = mFontDefaults->mFontDescription.width;
1062 inputStyle.isWidthDefined = true;
1065 if( mFontDefaults->slantDefined )
1067 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1068 inputStyle.isSlantDefined = true;
1071 if( mFontDefaults->sizeDefined )
1073 inputStyle.size = mFontDefaults->mDefaultPointSize;
1074 inputStyle.isSizeDefined = true;
1079 float Controller::Impl::GetDefaultFontLineHeight()
1081 FontId defaultFontId = 0u;
1082 if( NULL == mFontDefaults )
1084 TextAbstraction::FontDescription fontDescription;
1085 defaultFontId = mFontClient.GetFontId( fontDescription );
1089 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1092 Text::FontMetrics fontMetrics;
1093 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1095 return( fontMetrics.ascender - fontMetrics.descender );
1098 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1100 if( NULL == mEventData )
1102 // Nothing to do if there is no text input.
1106 int keyCode = event.p1.mInt;
1108 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1110 if( mEventData->mPrimaryCursorPosition > 0u )
1112 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1115 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1117 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1119 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1122 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1124 // Get first the line index of the current cursor position index.
1125 CharacterIndex characterIndex = 0u;
1127 if( mEventData->mPrimaryCursorPosition > 0u )
1129 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1132 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1134 if( lineIndex > 0u )
1136 // Retrieve the cursor position info.
1137 CursorInfo cursorInfo;
1138 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1141 // Get the line above.
1142 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1144 // Get the next hit 'y' point.
1145 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1147 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1148 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1151 mEventData->mCursorHookPositionX,
1155 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1157 // Get first the line index of the current cursor position index.
1158 CharacterIndex characterIndex = 0u;
1160 if( mEventData->mPrimaryCursorPosition > 0u )
1162 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1165 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1167 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1169 // Retrieve the cursor position info.
1170 CursorInfo cursorInfo;
1171 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1174 // Get the line below.
1175 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1177 // Get the next hit 'y' point.
1178 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1180 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1181 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1184 mEventData->mCursorHookPositionX,
1189 mEventData->mUpdateCursorPosition = true;
1190 mEventData->mUpdateInputStyle = true;
1191 mEventData->mScrollAfterUpdatePosition = true;
1194 void Controller::Impl::OnTapEvent( const Event& event )
1196 if( NULL != mEventData )
1198 const unsigned int tapCount = event.p1.mUint;
1200 if( 1u == tapCount )
1202 if( IsShowingRealText() )
1204 // Convert from control's coords to text's coords.
1205 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1206 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1208 // Keep the tap 'x' position. Used to move the cursor.
1209 mEventData->mCursorHookPositionX = xPosition;
1211 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1217 // When the cursor position is changing, delay cursor blinking
1218 mEventData->mDecorator->DelayCursorBlink();
1222 mEventData->mPrimaryCursorPosition = 0u;
1225 mEventData->mUpdateCursorPosition = true;
1226 mEventData->mUpdateGrabHandlePosition = true;
1227 mEventData->mScrollAfterUpdatePosition = true;
1228 mEventData->mUpdateInputStyle = true;
1230 // Notify the cursor position to the imf manager.
1231 if( mEventData->mImfManager )
1233 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1234 mEventData->mImfManager.NotifyCursorPosition();
1240 void Controller::Impl::OnPanEvent( const Event& event )
1242 if( NULL == mEventData )
1244 // Nothing to do if there is no text input.
1248 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1249 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1251 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1253 // Nothing to do if scrolling is not enabled.
1257 const int state = event.p1.mInt;
1261 case Gesture::Started:
1263 // Will remove the cursor, handles or text's popup, ...
1264 ChangeState( EventData::TEXT_PANNING );
1267 case Gesture::Continuing:
1269 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1270 const Vector2 currentScroll = mScrollPosition;
1272 if( isHorizontalScrollEnabled )
1274 const float displacementX = event.p2.mFloat;
1275 mScrollPosition.x += displacementX;
1277 ClampHorizontalScroll( layoutSize );
1280 if( isVerticalScrollEnabled )
1282 const float displacementY = event.p3.mFloat;
1283 mScrollPosition.y += displacementY;
1285 ClampVerticalScroll( layoutSize );
1288 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1291 case Gesture::Finished:
1292 case Gesture::Cancelled: // FALLTHROUGH
1294 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1295 ChangeState( mEventData->mPreviousState );
1303 void Controller::Impl::OnLongPressEvent( const Event& event )
1305 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1307 if( EventData::EDITING == mEventData->mState )
1309 ChangeState ( EventData::EDITING_WITH_POPUP );
1310 mEventData->mDecoratorUpdated = true;
1314 void Controller::Impl::OnHandleEvent( const Event& event )
1316 if( NULL == mEventData )
1318 // Nothing to do if there is no text input.
1322 const unsigned int state = event.p1.mUint;
1323 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1324 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1326 if( HANDLE_PRESSED == state )
1328 // Convert from decorator's coords to text's coords.
1329 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1330 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1332 // Need to calculate the handle's new position.
1333 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1339 if( Event::GRAB_HANDLE_EVENT == event.type )
1341 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1343 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1345 // Updates the cursor position if the handle's new position is different than the current one.
1346 mEventData->mUpdateCursorPosition = true;
1347 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1348 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1349 mEventData->mPrimaryCursorPosition = handleNewPosition;
1352 // 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.
1353 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1355 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1357 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1359 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1360 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1362 // Updates the highlight box if the handle's new position is different than the current one.
1363 mEventData->mUpdateHighlightBox = true;
1364 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1365 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1366 mEventData->mLeftSelectionPosition = handleNewPosition;
1369 // 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.
1370 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1372 // Will define the order to scroll the text to match the handle position.
1373 mEventData->mIsLeftHandleSelected = true;
1375 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1377 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1379 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1380 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1382 // Updates the highlight box if the handle's new position is different than the current one.
1383 mEventData->mUpdateHighlightBox = true;
1384 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1385 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1386 mEventData->mRightSelectionPosition = handleNewPosition;
1389 // 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.
1390 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1392 // Will define the order to scroll the text to match the handle position.
1393 mEventData->mIsLeftHandleSelected = false;
1395 } // end ( HANDLE_PRESSED == state )
1396 else if( ( HANDLE_RELEASED == state ) ||
1397 handleStopScrolling )
1399 CharacterIndex handlePosition = 0u;
1400 if( handleStopScrolling || isSmoothHandlePanEnabled )
1402 // Convert from decorator's coords to text's coords.
1403 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1404 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1406 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1413 if( Event::GRAB_HANDLE_EVENT == event.type )
1415 mEventData->mUpdateCursorPosition = true;
1416 mEventData->mUpdateGrabHandlePosition = true;
1417 mEventData->mUpdateInputStyle = true;
1419 if( !IsClipboardEmpty() )
1421 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1424 if( handleStopScrolling || isSmoothHandlePanEnabled )
1426 mEventData->mScrollAfterUpdatePosition = true;
1427 mEventData->mPrimaryCursorPosition = handlePosition;
1430 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1432 ChangeState( EventData::SELECTING );
1434 mEventData->mUpdateHighlightBox = true;
1435 mEventData->mUpdateLeftSelectionPosition = true;
1436 mEventData->mUpdateRightSelectionPosition = true;
1438 if( handleStopScrolling || isSmoothHandlePanEnabled )
1440 mEventData->mScrollAfterUpdatePosition = true;
1442 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1443 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1445 mEventData->mLeftSelectionPosition = handlePosition;
1449 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1451 ChangeState( EventData::SELECTING );
1453 mEventData->mUpdateHighlightBox = true;
1454 mEventData->mUpdateRightSelectionPosition = true;
1455 mEventData->mUpdateLeftSelectionPosition = true;
1457 if( handleStopScrolling || isSmoothHandlePanEnabled )
1459 mEventData->mScrollAfterUpdatePosition = true;
1460 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1461 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1463 mEventData->mRightSelectionPosition = handlePosition;
1468 mEventData->mDecoratorUpdated = true;
1469 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1470 else if( HANDLE_SCROLLING == state )
1472 const float xSpeed = event.p2.mFloat;
1473 const float ySpeed = event.p3.mFloat;
1474 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1475 const Vector2 currentScrollPosition = mScrollPosition;
1477 mScrollPosition.x += xSpeed;
1478 mScrollPosition.y += ySpeed;
1480 ClampHorizontalScroll( layoutSize );
1481 ClampVerticalScroll( layoutSize );
1483 bool endOfScroll = false;
1484 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1486 // Notify the decorator there is no more text to scroll.
1487 // The decorator won't send more scroll events.
1488 mEventData->mDecorator->NotifyEndOfScroll();
1489 // Still need to set the position of the handle.
1493 // Set the position of the handle.
1494 const bool scrollRightDirection = xSpeed > 0.f;
1495 const bool scrollBottomDirection = ySpeed > 0.f;
1496 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1497 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1499 if( Event::GRAB_HANDLE_EVENT == event.type )
1501 ChangeState( EventData::GRAB_HANDLE_PANNING );
1503 // Get the grab handle position in decorator coords.
1504 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1506 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1508 // Position the grag handle close to either the left or right edge.
1509 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1512 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1514 position.x = mEventData->mCursorHookPositionX;
1516 // Position the grag handle close to either the top or bottom edge.
1517 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1520 // Get the new handle position.
1521 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1522 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1525 position.x - mScrollPosition.x,
1526 position.y - mScrollPosition.y );
1528 if( mEventData->mPrimaryCursorPosition != handlePosition )
1530 mEventData->mUpdateCursorPosition = true;
1531 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1532 mEventData->mScrollAfterUpdatePosition = true;
1533 mEventData->mPrimaryCursorPosition = handlePosition;
1535 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1537 // Updates the decorator if the soft handle panning is enabled.
1538 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1540 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1542 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1544 // Get the selection handle position in decorator coords.
1545 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1547 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1549 // Position the selection handle close to either the left or right edge.
1550 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1553 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1555 position.x = mEventData->mCursorHookPositionX;
1557 // Position the grag handle close to either the top or bottom edge.
1558 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1561 // Get the new handle position.
1562 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1563 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1566 position.x - mScrollPosition.x,
1567 position.y - mScrollPosition.y );
1569 if( leftSelectionHandleEvent )
1571 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1573 if( differentHandles || endOfScroll )
1575 mEventData->mUpdateHighlightBox = true;
1576 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1577 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1578 mEventData->mLeftSelectionPosition = handlePosition;
1583 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1584 if( differentHandles || endOfScroll )
1586 mEventData->mUpdateHighlightBox = true;
1587 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1588 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1589 mEventData->mRightSelectionPosition = handlePosition;
1593 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1595 RepositionSelectionHandles();
1597 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1600 mEventData->mDecoratorUpdated = true;
1601 } // end ( HANDLE_SCROLLING == state )
1604 void Controller::Impl::OnSelectEvent( const Event& event )
1606 if( NULL == mEventData )
1608 // Nothing to do if there is no text.
1612 if( mEventData->mSelectionEnabled )
1614 // Convert from control's coords to text's coords.
1615 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1616 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1618 // Calculates the logical position from the x,y coords.
1619 RepositionSelectionHandles( xPosition,
1624 void Controller::Impl::OnSelectAllEvent()
1626 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1628 if( NULL == mEventData )
1630 // Nothing to do if there is no text.
1634 if( mEventData->mSelectionEnabled )
1636 ChangeState( EventData::SELECTING );
1638 mEventData->mLeftSelectionPosition = 0u;
1639 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1641 mEventData->mScrollAfterUpdatePosition = true;
1642 mEventData->mUpdateLeftSelectionPosition = true;
1643 mEventData->mUpdateRightSelectionPosition = true;
1644 mEventData->mUpdateHighlightBox = true;
1648 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1650 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1652 // Nothing to select if handles are in the same place.
1653 selectedText.clear();
1657 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1659 //Get start and end position of selection
1660 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1661 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1663 Vector<Character>& utf32Characters = mLogicalModel->mText;
1664 const Length numberOfCharacters = utf32Characters.Count();
1666 // Validate the start and end selection points
1667 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1669 //Get text as a UTF8 string
1670 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1672 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1674 // Keep a copy of the current input style.
1675 InputStyle currentInputStyle;
1676 currentInputStyle.Copy( mEventData->mInputStyle );
1678 // Set as input style the style of the first deleted character.
1679 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1681 // Compare if the input style has changed.
1682 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1684 if( hasInputStyleChanged )
1686 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1687 // Queue the input style changed signal.
1688 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1691 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1693 // Mark the paragraphs to be updated.
1694 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1695 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1697 // Delete text between handles
1698 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1699 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1700 utf32Characters.Erase( first, last );
1702 // Will show the cursor at the first character of the selection.
1703 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1707 // Will show the cursor at the last character of the selection.
1708 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1711 mEventData->mDecoratorUpdated = true;
1715 void Controller::Impl::ShowClipboard()
1719 mClipboard.ShowClipboard();
1723 void Controller::Impl::HideClipboard()
1725 if( mClipboard && mClipboardHideEnabled )
1727 mClipboard.HideClipboard();
1731 void Controller::Impl::SetClipboardHideEnable(bool enable)
1733 mClipboardHideEnabled = enable;
1736 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1738 //Send string to clipboard
1739 return ( mClipboard && mClipboard.SetItem( source ) );
1742 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1744 std::string selectedText;
1745 RetrieveSelection( selectedText, deleteAfterSending );
1746 CopyStringToClipboard( selectedText );
1747 ChangeState( EventData::EDITING );
1750 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1754 retrievedString = mClipboard.GetItem( itemIndex );
1758 void Controller::Impl::RepositionSelectionHandles()
1760 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1761 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1763 if( selectionStart == selectionEnd )
1765 // Nothing to select if handles are in the same place.
1769 mEventData->mDecorator->ClearHighlights();
1771 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1772 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1773 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1774 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1775 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1776 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1777 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1779 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1780 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1781 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1783 // Swap the indices if the start is greater than the end.
1784 const bool indicesSwapped = selectionStart > selectionEnd;
1786 // Tell the decorator to flip the selection handles if needed.
1787 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1789 if( indicesSwapped )
1791 std::swap( selectionStart, selectionEnd );
1794 // Get the indices to the first and last selected glyphs.
1795 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1796 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1797 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1798 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1800 // Get the lines where the glyphs are laid-out.
1801 const LineRun* lineRun = mVisualModel->mLines.Begin();
1803 LineIndex lineIndex = 0u;
1804 Length numberOfLines = 0u;
1805 mVisualModel->GetNumberOfLines( glyphStart,
1806 1u + glyphEnd - glyphStart,
1809 const LineIndex firstLineIndex = lineIndex;
1811 // Create the structure to store some selection box info.
1812 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1813 selectionBoxLinesInfo.Resize( numberOfLines );
1815 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1816 selectionBoxInfo->minX = MAX_FLOAT;
1817 selectionBoxInfo->maxX = MIN_FLOAT;
1819 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1820 float minHighlightX = std::numeric_limits<float>::max();
1821 float maxHighlightX = std::numeric_limits<float>::min();
1823 Vector2 highLightPosition; // The highlight position in decorator's coords.
1825 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1827 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1828 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1831 // Transform to decorator's (control) coords.
1832 selectionBoxInfo->lineOffset += mScrollPosition.y;
1834 lineRun += firstLineIndex;
1836 // The line height is the addition of the line ascender and the line descender.
1837 // However, the line descender has a negative value, hence the subtraction.
1838 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1840 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1842 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1843 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1844 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1846 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1847 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1848 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1850 // The number of quads of the selection box.
1851 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1852 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1854 // Count the actual number of quads.
1855 unsigned int actualNumberOfQuads = 0u;
1858 // Traverse the glyphs.
1859 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1861 const GlyphInfo& glyph = *( glyphsBuffer + index );
1862 const Vector2& position = *( positionsBuffer + index );
1864 if( splitStartGlyph )
1866 // 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.
1868 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1869 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1870 // Get the direction of the character.
1871 CharacterDirection isCurrentRightToLeft = false;
1872 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1874 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1877 // The end point could be in the middle of the ligature.
1878 // Calculate the number of characters selected.
1879 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1881 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1882 quad.y = selectionBoxInfo->lineOffset;
1883 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1884 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1886 // Store the min and max 'x' for each line.
1887 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1888 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1890 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1891 ++actualNumberOfQuads;
1893 splitStartGlyph = false;
1897 if( splitEndGlyph && ( index == glyphEnd ) )
1899 // 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.
1901 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1902 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1903 // Get the direction of the character.
1904 CharacterDirection isCurrentRightToLeft = false;
1905 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1907 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1910 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1912 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1913 quad.y = selectionBoxInfo->lineOffset;
1914 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1915 quad.w = quad.y + selectionBoxInfo->lineHeight;
1917 // Store the min and max 'x' for each line.
1918 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1919 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1921 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1923 ++actualNumberOfQuads;
1925 splitEndGlyph = false;
1929 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1930 quad.y = selectionBoxInfo->lineOffset;
1931 quad.z = quad.x + glyph.advance;
1932 quad.w = quad.y + selectionBoxInfo->lineHeight;
1934 // Store the min and max 'x' for each line.
1935 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1936 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1938 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1940 ++actualNumberOfQuads;
1942 // Whether to retrieve the next line.
1943 if( index == lastGlyphOfLine )
1945 // Retrieve the next line.
1948 // Get the last glyph of the new line.
1949 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1952 if( lineIndex < firstLineIndex + numberOfLines )
1954 // Keep the offset and height of the current selection box.
1955 const float currentLineOffset = selectionBoxInfo->lineOffset;
1956 const float currentLineHeight = selectionBoxInfo->lineHeight;
1958 // Get the selection box info for the next line.
1961 selectionBoxInfo->minX = MAX_FLOAT;
1962 selectionBoxInfo->maxX = MIN_FLOAT;
1964 // Update the line's vertical offset.
1965 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1967 // The line height is the addition of the line ascender and the line descender.
1968 // However, the line descender has a negative value, hence the subtraction.
1969 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1974 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1975 // The final width is calculated after 'boxifying' the selection.
1976 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1977 endIt = selectionBoxLinesInfo.End();
1981 const SelectionBoxInfo& info = *it;
1983 // Update the size of the highlighted text.
1984 highLightSize.height += info.lineHeight;
1985 minHighlightX = std::min( minHighlightX, info.minX );
1986 maxHighlightX = std::max( maxHighlightX, info.maxX );
1989 // Add extra geometry to 'boxify' the selection.
1991 if( 1u < numberOfLines )
1993 // Boxify the first line.
1994 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1995 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1997 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1998 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2003 quad.y = firstSelectionBoxLineInfo.lineOffset;
2004 quad.z = firstSelectionBoxLineInfo.minX;
2005 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2007 // Boxify at the beginning of the line.
2008 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2010 ++actualNumberOfQuads;
2012 // Update the size of the highlighted text.
2013 minHighlightX = 0.f;
2018 quad.x = firstSelectionBoxLineInfo.maxX;
2019 quad.y = firstSelectionBoxLineInfo.lineOffset;
2020 quad.z = mVisualModel->mControlSize.width;
2021 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2023 // Boxify at the end of the line.
2024 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2026 ++actualNumberOfQuads;
2028 // Update the size of the highlighted text.
2029 maxHighlightX = mVisualModel->mControlSize.width;
2032 // Boxify the central lines.
2033 if( 2u < numberOfLines )
2035 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2036 endIt = selectionBoxLinesInfo.End() - 1u;
2040 const SelectionBoxInfo& info = *it;
2043 quad.y = info.lineOffset;
2045 quad.w = info.lineOffset + info.lineHeight;
2047 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2049 ++actualNumberOfQuads;
2052 quad.y = info.lineOffset;
2053 quad.z = mVisualModel->mControlSize.width;
2054 quad.w = info.lineOffset + info.lineHeight;
2056 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2058 ++actualNumberOfQuads;
2061 // Update the size of the highlighted text.
2062 minHighlightX = 0.f;
2063 maxHighlightX = mVisualModel->mControlSize.width;
2066 // Boxify the last line.
2067 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2068 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2070 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2071 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2076 quad.y = lastSelectionBoxLineInfo.lineOffset;
2077 quad.z = lastSelectionBoxLineInfo.minX;
2078 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2080 // Boxify at the beginning of the line.
2081 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2083 ++actualNumberOfQuads;
2085 // Update the size of the highlighted text.
2086 minHighlightX = 0.f;
2091 quad.x = lastSelectionBoxLineInfo.maxX;
2092 quad.y = lastSelectionBoxLineInfo.lineOffset;
2093 quad.z = mVisualModel->mControlSize.width;
2094 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2096 // Boxify at the end of the line.
2097 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2099 ++actualNumberOfQuads;
2101 // Update the size of the highlighted text.
2102 maxHighlightX = mVisualModel->mControlSize.width;
2106 // Set the actual number of quads.
2107 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2109 // Sets the highlight's size and position. In decorator's coords.
2110 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2111 highLightSize.width = maxHighlightX - minHighlightX;
2113 highLightPosition.x = minHighlightX;
2114 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2115 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2117 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2119 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2121 CursorInfo primaryCursorInfo;
2122 GetCursorPosition( mEventData->mLeftSelectionPosition,
2123 primaryCursorInfo );
2125 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2127 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2129 primaryCursorInfo.lineOffset + mScrollPosition.y,
2130 primaryCursorInfo.lineHeight );
2132 CursorInfo secondaryCursorInfo;
2133 GetCursorPosition( mEventData->mRightSelectionPosition,
2134 secondaryCursorInfo );
2136 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2138 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2139 secondaryPosition.x,
2140 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2141 secondaryCursorInfo.lineHeight );
2144 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2145 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2147 // Set the flag to update the decorator.
2148 mEventData->mDecoratorUpdated = true;
2151 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2153 if( NULL == mEventData )
2155 // Nothing to do if there is no text input.
2159 if( IsShowingPlaceholderText() )
2161 // Nothing to do if there is the place-holder text.
2165 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2166 const Length numberOfLines = mVisualModel->mLines.Count();
2167 if( ( 0 == numberOfGlyphs ) ||
2168 ( 0 == numberOfLines ) )
2170 // Nothing to do if there is no text.
2174 // Find which word was selected
2175 CharacterIndex selectionStart( 0 );
2176 CharacterIndex selectionEnd( 0 );
2177 const bool indicesFound = FindSelectionIndices( mVisualModel,
2184 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2188 ChangeState( EventData::SELECTING );
2190 mEventData->mLeftSelectionPosition = selectionStart;
2191 mEventData->mRightSelectionPosition = selectionEnd;
2193 mEventData->mUpdateLeftSelectionPosition = true;
2194 mEventData->mUpdateRightSelectionPosition = true;
2195 mEventData->mUpdateHighlightBox = true;
2197 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2201 // Nothing to select. i.e. a white space, out of bounds
2202 ChangeState( EventData::EDITING );
2204 mEventData->mPrimaryCursorPosition = selectionEnd;
2206 mEventData->mUpdateCursorPosition = true;
2207 mEventData->mUpdateGrabHandlePosition = true;
2208 mEventData->mScrollAfterUpdatePosition = true;
2209 mEventData->mUpdateInputStyle = true;
2213 void Controller::Impl::SetPopupButtons()
2216 * Sets the Popup buttons to be shown depending on State.
2218 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2220 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2223 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2225 if( EventData::SELECTING == mEventData->mState )
2227 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2229 if( !IsClipboardEmpty() )
2231 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2232 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2235 if( !mEventData->mAllTextSelected )
2237 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2240 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2242 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2244 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2247 if( !IsClipboardEmpty() )
2249 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2250 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2253 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2255 if ( !IsClipboardEmpty() )
2257 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2258 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2262 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2265 void Controller::Impl::ChangeState( EventData::State newState )
2267 if( NULL == mEventData )
2269 // Nothing to do if there is no text input.
2273 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2275 if( mEventData->mState != newState )
2277 mEventData->mPreviousState = mEventData->mState;
2278 mEventData->mState = newState;
2280 switch( mEventData->mState )
2282 case EventData::INACTIVE:
2284 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2285 mEventData->mDecorator->StopCursorBlink();
2286 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2287 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2288 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2289 mEventData->mDecorator->SetHighlightActive( false );
2290 mEventData->mDecorator->SetPopupActive( false );
2291 mEventData->mDecoratorUpdated = true;
2295 case EventData::INTERRUPTED:
2297 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2298 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2299 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2300 mEventData->mDecorator->SetHighlightActive( false );
2301 mEventData->mDecorator->SetPopupActive( false );
2302 mEventData->mDecoratorUpdated = true;
2306 case EventData::SELECTING:
2308 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2309 mEventData->mDecorator->StopCursorBlink();
2310 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2311 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2312 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2313 mEventData->mDecorator->SetHighlightActive( true );
2314 if( mEventData->mGrabHandlePopupEnabled )
2317 mEventData->mDecorator->SetPopupActive( true );
2319 mEventData->mDecoratorUpdated = true;
2322 case EventData::EDITING:
2324 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2325 if( mEventData->mCursorBlinkEnabled )
2327 mEventData->mDecorator->StartCursorBlink();
2329 // Grab handle is not shown until a tap is received whilst EDITING
2330 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2331 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2332 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2333 mEventData->mDecorator->SetHighlightActive( false );
2334 if( mEventData->mGrabHandlePopupEnabled )
2336 mEventData->mDecorator->SetPopupActive( false );
2338 mEventData->mDecoratorUpdated = true;
2342 case EventData::EDITING_WITH_POPUP:
2344 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2346 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2347 if( mEventData->mCursorBlinkEnabled )
2349 mEventData->mDecorator->StartCursorBlink();
2351 if( mEventData->mSelectionEnabled )
2353 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2354 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2355 mEventData->mDecorator->SetHighlightActive( false );
2359 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2361 if( mEventData->mGrabHandlePopupEnabled )
2364 mEventData->mDecorator->SetPopupActive( true );
2367 mEventData->mDecoratorUpdated = true;
2370 case EventData::EDITING_WITH_GRAB_HANDLE:
2372 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2374 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2375 if( mEventData->mCursorBlinkEnabled )
2377 mEventData->mDecorator->StartCursorBlink();
2379 // Grab handle is not shown until a tap is received whilst EDITING
2380 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2381 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2382 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2383 mEventData->mDecorator->SetHighlightActive( false );
2384 if( mEventData->mGrabHandlePopupEnabled )
2386 mEventData->mDecorator->SetPopupActive( false );
2388 mEventData->mDecoratorUpdated = true;
2392 case EventData::SELECTION_HANDLE_PANNING:
2394 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2395 mEventData->mDecorator->StopCursorBlink();
2396 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2397 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2398 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2399 mEventData->mDecorator->SetHighlightActive( true );
2400 if( mEventData->mGrabHandlePopupEnabled )
2402 mEventData->mDecorator->SetPopupActive( false );
2404 mEventData->mDecoratorUpdated = true;
2407 case EventData::GRAB_HANDLE_PANNING:
2409 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2411 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2412 if( mEventData->mCursorBlinkEnabled )
2414 mEventData->mDecorator->StartCursorBlink();
2416 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2417 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2418 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2419 mEventData->mDecorator->SetHighlightActive( false );
2420 if( mEventData->mGrabHandlePopupEnabled )
2422 mEventData->mDecorator->SetPopupActive( false );
2424 mEventData->mDecoratorUpdated = true;
2427 case EventData::EDITING_WITH_PASTE_POPUP:
2429 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2431 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2432 if( mEventData->mCursorBlinkEnabled )
2434 mEventData->mDecorator->StartCursorBlink();
2437 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2438 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2439 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2440 mEventData->mDecorator->SetHighlightActive( false );
2442 if( mEventData->mGrabHandlePopupEnabled )
2445 mEventData->mDecorator->SetPopupActive( true );
2448 mEventData->mDecoratorUpdated = true;
2451 case EventData::TEXT_PANNING:
2453 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2454 mEventData->mDecorator->StopCursorBlink();
2455 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2456 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2457 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2459 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2460 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2461 mEventData->mDecorator->SetHighlightActive( true );
2464 if( mEventData->mGrabHandlePopupEnabled )
2466 mEventData->mDecorator->SetPopupActive( false );
2469 mEventData->mDecoratorUpdated = true;
2476 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2477 CursorInfo& cursorInfo )
2479 if( !IsShowingRealText() )
2481 // Do not want to use the place-holder text to set the cursor position.
2483 // Use the line's height of the font's family set to set the cursor's size.
2484 // If there is no font's family set, use the default font.
2485 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2487 cursorInfo.lineOffset = 0.f;
2488 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2489 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2491 switch( mLayoutEngine.GetHorizontalAlignment() )
2493 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2495 cursorInfo.primaryPosition.x = 0.f;
2498 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2500 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2503 case LayoutEngine::HORIZONTAL_ALIGN_END:
2505 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2510 // Nothing else to do.
2514 Text::GetCursorPosition( mVisualModel,
2520 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2522 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2524 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2525 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2527 if( 0.f > cursorInfo.primaryPosition.x )
2529 cursorInfo.primaryPosition.x = 0.f;
2532 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2533 if( cursorInfo.primaryPosition.x > edgeWidth )
2535 cursorInfo.primaryPosition.x = edgeWidth;
2540 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2542 if( NULL == mEventData )
2544 // Nothing to do if there is no text input.
2548 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2550 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2551 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2553 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2554 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2556 if( numberOfCharacters > 1u )
2558 const Script script = mLogicalModel->GetScript( index );
2559 if( HasLigatureMustBreak( script ) )
2561 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2562 numberOfCharacters = 1u;
2567 while( 0u == numberOfCharacters )
2570 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2574 if( index < mEventData->mPrimaryCursorPosition )
2576 cursorIndex -= numberOfCharacters;
2580 cursorIndex += numberOfCharacters;
2583 // Will update the cursor hook position.
2584 mEventData->mUpdateCursorHookPosition = true;
2589 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2591 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2592 if( NULL == mEventData )
2594 // Nothing to do if there is no text input.
2595 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2599 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2601 // Sets the cursor position.
2602 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2605 cursorInfo.primaryCursorHeight,
2606 cursorInfo.lineHeight );
2607 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2609 if( mEventData->mUpdateGrabHandlePosition )
2611 // Sets the grab handle position.
2612 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2614 cursorInfo.lineOffset + mScrollPosition.y,
2615 cursorInfo.lineHeight );
2618 if( cursorInfo.isSecondaryCursor )
2620 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2621 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2622 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2623 cursorInfo.secondaryCursorHeight,
2624 cursorInfo.lineHeight );
2625 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2628 // Set which cursors are active according the state.
2629 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2631 if( cursorInfo.isSecondaryCursor )
2633 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2637 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2642 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2645 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2648 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2649 const CursorInfo& cursorInfo )
2651 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2652 ( RIGHT_SELECTION_HANDLE != handleType ) )
2657 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2659 // Sets the handle's position.
2660 mEventData->mDecorator->SetPosition( handleType,
2662 cursorInfo.lineOffset + mScrollPosition.y,
2663 cursorInfo.lineHeight );
2665 // If selection handle at start of the text and other at end of the text then all text is selected.
2666 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2667 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2668 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2671 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2673 // Clamp between -space & 0.
2675 if( layoutSize.width > mVisualModel->mControlSize.width )
2677 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2678 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2679 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2681 mEventData->mDecoratorUpdated = true;
2685 mScrollPosition.x = 0.f;
2689 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2691 // Clamp between -space & 0.
2692 if( layoutSize.height > mVisualModel->mControlSize.height )
2694 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2695 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2696 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2698 mEventData->mDecoratorUpdated = true;
2702 mScrollPosition.y = 0.f;
2706 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2708 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2710 // position is in actor's coords.
2711 const float positionEndX = position.x + cursorWidth;
2712 const float positionEndY = position.y + lineHeight;
2714 // Transform the position to decorator coords.
2715 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2716 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2718 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2719 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2721 if( decoratorPositionBeginX < 0.f )
2723 mScrollPosition.x = -position.x;
2725 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2727 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2730 if( decoratorPositionBeginY < 0.f )
2732 mScrollPosition.y = -position.y;
2734 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2736 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2740 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2742 // Get the current cursor position in decorator coords.
2743 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2745 // Calculate the offset to match the cursor position before the character was deleted.
2746 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2747 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2749 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2750 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2752 // Makes the new cursor position visible if needed.
2753 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2756 void Controller::Impl::RequestRelayout()
2758 mControlInterface.RequestTextRelayout();
2763 } // namespace Toolkit