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-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 mPlaceholderTextActive(),
74 mPlaceholderTextInactive(),
75 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77 mInputStyleChangedQueue(),
79 mPrimaryCursorPosition( 0u ),
80 mLeftSelectionPosition( 0u ),
81 mRightSelectionPosition( 0u ),
82 mPreEditStartPosition( 0u ),
84 mCursorHookPositionX( 0.f ),
85 mIsShowingPlaceholderText( false ),
86 mPreEditFlag( false ),
87 mDecoratorUpdated( false ),
88 mCursorBlinkEnabled( true ),
89 mGrabHandleEnabled( true ),
90 mGrabHandlePopupEnabled( true ),
91 mSelectionEnabled( true ),
92 mUpdateCursorPosition( false ),
93 mUpdateGrabHandlePosition( false ),
94 mUpdateLeftSelectionPosition( false ),
95 mUpdateRightSelectionPosition( false ),
96 mIsLeftHandleSelected( false ),
97 mUpdateHighlightBox( false ),
98 mScrollAfterUpdatePosition( false ),
99 mScrollAfterDelete( false ),
100 mAllTextSelected( false ),
101 mUpdateInputStyle( false )
103 mImfManager = ImfManager::Get();
106 EventData::~EventData()
109 bool Controller::Impl::ProcessInputEvents()
111 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
112 if( NULL == mEventData )
114 // Nothing to do if there is no text input.
115 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
119 if( mEventData->mDecorator )
121 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
122 iter != mEventData->mEventQueue.end();
127 case Event::CURSOR_KEY_EVENT:
129 OnCursorKeyEvent( *iter );
132 case Event::TAP_EVENT:
137 case Event::LONG_PRESS_EVENT:
139 OnLongPressEvent( *iter );
142 case Event::PAN_EVENT:
147 case Event::GRAB_HANDLE_EVENT:
148 case Event::LEFT_SELECTION_HANDLE_EVENT:
149 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
151 OnHandleEvent( *iter );
156 OnSelectEvent( *iter );
159 case Event::SELECT_ALL:
168 if( mEventData->mUpdateCursorPosition ||
169 mEventData->mUpdateHighlightBox )
174 // The cursor must also be repositioned after inserts into the model
175 if( mEventData->mUpdateCursorPosition )
177 // Updates the cursor position and scrolls the text to make it visible.
178 CursorInfo cursorInfo;
179 // Calculate the cursor position from the new cursor index.
180 GetCursorPosition( mEventData->mPrimaryCursorPosition,
183 if( mEventData->mUpdateCursorHookPosition )
185 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
186 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
187 mEventData->mUpdateCursorHookPosition = false;
190 // Scroll first the text after delete ...
191 if( mEventData->mScrollAfterDelete )
193 ScrollTextToMatchCursor( cursorInfo );
196 // ... then, text can be scrolled to make the cursor visible.
197 if( mEventData->mScrollAfterUpdatePosition )
199 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
200 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
202 mEventData->mScrollAfterUpdatePosition = false;
203 mEventData->mScrollAfterDelete = false;
205 UpdateCursorPosition( cursorInfo );
207 mEventData->mDecoratorUpdated = true;
208 mEventData->mUpdateCursorPosition = false;
209 mEventData->mUpdateGrabHandlePosition = false;
213 CursorInfo leftHandleInfo;
214 CursorInfo rightHandleInfo;
216 if( mEventData->mUpdateHighlightBox )
218 GetCursorPosition( mEventData->mLeftSelectionPosition,
221 GetCursorPosition( mEventData->mRightSelectionPosition,
224 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
226 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
228 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
229 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
233 if( mEventData->mUpdateLeftSelectionPosition )
235 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
239 mEventData->mDecoratorUpdated = true;
240 mEventData->mUpdateLeftSelectionPosition = false;
243 if( mEventData->mUpdateRightSelectionPosition )
245 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
249 mEventData->mDecoratorUpdated = true;
250 mEventData->mUpdateRightSelectionPosition = false;
253 if( mEventData->mUpdateHighlightBox )
255 RepositionSelectionHandles();
257 mEventData->mUpdateLeftSelectionPosition = false;
258 mEventData->mUpdateRightSelectionPosition = false;
259 mEventData->mUpdateHighlightBox = false;
262 mEventData->mScrollAfterUpdatePosition = false;
265 if( mEventData->mUpdateInputStyle )
267 // Keep a copy of the current input style.
268 InputStyle currentInputStyle;
269 currentInputStyle.Copy( mEventData->mInputStyle );
271 // Set the default style first.
272 RetrieveDefaultInputStyle( mEventData->mInputStyle );
274 // Get the character index from the cursor index.
275 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
277 // Retrieve the style from the style runs stored in the logical model.
278 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
280 // Compare if the input style has changed.
281 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
283 if( hasInputStyleChanged )
285 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
286 // Queue the input style changed signal.
287 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
290 mEventData->mUpdateInputStyle = false;
293 mEventData->mEventQueue.clear();
295 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
297 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
298 mEventData->mDecoratorUpdated = false;
300 return decoratorUpdated;
303 void Controller::Impl::NotifyImfManager()
305 if( mEventData && mEventData->mImfManager )
307 CharacterIndex cursorPosition = GetLogicalCursorPosition();
309 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
311 // Update the cursor position by removing the initial white spaces.
312 if( cursorPosition < numberOfWhiteSpaces )
318 cursorPosition -= numberOfWhiteSpaces;
321 mEventData->mImfManager.SetCursorPosition( cursorPosition );
322 mEventData->mImfManager.NotifyCursorPosition();
326 void Controller::Impl::NotifyImfMultiLineStatus()
330 LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
331 mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
335 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
337 CharacterIndex cursorPosition = 0u;
341 if( ( EventData::SELECTING == mEventData->mState ) ||
342 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
344 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
348 cursorPosition = mEventData->mPrimaryCursorPosition;
352 return cursorPosition;
355 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
357 Length numberOfWhiteSpaces = 0u;
359 // Get the buffer to the text.
360 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
362 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
363 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
365 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
371 return numberOfWhiteSpaces;
374 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
376 // Get the total number of characters.
377 Length numberOfCharacters = mLogicalModel->mText.Count();
379 // Retrieve the text.
380 if( 0u != numberOfCharacters )
382 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
386 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
388 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
389 mTextUpdateInfo.mStartGlyphIndex = 0u;
390 mTextUpdateInfo.mStartLineIndex = 0u;
391 numberOfCharacters = 0u;
393 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
394 if( 0u == numberOfParagraphs )
396 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
397 numberOfCharacters = 0u;
399 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
401 // Nothing else to do if there are no paragraphs.
405 // Find the paragraphs to be updated.
406 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
407 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
409 // Text is being added at the end of the current text.
410 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
412 // Text is being added in a new paragraph after the last character of the text.
413 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
414 numberOfCharacters = 0u;
415 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
417 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
418 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
420 // Nothing else to do;
424 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
428 Length numberOfCharactersToUpdate = 0u;
429 if( mTextUpdateInfo.mFullRelayoutNeeded )
431 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
435 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
437 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
438 numberOfCharactersToUpdate,
439 paragraphsToBeUpdated );
442 if( 0u != paragraphsToBeUpdated.Count() )
444 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
445 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
446 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
448 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
449 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
451 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
452 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
453 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
454 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
456 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
457 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
459 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
463 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
467 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
468 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
471 void Controller::Impl::ClearFullModelData( OperationsMask operations )
473 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
475 mLogicalModel->mLineBreakInfo.Clear();
476 mLogicalModel->mParagraphInfo.Clear();
479 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
481 mLogicalModel->mLineBreakInfo.Clear();
484 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
486 mLogicalModel->mScriptRuns.Clear();
489 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
491 mLogicalModel->mFontRuns.Clear();
494 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
496 if( NO_OPERATION != ( BIDI_INFO & operations ) )
498 mLogicalModel->mBidirectionalParagraphInfo.Clear();
499 mLogicalModel->mCharacterDirections.Clear();
502 if( NO_OPERATION != ( REORDER & operations ) )
504 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
505 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
506 endIt = mLogicalModel->mBidirectionalLineInfo.End();
510 BidirectionalLineInfoRun& bidiLineInfo = *it;
512 free( bidiLineInfo.visualToLogicalMap );
513 bidiLineInfo.visualToLogicalMap = NULL;
515 mLogicalModel->mBidirectionalLineInfo.Clear();
519 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
521 mVisualModel->mGlyphs.Clear();
522 mVisualModel->mGlyphsToCharacters.Clear();
523 mVisualModel->mCharactersToGlyph.Clear();
524 mVisualModel->mCharactersPerGlyph.Clear();
525 mVisualModel->mGlyphsPerCharacter.Clear();
526 mVisualModel->mGlyphPositions.Clear();
529 if( NO_OPERATION != ( LAYOUT & operations ) )
531 mVisualModel->mLines.Clear();
534 if( NO_OPERATION != ( COLOR & operations ) )
536 mVisualModel->mColorIndices.Clear();
540 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
542 const CharacterIndex endIndexPlusOne = endIndex + 1u;
544 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
546 // Clear the line break info.
547 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
549 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
550 lineBreakInfoBuffer + endIndexPlusOne );
552 // Clear the paragraphs.
553 ClearCharacterRuns( startIndex,
555 mLogicalModel->mParagraphInfo );
558 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
560 // Clear the word break info.
561 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
563 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
564 wordBreakInfoBuffer + endIndexPlusOne );
567 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
569 // Clear the scripts.
570 ClearCharacterRuns( startIndex,
572 mLogicalModel->mScriptRuns );
575 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
578 ClearCharacterRuns( startIndex,
580 mLogicalModel->mFontRuns );
583 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
585 if( NO_OPERATION != ( BIDI_INFO & operations ) )
587 // Clear the bidirectional paragraph info.
588 ClearCharacterRuns( startIndex,
590 mLogicalModel->mBidirectionalParagraphInfo );
592 // Clear the character's directions.
593 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
595 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
596 characterDirectionsBuffer + endIndexPlusOne );
599 if( NO_OPERATION != ( REORDER & operations ) )
601 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
602 uint32_t endRemoveIndex = startRemoveIndex;
603 ClearCharacterRuns( startIndex,
605 mLogicalModel->mBidirectionalLineInfo,
609 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
611 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
612 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
613 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
617 BidirectionalLineInfoRun& bidiLineInfo = *it;
619 free( bidiLineInfo.visualToLogicalMap );
620 bidiLineInfo.visualToLogicalMap = NULL;
623 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
624 bidirectionalLineInfoBuffer + endRemoveIndex );
629 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
631 const CharacterIndex endIndexPlusOne = endIndex + 1u;
632 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
634 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
635 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
636 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
638 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
639 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
641 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
643 // Update the character to glyph indices.
644 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
645 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
649 CharacterIndex& index = *it;
650 index -= numberOfGlyphsRemoved;
653 // Clear the character to glyph conversion table.
654 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
655 charactersToGlyphBuffer + endIndexPlusOne );
657 // Clear the glyphs per character table.
658 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
659 glyphsPerCharacterBuffer + endIndexPlusOne );
661 // Clear the glyphs buffer.
662 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
663 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
664 glyphsBuffer + endGlyphIndexPlusOne );
666 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
668 // Update the glyph to character indices.
669 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
670 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
674 CharacterIndex& index = *it;
675 index -= numberOfCharactersRemoved;
678 // Clear the glyphs to characters buffer.
679 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
680 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
682 // Clear the characters per glyph buffer.
683 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
684 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
685 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
687 // Clear the positions buffer.
688 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
689 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
690 positionsBuffer + endGlyphIndexPlusOne );
693 if( NO_OPERATION != ( LAYOUT & operations ) )
696 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
697 uint32_t endRemoveIndex = startRemoveIndex;
698 ClearCharacterRuns( startIndex,
700 mVisualModel->mLines,
704 // Will update the glyph runs.
705 startRemoveIndex = mVisualModel->mLines.Count();
706 endRemoveIndex = startRemoveIndex;
707 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
708 endGlyphIndexPlusOne - 1u,
709 mVisualModel->mLines,
713 // Set the line index from where to insert the new laid-out lines.
714 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
716 LineRun* linesBuffer = mVisualModel->mLines.Begin();
717 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
718 linesBuffer + endRemoveIndex );
721 if( NO_OPERATION != ( COLOR & operations ) )
723 if( 0u != mVisualModel->mColorIndices.Count() )
725 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
726 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
727 colorIndexBuffer + endGlyphIndexPlusOne );
732 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
734 if( mTextUpdateInfo.mClearAll ||
735 ( ( 0u == startIndex ) &&
736 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
738 ClearFullModelData( operations );
742 // Clear the model data related with characters.
743 ClearCharacterModelData( startIndex, endIndex, operations );
745 // Clear the model data related with glyphs.
746 ClearGlyphModelData( startIndex, endIndex, operations );
749 // The estimated number of lines. Used to avoid reallocations when layouting.
750 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
752 mVisualModel->ClearCaches();
755 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
757 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
759 // Calculate the operations to be done.
760 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
762 if( NO_OPERATION == operations )
764 // Nothing to do if no operations are pending and required.
768 Vector<Character>& utf32Characters = mLogicalModel->mText;
770 const Length numberOfCharacters = utf32Characters.Count();
772 // Index to the first character of the first paragraph to be updated.
773 CharacterIndex startIndex = 0u;
774 // Number of characters of the paragraphs to be removed.
775 Length paragraphCharacters = 0u;
777 CalculateTextUpdateIndices( paragraphCharacters );
778 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
780 if( mTextUpdateInfo.mClearAll ||
781 ( 0u != paragraphCharacters ) )
783 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
786 mTextUpdateInfo.mClearAll = false;
788 // Whether the model is updated.
789 bool updated = false;
791 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
792 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
794 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
796 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
797 // calculate the bidirectional info for each 'paragraph'.
798 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
799 // is not shaped together).
800 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
802 SetLineBreakInfo( utf32Characters,
804 requestedNumberOfCharacters,
807 // Create the paragraph info.
808 mLogicalModel->CreateParagraphInfo( startIndex,
809 requestedNumberOfCharacters );
813 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
814 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
816 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
817 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
819 SetWordBreakInfo( utf32Characters,
821 requestedNumberOfCharacters,
826 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
827 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
829 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
830 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
832 if( getScripts || validateFonts )
834 // Validates the fonts assigned by the application or assigns default ones.
835 // It makes sure all the characters are going to be rendered by the correct font.
836 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
840 // Retrieves the scripts used in the text.
841 multilanguageSupport.SetScripts( utf32Characters,
843 requestedNumberOfCharacters,
849 // Validate the fonts set through the mark-up string.
850 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
852 // Get the default font's description.
853 TextAbstraction::FontDescription defaultFontDescription;
854 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
855 if( NULL != mFontDefaults )
857 defaultFontDescription = mFontDefaults->mFontDescription;
858 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
861 // Validates the fonts. If there is a character with no assigned font it sets a default one.
862 // After this call, fonts are validated.
863 multilanguageSupport.ValidateFonts( utf32Characters,
866 defaultFontDescription,
869 requestedNumberOfCharacters,
875 Vector<Character> mirroredUtf32Characters;
876 bool textMirrored = false;
877 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
878 if( NO_OPERATION != ( BIDI_INFO & operations ) )
880 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
881 bidirectionalInfo.Reserve( numberOfParagraphs );
883 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
884 SetBidirectionalInfo( utf32Characters,
888 requestedNumberOfCharacters,
891 if( 0u != bidirectionalInfo.Count() )
893 // Only set the character directions if there is right to left characters.
894 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
895 GetCharactersDirection( bidirectionalInfo,
898 requestedNumberOfCharacters,
901 // This paragraph has right to left text. Some characters may need to be mirrored.
902 // TODO: consider if the mirrored string can be stored as well.
904 textMirrored = GetMirroredText( utf32Characters,
908 requestedNumberOfCharacters,
909 mirroredUtf32Characters );
913 // There is no right to left characters. Clear the directions vector.
914 mLogicalModel->mCharacterDirections.Clear();
919 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
920 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
921 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
922 Vector<GlyphIndex> newParagraphGlyphs;
923 newParagraphGlyphs.Reserve( numberOfParagraphs );
925 const Length currentNumberOfGlyphs = glyphs.Count();
926 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
928 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
930 ShapeText( textToShape,
935 mTextUpdateInfo.mStartGlyphIndex,
936 requestedNumberOfCharacters,
938 glyphsToCharactersMap,
940 newParagraphGlyphs );
942 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
943 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
944 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
948 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
950 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
952 GlyphInfo* glyphsBuffer = glyphs.Begin();
953 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
955 // Update the width and advance of all new paragraph characters.
956 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
958 const GlyphIndex index = *it;
959 GlyphInfo& glyph = *( glyphsBuffer + index );
961 glyph.xBearing = 0.f;
968 if( NO_OPERATION != ( COLOR & operations ) )
970 // Set the color runs in glyphs.
971 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
972 mVisualModel->mCharactersToGlyph,
973 mVisualModel->mGlyphsPerCharacter,
975 mTextUpdateInfo.mStartGlyphIndex,
976 requestedNumberOfCharacters,
977 mVisualModel->mColors,
978 mVisualModel->mColorIndices );
983 if( ( NULL != mEventData ) &&
984 mEventData->mPreEditFlag &&
985 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
987 // Add the underline for the pre-edit text.
988 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
989 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
991 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
992 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
993 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
994 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
996 GlyphRun underlineRun;
997 underlineRun.glyphIndex = glyphStart;
998 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1000 // TODO: At the moment the underline runs are only for pre-edit.
1001 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1004 // The estimated number of lines. Used to avoid reallocations when layouting.
1005 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
1007 // Set the previous number of characters for the next time the text is updated.
1008 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1013 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1015 // Sets the default text's color.
1016 inputStyle.textColor = mTextColor;
1017 inputStyle.isDefaultColor = true;
1019 inputStyle.familyName.clear();
1020 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1021 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1022 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1023 inputStyle.size = 0.f;
1025 inputStyle.lineSpacing = 0.f;
1027 inputStyle.underlineProperties.clear();
1028 inputStyle.shadowProperties.clear();
1029 inputStyle.embossProperties.clear();
1030 inputStyle.outlineProperties.clear();
1032 inputStyle.isFamilyDefined = false;
1033 inputStyle.isWeightDefined = false;
1034 inputStyle.isWidthDefined = false;
1035 inputStyle.isSlantDefined = false;
1036 inputStyle.isSizeDefined = false;
1038 inputStyle.isLineSpacingDefined = false;
1040 inputStyle.isUnderlineDefined = false;
1041 inputStyle.isShadowDefined = false;
1042 inputStyle.isEmbossDefined = false;
1043 inputStyle.isOutlineDefined = false;
1045 // Sets the default font's family name, weight, width, slant and size.
1048 if( mFontDefaults->familyDefined )
1050 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1051 inputStyle.isFamilyDefined = true;
1054 if( mFontDefaults->weightDefined )
1056 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1057 inputStyle.isWeightDefined = true;
1060 if( mFontDefaults->widthDefined )
1062 inputStyle.width = mFontDefaults->mFontDescription.width;
1063 inputStyle.isWidthDefined = true;
1066 if( mFontDefaults->slantDefined )
1068 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1069 inputStyle.isSlantDefined = true;
1072 if( mFontDefaults->sizeDefined )
1074 inputStyle.size = mFontDefaults->mDefaultPointSize;
1075 inputStyle.isSizeDefined = true;
1080 float Controller::Impl::GetDefaultFontLineHeight()
1082 FontId defaultFontId = 0u;
1083 if( NULL == mFontDefaults )
1085 TextAbstraction::FontDescription fontDescription;
1086 defaultFontId = mFontClient.GetFontId( fontDescription );
1090 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1093 Text::FontMetrics fontMetrics;
1094 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1096 return( fontMetrics.ascender - fontMetrics.descender );
1099 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1101 if( NULL == mEventData )
1103 // Nothing to do if there is no text input.
1107 int keyCode = event.p1.mInt;
1109 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1111 if( mEventData->mPrimaryCursorPosition > 0u )
1113 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1116 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1118 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1120 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1123 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1125 // Get first the line index of the current cursor position index.
1126 CharacterIndex characterIndex = 0u;
1128 if( mEventData->mPrimaryCursorPosition > 0u )
1130 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1133 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1135 if( lineIndex > 0u )
1137 // Retrieve the cursor position info.
1138 CursorInfo cursorInfo;
1139 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1142 // Get the line above.
1143 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1145 // Get the next hit 'y' point.
1146 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1148 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1149 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1152 mEventData->mCursorHookPositionX,
1156 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1158 // Get first the line index of the current cursor position index.
1159 CharacterIndex characterIndex = 0u;
1161 if( mEventData->mPrimaryCursorPosition > 0u )
1163 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1166 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1168 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1170 // Retrieve the cursor position info.
1171 CursorInfo cursorInfo;
1172 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1175 // Get the line below.
1176 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1178 // Get the next hit 'y' point.
1179 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1181 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1182 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1185 mEventData->mCursorHookPositionX,
1190 mEventData->mUpdateCursorPosition = true;
1191 mEventData->mUpdateInputStyle = true;
1192 mEventData->mScrollAfterUpdatePosition = true;
1195 void Controller::Impl::OnTapEvent( const Event& event )
1197 if( NULL != mEventData )
1199 const unsigned int tapCount = event.p1.mUint;
1201 if( 1u == tapCount )
1203 if( IsShowingRealText() )
1205 // Convert from control's coords to text's coords.
1206 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1207 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1209 // Keep the tap 'x' position. Used to move the cursor.
1210 mEventData->mCursorHookPositionX = xPosition;
1212 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1218 // When the cursor position is changing, delay cursor blinking
1219 mEventData->mDecorator->DelayCursorBlink();
1223 mEventData->mPrimaryCursorPosition = 0u;
1226 mEventData->mUpdateCursorPosition = true;
1227 mEventData->mUpdateGrabHandlePosition = true;
1228 mEventData->mScrollAfterUpdatePosition = true;
1229 mEventData->mUpdateInputStyle = true;
1231 // Notify the cursor position to the imf manager.
1232 if( mEventData->mImfManager )
1234 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1235 mEventData->mImfManager.NotifyCursorPosition();
1241 void Controller::Impl::OnPanEvent( const Event& event )
1243 if( NULL == mEventData )
1245 // Nothing to do if there is no text input.
1249 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1250 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1252 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1254 // Nothing to do if scrolling is not enabled.
1258 const int state = event.p1.mInt;
1262 case Gesture::Started:
1264 // Will remove the cursor, handles or text's popup, ...
1265 ChangeState( EventData::TEXT_PANNING );
1268 case Gesture::Continuing:
1270 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1271 const Vector2 currentScroll = mScrollPosition;
1273 if( isHorizontalScrollEnabled )
1275 const float displacementX = event.p2.mFloat;
1276 mScrollPosition.x += displacementX;
1278 ClampHorizontalScroll( layoutSize );
1281 if( isVerticalScrollEnabled )
1283 const float displacementY = event.p3.mFloat;
1284 mScrollPosition.y += displacementY;
1286 ClampVerticalScroll( layoutSize );
1289 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1292 case Gesture::Finished:
1293 case Gesture::Cancelled: // FALLTHROUGH
1295 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1296 ChangeState( mEventData->mPreviousState );
1304 void Controller::Impl::OnLongPressEvent( const Event& event )
1306 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1308 if( EventData::EDITING == mEventData->mState )
1310 ChangeState ( EventData::EDITING_WITH_POPUP );
1311 mEventData->mDecoratorUpdated = true;
1315 void Controller::Impl::OnHandleEvent( const Event& event )
1317 if( NULL == mEventData )
1319 // Nothing to do if there is no text input.
1323 const unsigned int state = event.p1.mUint;
1324 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1325 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1327 if( HANDLE_PRESSED == state )
1329 // Convert from decorator's coords to text's coords.
1330 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1331 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1333 // Need to calculate the handle's new position.
1334 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1340 if( Event::GRAB_HANDLE_EVENT == event.type )
1342 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1344 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1346 // Updates the cursor position if the handle's new position is different than the current one.
1347 mEventData->mUpdateCursorPosition = true;
1348 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1349 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1350 mEventData->mPrimaryCursorPosition = handleNewPosition;
1353 // 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.
1354 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1356 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1358 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1360 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1361 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1363 // Updates the highlight box if the handle's new position is different than the current one.
1364 mEventData->mUpdateHighlightBox = true;
1365 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1366 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1367 mEventData->mLeftSelectionPosition = handleNewPosition;
1370 // 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.
1371 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1373 // Will define the order to scroll the text to match the handle position.
1374 mEventData->mIsLeftHandleSelected = true;
1376 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1378 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1380 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1381 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1383 // Updates the highlight box if the handle's new position is different than the current one.
1384 mEventData->mUpdateHighlightBox = true;
1385 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1386 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1387 mEventData->mRightSelectionPosition = handleNewPosition;
1390 // 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.
1391 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1393 // Will define the order to scroll the text to match the handle position.
1394 mEventData->mIsLeftHandleSelected = false;
1396 } // end ( HANDLE_PRESSED == state )
1397 else if( ( HANDLE_RELEASED == state ) ||
1398 handleStopScrolling )
1400 CharacterIndex handlePosition = 0u;
1401 if( handleStopScrolling || isSmoothHandlePanEnabled )
1403 // Convert from decorator's coords to text's coords.
1404 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1405 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1407 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1414 if( Event::GRAB_HANDLE_EVENT == event.type )
1416 mEventData->mUpdateCursorPosition = true;
1417 mEventData->mUpdateGrabHandlePosition = true;
1418 mEventData->mUpdateInputStyle = true;
1420 if( !IsClipboardEmpty() )
1422 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1425 if( handleStopScrolling || isSmoothHandlePanEnabled )
1427 mEventData->mScrollAfterUpdatePosition = true;
1428 mEventData->mPrimaryCursorPosition = handlePosition;
1431 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1433 ChangeState( EventData::SELECTING );
1435 mEventData->mUpdateHighlightBox = true;
1436 mEventData->mUpdateLeftSelectionPosition = true;
1437 mEventData->mUpdateRightSelectionPosition = true;
1439 if( handleStopScrolling || isSmoothHandlePanEnabled )
1441 mEventData->mScrollAfterUpdatePosition = true;
1443 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1444 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1446 mEventData->mLeftSelectionPosition = handlePosition;
1450 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1452 ChangeState( EventData::SELECTING );
1454 mEventData->mUpdateHighlightBox = true;
1455 mEventData->mUpdateRightSelectionPosition = true;
1456 mEventData->mUpdateLeftSelectionPosition = true;
1458 if( handleStopScrolling || isSmoothHandlePanEnabled )
1460 mEventData->mScrollAfterUpdatePosition = true;
1461 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1462 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1464 mEventData->mRightSelectionPosition = handlePosition;
1469 mEventData->mDecoratorUpdated = true;
1470 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1471 else if( HANDLE_SCROLLING == state )
1473 const float xSpeed = event.p2.mFloat;
1474 const float ySpeed = event.p3.mFloat;
1475 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1476 const Vector2 currentScrollPosition = mScrollPosition;
1478 mScrollPosition.x += xSpeed;
1479 mScrollPosition.y += ySpeed;
1481 ClampHorizontalScroll( layoutSize );
1482 ClampVerticalScroll( layoutSize );
1484 bool endOfScroll = false;
1485 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1487 // Notify the decorator there is no more text to scroll.
1488 // The decorator won't send more scroll events.
1489 mEventData->mDecorator->NotifyEndOfScroll();
1490 // Still need to set the position of the handle.
1494 // Set the position of the handle.
1495 const bool scrollRightDirection = xSpeed > 0.f;
1496 const bool scrollBottomDirection = ySpeed > 0.f;
1497 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1498 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1500 if( Event::GRAB_HANDLE_EVENT == event.type )
1502 ChangeState( EventData::GRAB_HANDLE_PANNING );
1504 // Get the grab handle position in decorator coords.
1505 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1507 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1509 // Position the grag handle close to either the left or right edge.
1510 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1513 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1515 position.x = mEventData->mCursorHookPositionX;
1517 // Position the grag handle close to either the top or bottom edge.
1518 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1521 // Get the new handle position.
1522 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1523 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1526 position.x - mScrollPosition.x,
1527 position.y - mScrollPosition.y );
1529 if( mEventData->mPrimaryCursorPosition != handlePosition )
1531 mEventData->mUpdateCursorPosition = true;
1532 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1533 mEventData->mScrollAfterUpdatePosition = true;
1534 mEventData->mPrimaryCursorPosition = handlePosition;
1536 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1538 // Updates the decorator if the soft handle panning is enabled.
1539 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1541 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1543 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1545 // Get the selection handle position in decorator coords.
1546 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1548 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1550 // Position the selection handle close to either the left or right edge.
1551 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1554 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1556 position.x = mEventData->mCursorHookPositionX;
1558 // Position the grag handle close to either the top or bottom edge.
1559 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1562 // Get the new handle position.
1563 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1564 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1567 position.x - mScrollPosition.x,
1568 position.y - mScrollPosition.y );
1570 if( leftSelectionHandleEvent )
1572 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1574 if( differentHandles || endOfScroll )
1576 mEventData->mUpdateHighlightBox = true;
1577 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1578 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1579 mEventData->mLeftSelectionPosition = handlePosition;
1584 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1585 if( differentHandles || endOfScroll )
1587 mEventData->mUpdateHighlightBox = true;
1588 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1589 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1590 mEventData->mRightSelectionPosition = handlePosition;
1594 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1596 RepositionSelectionHandles();
1598 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1601 mEventData->mDecoratorUpdated = true;
1602 } // end ( HANDLE_SCROLLING == state )
1605 void Controller::Impl::OnSelectEvent( const Event& event )
1607 if( NULL == mEventData )
1609 // Nothing to do if there is no text.
1613 if( mEventData->mSelectionEnabled )
1615 // Convert from control's coords to text's coords.
1616 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1617 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1619 // Calculates the logical position from the x,y coords.
1620 RepositionSelectionHandles( xPosition,
1625 void Controller::Impl::OnSelectAllEvent()
1627 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1629 if( NULL == mEventData )
1631 // Nothing to do if there is no text.
1635 if( mEventData->mSelectionEnabled )
1637 ChangeState( EventData::SELECTING );
1639 mEventData->mLeftSelectionPosition = 0u;
1640 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1642 mEventData->mScrollAfterUpdatePosition = true;
1643 mEventData->mUpdateLeftSelectionPosition = true;
1644 mEventData->mUpdateRightSelectionPosition = true;
1645 mEventData->mUpdateHighlightBox = true;
1649 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1651 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1653 // Nothing to select if handles are in the same place.
1654 selectedText.clear();
1658 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1660 //Get start and end position of selection
1661 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1662 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1664 Vector<Character>& utf32Characters = mLogicalModel->mText;
1665 const Length numberOfCharacters = utf32Characters.Count();
1667 // Validate the start and end selection points
1668 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1670 //Get text as a UTF8 string
1671 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1673 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1675 // Keep a copy of the current input style.
1676 InputStyle currentInputStyle;
1677 currentInputStyle.Copy( mEventData->mInputStyle );
1679 // Set as input style the style of the first deleted character.
1680 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1682 // Compare if the input style has changed.
1683 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1685 if( hasInputStyleChanged )
1687 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1688 // Queue the input style changed signal.
1689 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1692 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1694 // Mark the paragraphs to be updated.
1695 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1696 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1698 // Delete text between handles
1699 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1700 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1701 utf32Characters.Erase( first, last );
1703 // Will show the cursor at the first character of the selection.
1704 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1708 // Will show the cursor at the last character of the selection.
1709 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1712 mEventData->mDecoratorUpdated = true;
1716 void Controller::Impl::ShowClipboard()
1720 mClipboard.ShowClipboard();
1724 void Controller::Impl::HideClipboard()
1726 if( mClipboard && mClipboardHideEnabled )
1728 mClipboard.HideClipboard();
1732 void Controller::Impl::SetClipboardHideEnable(bool enable)
1734 mClipboardHideEnabled = enable;
1737 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1739 //Send string to clipboard
1740 return ( mClipboard && mClipboard.SetItem( source ) );
1743 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1745 std::string selectedText;
1746 RetrieveSelection( selectedText, deleteAfterSending );
1747 CopyStringToClipboard( selectedText );
1748 ChangeState( EventData::EDITING );
1751 void Controller::Impl::RequestGetTextFromClipboard()
1755 mClipboard.RequestItem();
1759 void Controller::Impl::RepositionSelectionHandles()
1761 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1762 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1764 if( selectionStart == selectionEnd )
1766 // Nothing to select if handles are in the same place.
1770 mEventData->mDecorator->ClearHighlights();
1772 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1773 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1774 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1775 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1776 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1777 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1778 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1780 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1781 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1782 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1784 // Swap the indices if the start is greater than the end.
1785 const bool indicesSwapped = selectionStart > selectionEnd;
1787 // Tell the decorator to flip the selection handles if needed.
1788 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1790 if( indicesSwapped )
1792 std::swap( selectionStart, selectionEnd );
1795 // Get the indices to the first and last selected glyphs.
1796 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1797 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1798 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1799 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1801 // Get the lines where the glyphs are laid-out.
1802 const LineRun* lineRun = mVisualModel->mLines.Begin();
1804 LineIndex lineIndex = 0u;
1805 Length numberOfLines = 0u;
1806 mVisualModel->GetNumberOfLines( glyphStart,
1807 1u + glyphEnd - glyphStart,
1810 const LineIndex firstLineIndex = lineIndex;
1812 // Create the structure to store some selection box info.
1813 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1814 selectionBoxLinesInfo.Resize( numberOfLines );
1816 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1817 selectionBoxInfo->minX = MAX_FLOAT;
1818 selectionBoxInfo->maxX = MIN_FLOAT;
1820 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1821 float minHighlightX = std::numeric_limits<float>::max();
1822 float maxHighlightX = std::numeric_limits<float>::min();
1824 Vector2 highLightPosition; // The highlight position in decorator's coords.
1826 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1828 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1829 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1832 // Transform to decorator's (control) coords.
1833 selectionBoxInfo->lineOffset += mScrollPosition.y;
1835 lineRun += firstLineIndex;
1837 // The line height is the addition of the line ascender and the line descender.
1838 // However, the line descender has a negative value, hence the subtraction.
1839 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1841 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1843 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1844 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1845 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1847 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1848 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1849 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1851 // The number of quads of the selection box.
1852 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1853 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1855 // Count the actual number of quads.
1856 unsigned int actualNumberOfQuads = 0u;
1859 // Traverse the glyphs.
1860 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1862 const GlyphInfo& glyph = *( glyphsBuffer + index );
1863 const Vector2& position = *( positionsBuffer + index );
1865 if( splitStartGlyph )
1867 // 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.
1869 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1870 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1871 // Get the direction of the character.
1872 CharacterDirection isCurrentRightToLeft = false;
1873 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1875 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1878 // The end point could be in the middle of the ligature.
1879 // Calculate the number of characters selected.
1880 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1882 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1883 quad.y = selectionBoxInfo->lineOffset;
1884 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1885 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1887 // Store the min and max 'x' for each line.
1888 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1889 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1891 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1892 ++actualNumberOfQuads;
1894 splitStartGlyph = false;
1898 if( splitEndGlyph && ( index == glyphEnd ) )
1900 // 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.
1902 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1903 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1904 // Get the direction of the character.
1905 CharacterDirection isCurrentRightToLeft = false;
1906 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1908 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1911 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1913 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1914 quad.y = selectionBoxInfo->lineOffset;
1915 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1916 quad.w = quad.y + selectionBoxInfo->lineHeight;
1918 // Store the min and max 'x' for each line.
1919 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1920 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1922 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1924 ++actualNumberOfQuads;
1926 splitEndGlyph = false;
1930 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1931 quad.y = selectionBoxInfo->lineOffset;
1932 quad.z = quad.x + glyph.advance;
1933 quad.w = quad.y + selectionBoxInfo->lineHeight;
1935 // Store the min and max 'x' for each line.
1936 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1937 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1939 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1941 ++actualNumberOfQuads;
1943 // Whether to retrieve the next line.
1944 if( index == lastGlyphOfLine )
1946 // Retrieve the next line.
1949 // Get the last glyph of the new line.
1950 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1953 if( lineIndex < firstLineIndex + numberOfLines )
1955 // Keep the offset and height of the current selection box.
1956 const float currentLineOffset = selectionBoxInfo->lineOffset;
1957 const float currentLineHeight = selectionBoxInfo->lineHeight;
1959 // Get the selection box info for the next line.
1962 selectionBoxInfo->minX = MAX_FLOAT;
1963 selectionBoxInfo->maxX = MIN_FLOAT;
1965 // Update the line's vertical offset.
1966 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1968 // The line height is the addition of the line ascender and the line descender.
1969 // However, the line descender has a negative value, hence the subtraction.
1970 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1975 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1976 // The final width is calculated after 'boxifying' the selection.
1977 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1978 endIt = selectionBoxLinesInfo.End();
1982 const SelectionBoxInfo& info = *it;
1984 // Update the size of the highlighted text.
1985 highLightSize.height += info.lineHeight;
1986 minHighlightX = std::min( minHighlightX, info.minX );
1987 maxHighlightX = std::max( maxHighlightX, info.maxX );
1990 // Add extra geometry to 'boxify' the selection.
1992 if( 1u < numberOfLines )
1994 // Boxify the first line.
1995 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1996 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1998 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1999 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2004 quad.y = firstSelectionBoxLineInfo.lineOffset;
2005 quad.z = firstSelectionBoxLineInfo.minX;
2006 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2008 // Boxify at the beginning of the line.
2009 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2011 ++actualNumberOfQuads;
2013 // Update the size of the highlighted text.
2014 minHighlightX = 0.f;
2019 quad.x = firstSelectionBoxLineInfo.maxX;
2020 quad.y = firstSelectionBoxLineInfo.lineOffset;
2021 quad.z = mVisualModel->mControlSize.width;
2022 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2024 // Boxify at the end of the line.
2025 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2027 ++actualNumberOfQuads;
2029 // Update the size of the highlighted text.
2030 maxHighlightX = mVisualModel->mControlSize.width;
2033 // Boxify the central lines.
2034 if( 2u < numberOfLines )
2036 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2037 endIt = selectionBoxLinesInfo.End() - 1u;
2041 const SelectionBoxInfo& info = *it;
2044 quad.y = info.lineOffset;
2046 quad.w = info.lineOffset + info.lineHeight;
2048 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2050 ++actualNumberOfQuads;
2053 quad.y = info.lineOffset;
2054 quad.z = mVisualModel->mControlSize.width;
2055 quad.w = info.lineOffset + info.lineHeight;
2057 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2059 ++actualNumberOfQuads;
2062 // Update the size of the highlighted text.
2063 minHighlightX = 0.f;
2064 maxHighlightX = mVisualModel->mControlSize.width;
2067 // Boxify the last line.
2068 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2069 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2071 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2072 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2077 quad.y = lastSelectionBoxLineInfo.lineOffset;
2078 quad.z = lastSelectionBoxLineInfo.minX;
2079 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2081 // Boxify at the beginning of the line.
2082 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2084 ++actualNumberOfQuads;
2086 // Update the size of the highlighted text.
2087 minHighlightX = 0.f;
2092 quad.x = lastSelectionBoxLineInfo.maxX;
2093 quad.y = lastSelectionBoxLineInfo.lineOffset;
2094 quad.z = mVisualModel->mControlSize.width;
2095 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2097 // Boxify at the end of the line.
2098 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2100 ++actualNumberOfQuads;
2102 // Update the size of the highlighted text.
2103 maxHighlightX = mVisualModel->mControlSize.width;
2107 // Set the actual number of quads.
2108 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2110 // Sets the highlight's size and position. In decorator's coords.
2111 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2112 highLightSize.width = maxHighlightX - minHighlightX;
2114 highLightPosition.x = minHighlightX;
2115 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2116 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2118 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2120 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2122 CursorInfo primaryCursorInfo;
2123 GetCursorPosition( mEventData->mLeftSelectionPosition,
2124 primaryCursorInfo );
2126 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2128 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2130 primaryCursorInfo.lineOffset + mScrollPosition.y,
2131 primaryCursorInfo.lineHeight );
2133 CursorInfo secondaryCursorInfo;
2134 GetCursorPosition( mEventData->mRightSelectionPosition,
2135 secondaryCursorInfo );
2137 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2139 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2140 secondaryPosition.x,
2141 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2142 secondaryCursorInfo.lineHeight );
2145 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2146 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2148 // Set the flag to update the decorator.
2149 mEventData->mDecoratorUpdated = true;
2152 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2154 if( NULL == mEventData )
2156 // Nothing to do if there is no text input.
2160 if( IsShowingPlaceholderText() )
2162 // Nothing to do if there is the place-holder text.
2166 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2167 const Length numberOfLines = mVisualModel->mLines.Count();
2168 if( ( 0 == numberOfGlyphs ) ||
2169 ( 0 == numberOfLines ) )
2171 // Nothing to do if there is no text.
2175 // Find which word was selected
2176 CharacterIndex selectionStart( 0 );
2177 CharacterIndex selectionEnd( 0 );
2178 const bool indicesFound = FindSelectionIndices( mVisualModel,
2185 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2189 ChangeState( EventData::SELECTING );
2191 mEventData->mLeftSelectionPosition = selectionStart;
2192 mEventData->mRightSelectionPosition = selectionEnd;
2194 mEventData->mUpdateLeftSelectionPosition = true;
2195 mEventData->mUpdateRightSelectionPosition = true;
2196 mEventData->mUpdateHighlightBox = true;
2198 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2202 // Nothing to select. i.e. a white space, out of bounds
2203 ChangeState( EventData::EDITING );
2205 mEventData->mPrimaryCursorPosition = selectionEnd;
2207 mEventData->mUpdateCursorPosition = true;
2208 mEventData->mUpdateGrabHandlePosition = true;
2209 mEventData->mScrollAfterUpdatePosition = true;
2210 mEventData->mUpdateInputStyle = true;
2214 void Controller::Impl::SetPopupButtons()
2217 * Sets the Popup buttons to be shown depending on State.
2219 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2221 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2224 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2226 if( EventData::SELECTING == mEventData->mState )
2228 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2230 if( !IsClipboardEmpty() )
2232 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2233 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2236 if( !mEventData->mAllTextSelected )
2238 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2241 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2243 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2245 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2248 if( !IsClipboardEmpty() )
2250 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2251 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2254 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2256 if ( !IsClipboardEmpty() )
2258 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2259 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2263 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2266 void Controller::Impl::ChangeState( EventData::State newState )
2268 if( NULL == mEventData )
2270 // Nothing to do if there is no text input.
2274 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2276 if( mEventData->mState != newState )
2278 mEventData->mPreviousState = mEventData->mState;
2279 mEventData->mState = newState;
2281 switch( mEventData->mState )
2283 case EventData::INACTIVE:
2285 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2286 mEventData->mDecorator->StopCursorBlink();
2287 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2288 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2289 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2290 mEventData->mDecorator->SetHighlightActive( false );
2291 mEventData->mDecorator->SetPopupActive( false );
2292 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;
2305 case EventData::SELECTING:
2307 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2308 mEventData->mDecorator->StopCursorBlink();
2309 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2310 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2311 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2312 mEventData->mDecorator->SetHighlightActive( true );
2313 if( mEventData->mGrabHandlePopupEnabled )
2316 mEventData->mDecorator->SetPopupActive( true );
2318 mEventData->mDecoratorUpdated = true;
2321 case EventData::EDITING:
2323 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2324 if( mEventData->mCursorBlinkEnabled )
2326 mEventData->mDecorator->StartCursorBlink();
2328 // Grab handle is not shown until a tap is received whilst EDITING
2329 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2330 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2331 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2332 mEventData->mDecorator->SetHighlightActive( false );
2333 if( mEventData->mGrabHandlePopupEnabled )
2335 mEventData->mDecorator->SetPopupActive( false );
2337 mEventData->mDecoratorUpdated = true;
2340 case EventData::EDITING_WITH_POPUP:
2342 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2344 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2345 if( mEventData->mCursorBlinkEnabled )
2347 mEventData->mDecorator->StartCursorBlink();
2349 if( mEventData->mSelectionEnabled )
2351 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2352 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2353 mEventData->mDecorator->SetHighlightActive( false );
2357 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2359 if( mEventData->mGrabHandlePopupEnabled )
2362 mEventData->mDecorator->SetPopupActive( true );
2364 mEventData->mDecoratorUpdated = true;
2367 case EventData::EDITING_WITH_GRAB_HANDLE:
2369 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2371 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2372 if( mEventData->mCursorBlinkEnabled )
2374 mEventData->mDecorator->StartCursorBlink();
2376 // Grab handle is not shown until a tap is received whilst EDITING
2377 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2378 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2379 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2380 mEventData->mDecorator->SetHighlightActive( false );
2381 if( mEventData->mGrabHandlePopupEnabled )
2383 mEventData->mDecorator->SetPopupActive( false );
2385 mEventData->mDecoratorUpdated = true;
2388 case EventData::SELECTION_HANDLE_PANNING:
2390 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2391 mEventData->mDecorator->StopCursorBlink();
2392 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2393 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2394 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2395 mEventData->mDecorator->SetHighlightActive( true );
2396 if( mEventData->mGrabHandlePopupEnabled )
2398 mEventData->mDecorator->SetPopupActive( false );
2400 mEventData->mDecoratorUpdated = true;
2403 case EventData::GRAB_HANDLE_PANNING:
2405 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2407 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2408 if( mEventData->mCursorBlinkEnabled )
2410 mEventData->mDecorator->StartCursorBlink();
2412 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2413 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2414 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2415 mEventData->mDecorator->SetHighlightActive( false );
2416 if( mEventData->mGrabHandlePopupEnabled )
2418 mEventData->mDecorator->SetPopupActive( false );
2420 mEventData->mDecoratorUpdated = true;
2423 case EventData::EDITING_WITH_PASTE_POPUP:
2425 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2427 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2428 if( mEventData->mCursorBlinkEnabled )
2430 mEventData->mDecorator->StartCursorBlink();
2433 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2434 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2435 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2436 mEventData->mDecorator->SetHighlightActive( false );
2438 if( mEventData->mGrabHandlePopupEnabled )
2441 mEventData->mDecorator->SetPopupActive( true );
2443 mEventData->mDecoratorUpdated = true;
2446 case EventData::TEXT_PANNING:
2448 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2449 mEventData->mDecorator->StopCursorBlink();
2450 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2451 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2452 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2454 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2455 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2456 mEventData->mDecorator->SetHighlightActive( true );
2459 if( mEventData->mGrabHandlePopupEnabled )
2461 mEventData->mDecorator->SetPopupActive( false );
2464 mEventData->mDecoratorUpdated = true;
2471 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2472 CursorInfo& cursorInfo )
2474 if( !IsShowingRealText() )
2476 // Do not want to use the place-holder text to set the cursor position.
2478 // Use the line's height of the font's family set to set the cursor's size.
2479 // If there is no font's family set, use the default font.
2480 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2482 cursorInfo.lineOffset = 0.f;
2483 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2484 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2486 switch( mLayoutEngine.GetHorizontalAlignment() )
2488 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2490 cursorInfo.primaryPosition.x = 0.f;
2493 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2495 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2498 case LayoutEngine::HORIZONTAL_ALIGN_END:
2500 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2505 // Nothing else to do.
2509 Text::GetCursorPosition( mVisualModel,
2515 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2517 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2519 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2520 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2522 if( 0.f > cursorInfo.primaryPosition.x )
2524 cursorInfo.primaryPosition.x = 0.f;
2527 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2528 if( cursorInfo.primaryPosition.x > edgeWidth )
2530 cursorInfo.primaryPosition.x = edgeWidth;
2535 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2537 if( NULL == mEventData )
2539 // Nothing to do if there is no text input.
2543 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2545 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2546 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2548 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2549 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2551 if( numberOfCharacters > 1u )
2553 const Script script = mLogicalModel->GetScript( index );
2554 if( HasLigatureMustBreak( script ) )
2556 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2557 numberOfCharacters = 1u;
2562 while( 0u == numberOfCharacters )
2565 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2569 if( index < mEventData->mPrimaryCursorPosition )
2571 cursorIndex -= numberOfCharacters;
2575 cursorIndex += numberOfCharacters;
2578 // Will update the cursor hook position.
2579 mEventData->mUpdateCursorHookPosition = true;
2584 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2586 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2587 if( NULL == mEventData )
2589 // Nothing to do if there is no text input.
2590 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2594 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2596 // Sets the cursor position.
2597 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2600 cursorInfo.primaryCursorHeight,
2601 cursorInfo.lineHeight );
2602 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2604 if( mEventData->mUpdateGrabHandlePosition )
2606 // Sets the grab handle position.
2607 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2609 cursorInfo.lineOffset + mScrollPosition.y,
2610 cursorInfo.lineHeight );
2613 if( cursorInfo.isSecondaryCursor )
2615 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2616 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2617 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2618 cursorInfo.secondaryCursorHeight,
2619 cursorInfo.lineHeight );
2620 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2623 // Set which cursors are active according the state.
2624 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2626 if( cursorInfo.isSecondaryCursor )
2628 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2632 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2637 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2640 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2643 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2644 const CursorInfo& cursorInfo )
2646 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2647 ( RIGHT_SELECTION_HANDLE != handleType ) )
2652 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2654 // Sets the handle's position.
2655 mEventData->mDecorator->SetPosition( handleType,
2657 cursorInfo.lineOffset + mScrollPosition.y,
2658 cursorInfo.lineHeight );
2660 // If selection handle at start of the text and other at end of the text then all text is selected.
2661 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2662 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2663 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2666 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2668 // Clamp between -space & 0.
2670 if( layoutSize.width > mVisualModel->mControlSize.width )
2672 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2673 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2674 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2676 mEventData->mDecoratorUpdated = true;
2680 mScrollPosition.x = 0.f;
2684 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2686 // Clamp between -space & 0.
2687 if( layoutSize.height > mVisualModel->mControlSize.height )
2689 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2690 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2691 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2693 mEventData->mDecoratorUpdated = true;
2697 mScrollPosition.y = 0.f;
2701 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2703 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2705 // position is in actor's coords.
2706 const float positionEndX = position.x + cursorWidth;
2707 const float positionEndY = position.y + lineHeight;
2709 // Transform the position to decorator coords.
2710 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2711 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2713 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2714 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2716 if( decoratorPositionBeginX < 0.f )
2718 mScrollPosition.x = -position.x;
2720 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2722 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2725 if( decoratorPositionBeginY < 0.f )
2727 mScrollPosition.y = -position.y;
2729 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2731 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2735 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2737 // Get the current cursor position in decorator coords.
2738 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2740 // Calculate the offset to match the cursor position before the character was deleted.
2741 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2742 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2744 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2745 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2747 // Makes the new cursor position visible if needed.
2748 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2751 void Controller::Impl::RequestRelayout()
2753 if( NULL != mControlInterface )
2755 mControlInterface->RequestTextRelayout();
2761 } // namespace Toolkit