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 mUpdateCursorHookPosition( false ),
93 mUpdateCursorPosition( false ),
94 mUpdateGrabHandlePosition( false ),
95 mUpdateLeftSelectionPosition( false ),
96 mUpdateRightSelectionPosition( false ),
97 mIsLeftHandleSelected( false ),
98 mUpdateHighlightBox( false ),
99 mScrollAfterUpdatePosition( false ),
100 mScrollAfterDelete( false ),
101 mAllTextSelected( false ),
102 mUpdateInputStyle( false )
104 mImfManager = ImfManager::Get();
107 EventData::~EventData()
110 bool Controller::Impl::ProcessInputEvents()
112 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
113 if( NULL == mEventData )
115 // Nothing to do if there is no text input.
116 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
120 if( mEventData->mDecorator )
122 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
123 iter != mEventData->mEventQueue.end();
128 case Event::CURSOR_KEY_EVENT:
130 OnCursorKeyEvent( *iter );
133 case Event::TAP_EVENT:
138 case Event::LONG_PRESS_EVENT:
140 OnLongPressEvent( *iter );
143 case Event::PAN_EVENT:
148 case Event::GRAB_HANDLE_EVENT:
149 case Event::LEFT_SELECTION_HANDLE_EVENT:
150 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
152 OnHandleEvent( *iter );
157 OnSelectEvent( *iter );
160 case Event::SELECT_ALL:
169 if( mEventData->mUpdateCursorPosition ||
170 mEventData->mUpdateHighlightBox )
175 // The cursor must also be repositioned after inserts into the model
176 if( mEventData->mUpdateCursorPosition )
178 // Updates the cursor position and scrolls the text to make it visible.
179 CursorInfo cursorInfo;
180 // Calculate the cursor position from the new cursor index.
181 GetCursorPosition( mEventData->mPrimaryCursorPosition,
184 if( mEventData->mUpdateCursorHookPosition )
186 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
187 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
188 mEventData->mUpdateCursorHookPosition = false;
191 // Scroll first the text after delete ...
192 if( mEventData->mScrollAfterDelete )
194 ScrollTextToMatchCursor( cursorInfo );
197 // ... then, text can be scrolled to make the cursor visible.
198 if( mEventData->mScrollAfterUpdatePosition )
200 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
201 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
203 mEventData->mScrollAfterUpdatePosition = false;
204 mEventData->mScrollAfterDelete = false;
206 UpdateCursorPosition( cursorInfo );
208 mEventData->mDecoratorUpdated = true;
209 mEventData->mUpdateCursorPosition = false;
210 mEventData->mUpdateGrabHandlePosition = false;
214 CursorInfo leftHandleInfo;
215 CursorInfo rightHandleInfo;
217 if( mEventData->mUpdateHighlightBox )
219 GetCursorPosition( mEventData->mLeftSelectionPosition,
222 GetCursorPosition( mEventData->mRightSelectionPosition,
225 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
227 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
229 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
230 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
234 if( mEventData->mUpdateLeftSelectionPosition )
236 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
240 mEventData->mDecoratorUpdated = true;
241 mEventData->mUpdateLeftSelectionPosition = false;
244 if( mEventData->mUpdateRightSelectionPosition )
246 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
250 mEventData->mDecoratorUpdated = true;
251 mEventData->mUpdateRightSelectionPosition = false;
254 if( mEventData->mUpdateHighlightBox )
256 RepositionSelectionHandles();
258 mEventData->mUpdateLeftSelectionPosition = false;
259 mEventData->mUpdateRightSelectionPosition = false;
260 mEventData->mUpdateHighlightBox = false;
263 mEventData->mScrollAfterUpdatePosition = false;
266 if( mEventData->mUpdateInputStyle )
268 // Keep a copy of the current input style.
269 InputStyle currentInputStyle;
270 currentInputStyle.Copy( mEventData->mInputStyle );
272 // Set the default style first.
273 RetrieveDefaultInputStyle( mEventData->mInputStyle );
275 // Get the character index from the cursor index.
276 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
278 // Retrieve the style from the style runs stored in the logical model.
279 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
281 // Compare if the input style has changed.
282 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
284 if( hasInputStyleChanged )
286 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
287 // Queue the input style changed signal.
288 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
291 mEventData->mUpdateInputStyle = false;
294 mEventData->mEventQueue.clear();
296 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
298 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
299 mEventData->mDecoratorUpdated = false;
301 return decoratorUpdated;
304 void Controller::Impl::NotifyImfManager()
306 if( mEventData && mEventData->mImfManager )
308 CharacterIndex cursorPosition = GetLogicalCursorPosition();
310 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
312 // Update the cursor position by removing the initial white spaces.
313 if( cursorPosition < numberOfWhiteSpaces )
319 cursorPosition -= numberOfWhiteSpaces;
322 mEventData->mImfManager.SetCursorPosition( cursorPosition );
323 mEventData->mImfManager.NotifyCursorPosition();
327 void Controller::Impl::NotifyImfMultiLineStatus()
331 LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
332 mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
336 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
338 CharacterIndex cursorPosition = 0u;
342 if( ( EventData::SELECTING == mEventData->mState ) ||
343 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
345 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
349 cursorPosition = mEventData->mPrimaryCursorPosition;
353 return cursorPosition;
356 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
358 Length numberOfWhiteSpaces = 0u;
360 // Get the buffer to the text.
361 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
363 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
364 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
366 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
372 return numberOfWhiteSpaces;
375 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
377 // Get the total number of characters.
378 Length numberOfCharacters = mLogicalModel->mText.Count();
380 // Retrieve the text.
381 if( 0u != numberOfCharacters )
383 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
387 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
389 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
390 mTextUpdateInfo.mStartGlyphIndex = 0u;
391 mTextUpdateInfo.mStartLineIndex = 0u;
392 numberOfCharacters = 0u;
394 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
395 if( 0u == numberOfParagraphs )
397 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
398 numberOfCharacters = 0u;
400 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
402 // Nothing else to do if there are no paragraphs.
406 // Find the paragraphs to be updated.
407 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
408 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
410 // Text is being added at the end of the current text.
411 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
413 // Text is being added in a new paragraph after the last character of the text.
414 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
415 numberOfCharacters = 0u;
416 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
418 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
419 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
421 // Nothing else to do;
425 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
429 Length numberOfCharactersToUpdate = 0u;
430 if( mTextUpdateInfo.mFullRelayoutNeeded )
432 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
436 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
438 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
439 numberOfCharactersToUpdate,
440 paragraphsToBeUpdated );
443 if( 0u != paragraphsToBeUpdated.Count() )
445 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
446 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
447 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
449 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
450 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
452 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
453 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
454 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
455 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
457 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
458 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
460 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
464 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
468 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
469 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
472 void Controller::Impl::ClearFullModelData( OperationsMask operations )
474 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
476 mLogicalModel->mLineBreakInfo.Clear();
477 mLogicalModel->mParagraphInfo.Clear();
480 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
482 mLogicalModel->mLineBreakInfo.Clear();
485 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
487 mLogicalModel->mScriptRuns.Clear();
490 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
492 mLogicalModel->mFontRuns.Clear();
495 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
497 if( NO_OPERATION != ( BIDI_INFO & operations ) )
499 mLogicalModel->mBidirectionalParagraphInfo.Clear();
500 mLogicalModel->mCharacterDirections.Clear();
503 if( NO_OPERATION != ( REORDER & operations ) )
505 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
506 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
507 endIt = mLogicalModel->mBidirectionalLineInfo.End();
511 BidirectionalLineInfoRun& bidiLineInfo = *it;
513 free( bidiLineInfo.visualToLogicalMap );
514 bidiLineInfo.visualToLogicalMap = NULL;
516 mLogicalModel->mBidirectionalLineInfo.Clear();
520 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
522 mVisualModel->mGlyphs.Clear();
523 mVisualModel->mGlyphsToCharacters.Clear();
524 mVisualModel->mCharactersToGlyph.Clear();
525 mVisualModel->mCharactersPerGlyph.Clear();
526 mVisualModel->mGlyphsPerCharacter.Clear();
527 mVisualModel->mGlyphPositions.Clear();
530 if( NO_OPERATION != ( LAYOUT & operations ) )
532 mVisualModel->mLines.Clear();
535 if( NO_OPERATION != ( COLOR & operations ) )
537 mVisualModel->mColorIndices.Clear();
541 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
543 const CharacterIndex endIndexPlusOne = endIndex + 1u;
545 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
547 // Clear the line break info.
548 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
550 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
551 lineBreakInfoBuffer + endIndexPlusOne );
553 // Clear the paragraphs.
554 ClearCharacterRuns( startIndex,
556 mLogicalModel->mParagraphInfo );
559 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
561 // Clear the word break info.
562 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
564 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
565 wordBreakInfoBuffer + endIndexPlusOne );
568 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
570 // Clear the scripts.
571 ClearCharacterRuns( startIndex,
573 mLogicalModel->mScriptRuns );
576 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
579 ClearCharacterRuns( startIndex,
581 mLogicalModel->mFontRuns );
584 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
586 if( NO_OPERATION != ( BIDI_INFO & operations ) )
588 // Clear the bidirectional paragraph info.
589 ClearCharacterRuns( startIndex,
591 mLogicalModel->mBidirectionalParagraphInfo );
593 // Clear the character's directions.
594 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
596 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
597 characterDirectionsBuffer + endIndexPlusOne );
600 if( NO_OPERATION != ( REORDER & operations ) )
602 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
603 uint32_t endRemoveIndex = startRemoveIndex;
604 ClearCharacterRuns( startIndex,
606 mLogicalModel->mBidirectionalLineInfo,
610 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
612 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
613 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
614 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
618 BidirectionalLineInfoRun& bidiLineInfo = *it;
620 free( bidiLineInfo.visualToLogicalMap );
621 bidiLineInfo.visualToLogicalMap = NULL;
624 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
625 bidirectionalLineInfoBuffer + endRemoveIndex );
630 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
632 const CharacterIndex endIndexPlusOne = endIndex + 1u;
633 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
635 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
636 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
637 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
639 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
640 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
642 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
644 // Update the character to glyph indices.
645 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
646 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
650 CharacterIndex& index = *it;
651 index -= numberOfGlyphsRemoved;
654 // Clear the character to glyph conversion table.
655 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
656 charactersToGlyphBuffer + endIndexPlusOne );
658 // Clear the glyphs per character table.
659 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
660 glyphsPerCharacterBuffer + endIndexPlusOne );
662 // Clear the glyphs buffer.
663 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
664 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
665 glyphsBuffer + endGlyphIndexPlusOne );
667 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
669 // Update the glyph to character indices.
670 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
671 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
675 CharacterIndex& index = *it;
676 index -= numberOfCharactersRemoved;
679 // Clear the glyphs to characters buffer.
680 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
681 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
683 // Clear the characters per glyph buffer.
684 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
685 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
686 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
688 // Clear the positions buffer.
689 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
690 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
691 positionsBuffer + endGlyphIndexPlusOne );
694 if( NO_OPERATION != ( LAYOUT & operations ) )
697 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
698 uint32_t endRemoveIndex = startRemoveIndex;
699 ClearCharacterRuns( startIndex,
701 mVisualModel->mLines,
705 // Will update the glyph runs.
706 startRemoveIndex = mVisualModel->mLines.Count();
707 endRemoveIndex = startRemoveIndex;
708 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
709 endGlyphIndexPlusOne - 1u,
710 mVisualModel->mLines,
714 // Set the line index from where to insert the new laid-out lines.
715 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
717 LineRun* linesBuffer = mVisualModel->mLines.Begin();
718 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
719 linesBuffer + endRemoveIndex );
722 if( NO_OPERATION != ( COLOR & operations ) )
724 if( 0u != mVisualModel->mColorIndices.Count() )
726 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
727 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
728 colorIndexBuffer + endGlyphIndexPlusOne );
733 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
735 if( mTextUpdateInfo.mClearAll ||
736 ( ( 0u == startIndex ) &&
737 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
739 ClearFullModelData( operations );
743 // Clear the model data related with characters.
744 ClearCharacterModelData( startIndex, endIndex, operations );
746 // Clear the model data related with glyphs.
747 ClearGlyphModelData( startIndex, endIndex, operations );
750 // The estimated number of lines. Used to avoid reallocations when layouting.
751 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
753 mVisualModel->ClearCaches();
756 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
758 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
760 // Calculate the operations to be done.
761 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
763 if( NO_OPERATION == operations )
765 // Nothing to do if no operations are pending and required.
769 Vector<Character>& utf32Characters = mLogicalModel->mText;
771 const Length numberOfCharacters = utf32Characters.Count();
773 // Index to the first character of the first paragraph to be updated.
774 CharacterIndex startIndex = 0u;
775 // Number of characters of the paragraphs to be removed.
776 Length paragraphCharacters = 0u;
778 CalculateTextUpdateIndices( paragraphCharacters );
779 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
781 if( mTextUpdateInfo.mClearAll ||
782 ( 0u != paragraphCharacters ) )
784 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
787 mTextUpdateInfo.mClearAll = false;
789 // Whether the model is updated.
790 bool updated = false;
792 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
793 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
795 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
797 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
798 // calculate the bidirectional info for each 'paragraph'.
799 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
800 // is not shaped together).
801 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
803 SetLineBreakInfo( utf32Characters,
805 requestedNumberOfCharacters,
808 // Create the paragraph info.
809 mLogicalModel->CreateParagraphInfo( startIndex,
810 requestedNumberOfCharacters );
814 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
815 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
817 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
818 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
820 SetWordBreakInfo( utf32Characters,
822 requestedNumberOfCharacters,
827 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
828 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
830 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
831 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
833 if( getScripts || validateFonts )
835 // Validates the fonts assigned by the application or assigns default ones.
836 // It makes sure all the characters are going to be rendered by the correct font.
837 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
841 // Retrieves the scripts used in the text.
842 multilanguageSupport.SetScripts( utf32Characters,
844 requestedNumberOfCharacters,
850 // Validate the fonts set through the mark-up string.
851 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
853 // Get the default font's description.
854 TextAbstraction::FontDescription defaultFontDescription;
855 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
856 if( NULL != mFontDefaults )
858 defaultFontDescription = mFontDefaults->mFontDescription;
859 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
862 // Validates the fonts. If there is a character with no assigned font it sets a default one.
863 // After this call, fonts are validated.
864 multilanguageSupport.ValidateFonts( utf32Characters,
867 defaultFontDescription,
870 requestedNumberOfCharacters,
876 Vector<Character> mirroredUtf32Characters;
877 bool textMirrored = false;
878 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
879 if( NO_OPERATION != ( BIDI_INFO & operations ) )
881 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
882 bidirectionalInfo.Reserve( numberOfParagraphs );
884 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
885 SetBidirectionalInfo( utf32Characters,
889 requestedNumberOfCharacters,
892 if( 0u != bidirectionalInfo.Count() )
894 // Only set the character directions if there is right to left characters.
895 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
896 GetCharactersDirection( bidirectionalInfo,
899 requestedNumberOfCharacters,
902 // This paragraph has right to left text. Some characters may need to be mirrored.
903 // TODO: consider if the mirrored string can be stored as well.
905 textMirrored = GetMirroredText( utf32Characters,
909 requestedNumberOfCharacters,
910 mirroredUtf32Characters );
914 // There is no right to left characters. Clear the directions vector.
915 mLogicalModel->mCharacterDirections.Clear();
920 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
921 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
922 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
923 Vector<GlyphIndex> newParagraphGlyphs;
924 newParagraphGlyphs.Reserve( numberOfParagraphs );
926 const Length currentNumberOfGlyphs = glyphs.Count();
927 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
929 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
931 ShapeText( textToShape,
936 mTextUpdateInfo.mStartGlyphIndex,
937 requestedNumberOfCharacters,
939 glyphsToCharactersMap,
941 newParagraphGlyphs );
943 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
944 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
945 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
949 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
951 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
953 GlyphInfo* glyphsBuffer = glyphs.Begin();
954 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
956 // Update the width and advance of all new paragraph characters.
957 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
959 const GlyphIndex index = *it;
960 GlyphInfo& glyph = *( glyphsBuffer + index );
962 glyph.xBearing = 0.f;
969 if( NO_OPERATION != ( COLOR & operations ) )
971 // Set the color runs in glyphs.
972 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
973 mVisualModel->mCharactersToGlyph,
974 mVisualModel->mGlyphsPerCharacter,
976 mTextUpdateInfo.mStartGlyphIndex,
977 requestedNumberOfCharacters,
978 mVisualModel->mColors,
979 mVisualModel->mColorIndices );
984 if( ( NULL != mEventData ) &&
985 mEventData->mPreEditFlag &&
986 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
988 // Add the underline for the pre-edit text.
989 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
990 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
992 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
993 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
994 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
995 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
997 GlyphRun underlineRun;
998 underlineRun.glyphIndex = glyphStart;
999 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1001 // TODO: At the moment the underline runs are only for pre-edit.
1002 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1005 // The estimated number of lines. Used to avoid reallocations when layouting.
1006 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
1008 // Set the previous number of characters for the next time the text is updated.
1009 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1014 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1016 // Sets the default text's color.
1017 inputStyle.textColor = mTextColor;
1018 inputStyle.isDefaultColor = true;
1020 inputStyle.familyName.clear();
1021 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1022 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1023 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1024 inputStyle.size = 0.f;
1026 inputStyle.lineSpacing = 0.f;
1028 inputStyle.underlineProperties.clear();
1029 inputStyle.shadowProperties.clear();
1030 inputStyle.embossProperties.clear();
1031 inputStyle.outlineProperties.clear();
1033 inputStyle.isFamilyDefined = false;
1034 inputStyle.isWeightDefined = false;
1035 inputStyle.isWidthDefined = false;
1036 inputStyle.isSlantDefined = false;
1037 inputStyle.isSizeDefined = false;
1039 inputStyle.isLineSpacingDefined = false;
1041 inputStyle.isUnderlineDefined = false;
1042 inputStyle.isShadowDefined = false;
1043 inputStyle.isEmbossDefined = false;
1044 inputStyle.isOutlineDefined = false;
1046 // Sets the default font's family name, weight, width, slant and size.
1049 if( mFontDefaults->familyDefined )
1051 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1052 inputStyle.isFamilyDefined = true;
1055 if( mFontDefaults->weightDefined )
1057 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1058 inputStyle.isWeightDefined = true;
1061 if( mFontDefaults->widthDefined )
1063 inputStyle.width = mFontDefaults->mFontDescription.width;
1064 inputStyle.isWidthDefined = true;
1067 if( mFontDefaults->slantDefined )
1069 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1070 inputStyle.isSlantDefined = true;
1073 if( mFontDefaults->sizeDefined )
1075 inputStyle.size = mFontDefaults->mDefaultPointSize;
1076 inputStyle.isSizeDefined = true;
1081 float Controller::Impl::GetDefaultFontLineHeight()
1083 FontId defaultFontId = 0u;
1084 if( NULL == mFontDefaults )
1086 TextAbstraction::FontDescription fontDescription;
1087 defaultFontId = mFontClient.GetFontId( fontDescription );
1091 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1094 Text::FontMetrics fontMetrics;
1095 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1097 return( fontMetrics.ascender - fontMetrics.descender );
1100 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1102 if( NULL == mEventData )
1104 // Nothing to do if there is no text input.
1108 int keyCode = event.p1.mInt;
1110 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1112 if( mEventData->mPrimaryCursorPosition > 0u )
1114 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1117 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1119 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1121 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1124 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1126 // Get first the line index of the current cursor position index.
1127 CharacterIndex characterIndex = 0u;
1129 if( mEventData->mPrimaryCursorPosition > 0u )
1131 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1134 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1136 if( lineIndex > 0u )
1138 // Retrieve the cursor position info.
1139 CursorInfo cursorInfo;
1140 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1143 // Get the line above.
1144 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1146 // Get the next hit 'y' point.
1147 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1149 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1150 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1153 mEventData->mCursorHookPositionX,
1157 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1159 // Get first the line index of the current cursor position index.
1160 CharacterIndex characterIndex = 0u;
1162 if( mEventData->mPrimaryCursorPosition > 0u )
1164 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1167 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1169 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1171 // Retrieve the cursor position info.
1172 CursorInfo cursorInfo;
1173 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1176 // Get the line below.
1177 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1179 // Get the next hit 'y' point.
1180 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1182 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1183 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1186 mEventData->mCursorHookPositionX,
1191 mEventData->mUpdateCursorPosition = true;
1192 mEventData->mUpdateInputStyle = true;
1193 mEventData->mScrollAfterUpdatePosition = true;
1196 void Controller::Impl::OnTapEvent( const Event& event )
1198 if( NULL != mEventData )
1200 const unsigned int tapCount = event.p1.mUint;
1202 if( 1u == tapCount )
1204 if( IsShowingRealText() )
1206 // Convert from control's coords to text's coords.
1207 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1208 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1210 // Keep the tap 'x' position. Used to move the cursor.
1211 mEventData->mCursorHookPositionX = xPosition;
1213 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1219 // When the cursor position is changing, delay cursor blinking
1220 mEventData->mDecorator->DelayCursorBlink();
1224 mEventData->mPrimaryCursorPosition = 0u;
1227 mEventData->mUpdateCursorPosition = true;
1228 mEventData->mUpdateGrabHandlePosition = true;
1229 mEventData->mScrollAfterUpdatePosition = true;
1230 mEventData->mUpdateInputStyle = true;
1232 // Notify the cursor position to the imf manager.
1233 if( mEventData->mImfManager )
1235 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1236 mEventData->mImfManager.NotifyCursorPosition();
1242 void Controller::Impl::OnPanEvent( const Event& event )
1244 if( NULL == mEventData )
1246 // Nothing to do if there is no text input.
1250 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1251 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1253 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1255 // Nothing to do if scrolling is not enabled.
1259 const int state = event.p1.mInt;
1263 case Gesture::Started:
1265 // Will remove the cursor, handles or text's popup, ...
1266 ChangeState( EventData::TEXT_PANNING );
1269 case Gesture::Continuing:
1271 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1272 const Vector2 currentScroll = mScrollPosition;
1274 if( isHorizontalScrollEnabled )
1276 const float displacementX = event.p2.mFloat;
1277 mScrollPosition.x += displacementX;
1279 ClampHorizontalScroll( layoutSize );
1282 if( isVerticalScrollEnabled )
1284 const float displacementY = event.p3.mFloat;
1285 mScrollPosition.y += displacementY;
1287 ClampVerticalScroll( layoutSize );
1290 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1293 case Gesture::Finished:
1294 case Gesture::Cancelled: // FALLTHROUGH
1296 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1297 ChangeState( mEventData->mPreviousState );
1305 void Controller::Impl::OnLongPressEvent( const Event& event )
1307 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1309 if( EventData::EDITING == mEventData->mState )
1311 ChangeState ( EventData::EDITING_WITH_POPUP );
1312 mEventData->mDecoratorUpdated = true;
1316 void Controller::Impl::OnHandleEvent( const Event& event )
1318 if( NULL == mEventData )
1320 // Nothing to do if there is no text input.
1324 const unsigned int state = event.p1.mUint;
1325 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1326 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1328 if( HANDLE_PRESSED == state )
1330 // Convert from decorator's coords to text's coords.
1331 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1332 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1334 // Need to calculate the handle's new position.
1335 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1341 if( Event::GRAB_HANDLE_EVENT == event.type )
1343 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1345 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1347 // Updates the cursor position if the handle's new position is different than the current one.
1348 mEventData->mUpdateCursorPosition = true;
1349 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1350 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1351 mEventData->mPrimaryCursorPosition = handleNewPosition;
1354 // 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.
1355 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1357 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1359 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1361 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1362 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1364 // Updates the highlight box if the handle's new position is different than the current one.
1365 mEventData->mUpdateHighlightBox = true;
1366 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1367 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1368 mEventData->mLeftSelectionPosition = handleNewPosition;
1371 // 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.
1372 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1374 // Will define the order to scroll the text to match the handle position.
1375 mEventData->mIsLeftHandleSelected = true;
1377 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1379 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1381 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1382 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1384 // Updates the highlight box if the handle's new position is different than the current one.
1385 mEventData->mUpdateHighlightBox = true;
1386 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1387 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1388 mEventData->mRightSelectionPosition = handleNewPosition;
1391 // 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.
1392 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1394 // Will define the order to scroll the text to match the handle position.
1395 mEventData->mIsLeftHandleSelected = false;
1397 } // end ( HANDLE_PRESSED == state )
1398 else if( ( HANDLE_RELEASED == state ) ||
1399 handleStopScrolling )
1401 CharacterIndex handlePosition = 0u;
1402 if( handleStopScrolling || isSmoothHandlePanEnabled )
1404 // Convert from decorator's coords to text's coords.
1405 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1406 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1408 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1415 if( Event::GRAB_HANDLE_EVENT == event.type )
1417 mEventData->mUpdateCursorPosition = true;
1418 mEventData->mUpdateGrabHandlePosition = true;
1419 mEventData->mUpdateInputStyle = true;
1421 if( !IsClipboardEmpty() )
1423 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1426 if( handleStopScrolling || isSmoothHandlePanEnabled )
1428 mEventData->mScrollAfterUpdatePosition = true;
1429 mEventData->mPrimaryCursorPosition = handlePosition;
1432 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1434 ChangeState( EventData::SELECTING );
1436 mEventData->mUpdateHighlightBox = true;
1437 mEventData->mUpdateLeftSelectionPosition = true;
1438 mEventData->mUpdateRightSelectionPosition = true;
1440 if( handleStopScrolling || isSmoothHandlePanEnabled )
1442 mEventData->mScrollAfterUpdatePosition = true;
1444 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1445 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1447 mEventData->mLeftSelectionPosition = handlePosition;
1451 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1453 ChangeState( EventData::SELECTING );
1455 mEventData->mUpdateHighlightBox = true;
1456 mEventData->mUpdateRightSelectionPosition = true;
1457 mEventData->mUpdateLeftSelectionPosition = true;
1459 if( handleStopScrolling || isSmoothHandlePanEnabled )
1461 mEventData->mScrollAfterUpdatePosition = true;
1462 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1463 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1465 mEventData->mRightSelectionPosition = handlePosition;
1470 mEventData->mDecoratorUpdated = true;
1471 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1472 else if( HANDLE_SCROLLING == state )
1474 const float xSpeed = event.p2.mFloat;
1475 const float ySpeed = event.p3.mFloat;
1476 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1477 const Vector2 currentScrollPosition = mScrollPosition;
1479 mScrollPosition.x += xSpeed;
1480 mScrollPosition.y += ySpeed;
1482 ClampHorizontalScroll( layoutSize );
1483 ClampVerticalScroll( layoutSize );
1485 bool endOfScroll = false;
1486 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1488 // Notify the decorator there is no more text to scroll.
1489 // The decorator won't send more scroll events.
1490 mEventData->mDecorator->NotifyEndOfScroll();
1491 // Still need to set the position of the handle.
1495 // Set the position of the handle.
1496 const bool scrollRightDirection = xSpeed > 0.f;
1497 const bool scrollBottomDirection = ySpeed > 0.f;
1498 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1499 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1501 if( Event::GRAB_HANDLE_EVENT == event.type )
1503 ChangeState( EventData::GRAB_HANDLE_PANNING );
1505 // Get the grab handle position in decorator coords.
1506 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1508 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1510 // Position the grag handle close to either the left or right edge.
1511 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1514 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1516 position.x = mEventData->mCursorHookPositionX;
1518 // Position the grag handle close to either the top or bottom edge.
1519 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1522 // Get the new handle position.
1523 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1524 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1527 position.x - mScrollPosition.x,
1528 position.y - mScrollPosition.y );
1530 if( mEventData->mPrimaryCursorPosition != handlePosition )
1532 mEventData->mUpdateCursorPosition = true;
1533 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1534 mEventData->mScrollAfterUpdatePosition = true;
1535 mEventData->mPrimaryCursorPosition = handlePosition;
1537 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1539 // Updates the decorator if the soft handle panning is enabled.
1540 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1542 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1544 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1546 // Get the selection handle position in decorator coords.
1547 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1549 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1551 // Position the selection handle close to either the left or right edge.
1552 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1555 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1557 position.x = mEventData->mCursorHookPositionX;
1559 // Position the grag handle close to either the top or bottom edge.
1560 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1563 // Get the new handle position.
1564 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1565 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1568 position.x - mScrollPosition.x,
1569 position.y - mScrollPosition.y );
1571 if( leftSelectionHandleEvent )
1573 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1575 if( differentHandles || endOfScroll )
1577 mEventData->mUpdateHighlightBox = true;
1578 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1579 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1580 mEventData->mLeftSelectionPosition = handlePosition;
1585 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1586 if( differentHandles || endOfScroll )
1588 mEventData->mUpdateHighlightBox = true;
1589 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1590 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1591 mEventData->mRightSelectionPosition = handlePosition;
1595 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1597 RepositionSelectionHandles();
1599 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1602 mEventData->mDecoratorUpdated = true;
1603 } // end ( HANDLE_SCROLLING == state )
1606 void Controller::Impl::OnSelectEvent( const Event& event )
1608 if( NULL == mEventData )
1610 // Nothing to do if there is no text.
1614 if( mEventData->mSelectionEnabled )
1616 // Convert from control's coords to text's coords.
1617 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1618 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1620 // Calculates the logical position from the x,y coords.
1621 RepositionSelectionHandles( xPosition,
1626 void Controller::Impl::OnSelectAllEvent()
1628 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1630 if( NULL == mEventData )
1632 // Nothing to do if there is no text.
1636 if( mEventData->mSelectionEnabled )
1638 ChangeState( EventData::SELECTING );
1640 mEventData->mLeftSelectionPosition = 0u;
1641 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1643 mEventData->mScrollAfterUpdatePosition = true;
1644 mEventData->mUpdateLeftSelectionPosition = true;
1645 mEventData->mUpdateRightSelectionPosition = true;
1646 mEventData->mUpdateHighlightBox = true;
1650 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1652 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1654 // Nothing to select if handles are in the same place.
1655 selectedText.clear();
1659 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1661 //Get start and end position of selection
1662 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1663 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1665 Vector<Character>& utf32Characters = mLogicalModel->mText;
1666 const Length numberOfCharacters = utf32Characters.Count();
1668 // Validate the start and end selection points
1669 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1671 //Get text as a UTF8 string
1672 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1674 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1676 // Keep a copy of the current input style.
1677 InputStyle currentInputStyle;
1678 currentInputStyle.Copy( mEventData->mInputStyle );
1680 // Set as input style the style of the first deleted character.
1681 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1683 // Compare if the input style has changed.
1684 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1686 if( hasInputStyleChanged )
1688 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1689 // Queue the input style changed signal.
1690 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1693 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1695 // Mark the paragraphs to be updated.
1696 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1697 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1699 // Delete text between handles
1700 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1701 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1702 utf32Characters.Erase( first, last );
1704 // Will show the cursor at the first character of the selection.
1705 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1709 // Will show the cursor at the last character of the selection.
1710 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1713 mEventData->mDecoratorUpdated = true;
1717 void Controller::Impl::ShowClipboard()
1721 mClipboard.ShowClipboard();
1725 void Controller::Impl::HideClipboard()
1727 if( mClipboard && mClipboardHideEnabled )
1729 mClipboard.HideClipboard();
1733 void Controller::Impl::SetClipboardHideEnable(bool enable)
1735 mClipboardHideEnabled = enable;
1738 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1740 //Send string to clipboard
1741 return ( mClipboard && mClipboard.SetItem( source ) );
1744 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1746 std::string selectedText;
1747 RetrieveSelection( selectedText, deleteAfterSending );
1748 CopyStringToClipboard( selectedText );
1749 ChangeState( EventData::EDITING );
1752 void Controller::Impl::RequestGetTextFromClipboard()
1756 mClipboard.RequestItem();
1760 void Controller::Impl::RepositionSelectionHandles()
1762 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1763 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1765 if( selectionStart == selectionEnd )
1767 // Nothing to select if handles are in the same place.
1771 mEventData->mDecorator->ClearHighlights();
1773 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1774 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1775 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1776 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1777 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1778 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1779 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1781 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1782 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1783 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1785 // Swap the indices if the start is greater than the end.
1786 const bool indicesSwapped = selectionStart > selectionEnd;
1788 // Tell the decorator to flip the selection handles if needed.
1789 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1791 if( indicesSwapped )
1793 std::swap( selectionStart, selectionEnd );
1796 // Get the indices to the first and last selected glyphs.
1797 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1798 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1799 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1800 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1802 // Get the lines where the glyphs are laid-out.
1803 const LineRun* lineRun = mVisualModel->mLines.Begin();
1805 LineIndex lineIndex = 0u;
1806 Length numberOfLines = 0u;
1807 mVisualModel->GetNumberOfLines( glyphStart,
1808 1u + glyphEnd - glyphStart,
1811 const LineIndex firstLineIndex = lineIndex;
1813 // Create the structure to store some selection box info.
1814 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1815 selectionBoxLinesInfo.Resize( numberOfLines );
1817 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1818 selectionBoxInfo->minX = MAX_FLOAT;
1819 selectionBoxInfo->maxX = MIN_FLOAT;
1821 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1822 float minHighlightX = std::numeric_limits<float>::max();
1823 float maxHighlightX = std::numeric_limits<float>::min();
1825 Vector2 highLightPosition; // The highlight position in decorator's coords.
1827 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1829 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1830 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1833 // Transform to decorator's (control) coords.
1834 selectionBoxInfo->lineOffset += mScrollPosition.y;
1836 lineRun += firstLineIndex;
1838 // The line height is the addition of the line ascender and the line descender.
1839 // However, the line descender has a negative value, hence the subtraction.
1840 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1842 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1844 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1845 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1846 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1848 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1849 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1850 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1852 // The number of quads of the selection box.
1853 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1854 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1856 // Count the actual number of quads.
1857 unsigned int actualNumberOfQuads = 0u;
1860 // Traverse the glyphs.
1861 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1863 const GlyphInfo& glyph = *( glyphsBuffer + index );
1864 const Vector2& position = *( positionsBuffer + index );
1866 if( splitStartGlyph )
1868 // 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.
1870 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1871 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1872 // Get the direction of the character.
1873 CharacterDirection isCurrentRightToLeft = false;
1874 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1876 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1879 // The end point could be in the middle of the ligature.
1880 // Calculate the number of characters selected.
1881 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1883 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1884 quad.y = selectionBoxInfo->lineOffset;
1885 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1886 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1888 // Store the min and max 'x' for each line.
1889 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1890 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1892 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1893 ++actualNumberOfQuads;
1895 splitStartGlyph = false;
1899 if( splitEndGlyph && ( index == glyphEnd ) )
1901 // 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.
1903 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1904 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1905 // Get the direction of the character.
1906 CharacterDirection isCurrentRightToLeft = false;
1907 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1909 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1912 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1914 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1915 quad.y = selectionBoxInfo->lineOffset;
1916 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1917 quad.w = quad.y + selectionBoxInfo->lineHeight;
1919 // Store the min and max 'x' for each line.
1920 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1921 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1923 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1925 ++actualNumberOfQuads;
1927 splitEndGlyph = false;
1931 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1932 quad.y = selectionBoxInfo->lineOffset;
1933 quad.z = quad.x + glyph.advance;
1934 quad.w = quad.y + selectionBoxInfo->lineHeight;
1936 // Store the min and max 'x' for each line.
1937 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1938 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1940 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1942 ++actualNumberOfQuads;
1944 // Whether to retrieve the next line.
1945 if( index == lastGlyphOfLine )
1947 // Retrieve the next line.
1950 // Get the last glyph of the new line.
1951 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1954 if( lineIndex < firstLineIndex + numberOfLines )
1956 // Keep the offset and height of the current selection box.
1957 const float currentLineOffset = selectionBoxInfo->lineOffset;
1958 const float currentLineHeight = selectionBoxInfo->lineHeight;
1960 // Get the selection box info for the next line.
1963 selectionBoxInfo->minX = MAX_FLOAT;
1964 selectionBoxInfo->maxX = MIN_FLOAT;
1966 // Update the line's vertical offset.
1967 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1969 // The line height is the addition of the line ascender and the line descender.
1970 // However, the line descender has a negative value, hence the subtraction.
1971 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1976 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1977 // The final width is calculated after 'boxifying' the selection.
1978 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1979 endIt = selectionBoxLinesInfo.End();
1983 const SelectionBoxInfo& info = *it;
1985 // Update the size of the highlighted text.
1986 highLightSize.height += info.lineHeight;
1987 minHighlightX = std::min( minHighlightX, info.minX );
1988 maxHighlightX = std::max( maxHighlightX, info.maxX );
1991 // Add extra geometry to 'boxify' the selection.
1993 if( 1u < numberOfLines )
1995 // Boxify the first line.
1996 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1997 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1999 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2000 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2005 quad.y = firstSelectionBoxLineInfo.lineOffset;
2006 quad.z = firstSelectionBoxLineInfo.minX;
2007 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2009 // Boxify at the beginning of the line.
2010 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2012 ++actualNumberOfQuads;
2014 // Update the size of the highlighted text.
2015 minHighlightX = 0.f;
2020 quad.x = firstSelectionBoxLineInfo.maxX;
2021 quad.y = firstSelectionBoxLineInfo.lineOffset;
2022 quad.z = mVisualModel->mControlSize.width;
2023 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2025 // Boxify at the end of the line.
2026 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2028 ++actualNumberOfQuads;
2030 // Update the size of the highlighted text.
2031 maxHighlightX = mVisualModel->mControlSize.width;
2034 // Boxify the central lines.
2035 if( 2u < numberOfLines )
2037 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2038 endIt = selectionBoxLinesInfo.End() - 1u;
2042 const SelectionBoxInfo& info = *it;
2045 quad.y = info.lineOffset;
2047 quad.w = info.lineOffset + info.lineHeight;
2049 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2051 ++actualNumberOfQuads;
2054 quad.y = info.lineOffset;
2055 quad.z = mVisualModel->mControlSize.width;
2056 quad.w = info.lineOffset + info.lineHeight;
2058 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2060 ++actualNumberOfQuads;
2063 // Update the size of the highlighted text.
2064 minHighlightX = 0.f;
2065 maxHighlightX = mVisualModel->mControlSize.width;
2068 // Boxify the last line.
2069 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2070 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2072 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2073 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2078 quad.y = lastSelectionBoxLineInfo.lineOffset;
2079 quad.z = lastSelectionBoxLineInfo.minX;
2080 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2082 // Boxify at the beginning of the line.
2083 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2085 ++actualNumberOfQuads;
2087 // Update the size of the highlighted text.
2088 minHighlightX = 0.f;
2093 quad.x = lastSelectionBoxLineInfo.maxX;
2094 quad.y = lastSelectionBoxLineInfo.lineOffset;
2095 quad.z = mVisualModel->mControlSize.width;
2096 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2098 // Boxify at the end of the line.
2099 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2101 ++actualNumberOfQuads;
2103 // Update the size of the highlighted text.
2104 maxHighlightX = mVisualModel->mControlSize.width;
2108 // Set the actual number of quads.
2109 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2111 // Sets the highlight's size and position. In decorator's coords.
2112 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2113 highLightSize.width = maxHighlightX - minHighlightX;
2115 highLightPosition.x = minHighlightX;
2116 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2117 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2119 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2121 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2123 CursorInfo primaryCursorInfo;
2124 GetCursorPosition( mEventData->mLeftSelectionPosition,
2125 primaryCursorInfo );
2127 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2129 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2131 primaryCursorInfo.lineOffset + mScrollPosition.y,
2132 primaryCursorInfo.lineHeight );
2134 CursorInfo secondaryCursorInfo;
2135 GetCursorPosition( mEventData->mRightSelectionPosition,
2136 secondaryCursorInfo );
2138 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2140 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2141 secondaryPosition.x,
2142 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2143 secondaryCursorInfo.lineHeight );
2146 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2147 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2149 // Set the flag to update the decorator.
2150 mEventData->mDecoratorUpdated = true;
2153 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2155 if( NULL == mEventData )
2157 // Nothing to do if there is no text input.
2161 if( IsShowingPlaceholderText() )
2163 // Nothing to do if there is the place-holder text.
2167 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2168 const Length numberOfLines = mVisualModel->mLines.Count();
2169 if( ( 0 == numberOfGlyphs ) ||
2170 ( 0 == numberOfLines ) )
2172 // Nothing to do if there is no text.
2176 // Find which word was selected
2177 CharacterIndex selectionStart( 0 );
2178 CharacterIndex selectionEnd( 0 );
2179 const bool indicesFound = FindSelectionIndices( mVisualModel,
2186 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2190 ChangeState( EventData::SELECTING );
2192 mEventData->mLeftSelectionPosition = selectionStart;
2193 mEventData->mRightSelectionPosition = selectionEnd;
2195 mEventData->mUpdateLeftSelectionPosition = true;
2196 mEventData->mUpdateRightSelectionPosition = true;
2197 mEventData->mUpdateHighlightBox = true;
2199 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2203 // Nothing to select. i.e. a white space, out of bounds
2204 ChangeState( EventData::EDITING );
2206 mEventData->mPrimaryCursorPosition = selectionEnd;
2208 mEventData->mUpdateCursorPosition = true;
2209 mEventData->mUpdateGrabHandlePosition = true;
2210 mEventData->mScrollAfterUpdatePosition = true;
2211 mEventData->mUpdateInputStyle = true;
2215 void Controller::Impl::SetPopupButtons()
2218 * Sets the Popup buttons to be shown depending on State.
2220 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2222 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2225 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2227 if( EventData::SELECTING == mEventData->mState )
2229 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2231 if( !IsClipboardEmpty() )
2233 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2234 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2237 if( !mEventData->mAllTextSelected )
2239 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2242 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2244 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2246 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2249 if( !IsClipboardEmpty() )
2251 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2252 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2255 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2257 if ( !IsClipboardEmpty() )
2259 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2260 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2264 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2267 void Controller::Impl::ChangeState( EventData::State newState )
2269 if( NULL == mEventData )
2271 // Nothing to do if there is no text input.
2275 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2277 if( mEventData->mState != newState )
2279 mEventData->mPreviousState = mEventData->mState;
2280 mEventData->mState = newState;
2282 switch( mEventData->mState )
2284 case EventData::INACTIVE:
2286 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2287 mEventData->mDecorator->StopCursorBlink();
2288 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2289 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2290 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2291 mEventData->mDecorator->SetHighlightActive( false );
2292 mEventData->mDecorator->SetPopupActive( false );
2293 mEventData->mDecoratorUpdated = true;
2296 case EventData::INTERRUPTED:
2298 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2299 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2300 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2301 mEventData->mDecorator->SetHighlightActive( false );
2302 mEventData->mDecorator->SetPopupActive( false );
2303 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;
2341 case EventData::EDITING_WITH_POPUP:
2343 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2345 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2346 if( mEventData->mCursorBlinkEnabled )
2348 mEventData->mDecorator->StartCursorBlink();
2350 if( mEventData->mSelectionEnabled )
2352 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2353 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2354 mEventData->mDecorator->SetHighlightActive( false );
2358 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2360 if( mEventData->mGrabHandlePopupEnabled )
2363 mEventData->mDecorator->SetPopupActive( true );
2365 mEventData->mDecoratorUpdated = true;
2368 case EventData::EDITING_WITH_GRAB_HANDLE:
2370 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2372 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2373 if( mEventData->mCursorBlinkEnabled )
2375 mEventData->mDecorator->StartCursorBlink();
2377 // Grab handle is not shown until a tap is received whilst EDITING
2378 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2379 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2380 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2381 mEventData->mDecorator->SetHighlightActive( false );
2382 if( mEventData->mGrabHandlePopupEnabled )
2384 mEventData->mDecorator->SetPopupActive( false );
2386 mEventData->mDecoratorUpdated = true;
2389 case EventData::SELECTION_HANDLE_PANNING:
2391 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2392 mEventData->mDecorator->StopCursorBlink();
2393 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2394 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2395 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2396 mEventData->mDecorator->SetHighlightActive( true );
2397 if( mEventData->mGrabHandlePopupEnabled )
2399 mEventData->mDecorator->SetPopupActive( false );
2401 mEventData->mDecoratorUpdated = true;
2404 case EventData::GRAB_HANDLE_PANNING:
2406 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2408 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2409 if( mEventData->mCursorBlinkEnabled )
2411 mEventData->mDecorator->StartCursorBlink();
2413 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2414 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2415 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2416 mEventData->mDecorator->SetHighlightActive( false );
2417 if( mEventData->mGrabHandlePopupEnabled )
2419 mEventData->mDecorator->SetPopupActive( false );
2421 mEventData->mDecoratorUpdated = true;
2424 case EventData::EDITING_WITH_PASTE_POPUP:
2426 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2428 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2429 if( mEventData->mCursorBlinkEnabled )
2431 mEventData->mDecorator->StartCursorBlink();
2434 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2435 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2436 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2437 mEventData->mDecorator->SetHighlightActive( false );
2439 if( mEventData->mGrabHandlePopupEnabled )
2442 mEventData->mDecorator->SetPopupActive( true );
2444 mEventData->mDecoratorUpdated = true;
2447 case EventData::TEXT_PANNING:
2449 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2450 mEventData->mDecorator->StopCursorBlink();
2451 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2452 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2453 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2455 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2456 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2457 mEventData->mDecorator->SetHighlightActive( true );
2460 if( mEventData->mGrabHandlePopupEnabled )
2462 mEventData->mDecorator->SetPopupActive( false );
2465 mEventData->mDecoratorUpdated = true;
2472 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2473 CursorInfo& cursorInfo )
2475 if( !IsShowingRealText() )
2477 // Do not want to use the place-holder text to set the cursor position.
2479 // Use the line's height of the font's family set to set the cursor's size.
2480 // If there is no font's family set, use the default font.
2481 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2483 cursorInfo.lineOffset = 0.f;
2484 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2485 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2487 switch( mLayoutEngine.GetHorizontalAlignment() )
2489 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2491 cursorInfo.primaryPosition.x = 0.f;
2494 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2496 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2499 case LayoutEngine::HORIZONTAL_ALIGN_END:
2501 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2506 // Nothing else to do.
2510 Text::GetCursorPosition( mVisualModel,
2516 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2518 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2520 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2521 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2523 if( 0.f > cursorInfo.primaryPosition.x )
2525 cursorInfo.primaryPosition.x = 0.f;
2528 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2529 if( cursorInfo.primaryPosition.x > edgeWidth )
2531 cursorInfo.primaryPosition.x = edgeWidth;
2536 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2538 if( NULL == mEventData )
2540 // Nothing to do if there is no text input.
2544 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2546 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2547 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2549 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2550 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2552 if( numberOfCharacters > 1u )
2554 const Script script = mLogicalModel->GetScript( index );
2555 if( HasLigatureMustBreak( script ) )
2557 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2558 numberOfCharacters = 1u;
2563 while( 0u == numberOfCharacters )
2566 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2570 if( index < mEventData->mPrimaryCursorPosition )
2572 cursorIndex -= numberOfCharacters;
2576 cursorIndex += numberOfCharacters;
2579 // Will update the cursor hook position.
2580 mEventData->mUpdateCursorHookPosition = true;
2585 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2587 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2588 if( NULL == mEventData )
2590 // Nothing to do if there is no text input.
2591 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2595 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2597 // Sets the cursor position.
2598 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2601 cursorInfo.primaryCursorHeight,
2602 cursorInfo.lineHeight );
2603 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2605 if( mEventData->mUpdateGrabHandlePosition )
2607 // Sets the grab handle position.
2608 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2610 cursorInfo.lineOffset + mScrollPosition.y,
2611 cursorInfo.lineHeight );
2614 if( cursorInfo.isSecondaryCursor )
2616 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2617 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2618 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2619 cursorInfo.secondaryCursorHeight,
2620 cursorInfo.lineHeight );
2621 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2624 // Set which cursors are active according the state.
2625 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2627 if( cursorInfo.isSecondaryCursor )
2629 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2633 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2638 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2641 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2644 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2645 const CursorInfo& cursorInfo )
2647 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2648 ( RIGHT_SELECTION_HANDLE != handleType ) )
2653 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2655 // Sets the handle's position.
2656 mEventData->mDecorator->SetPosition( handleType,
2658 cursorInfo.lineOffset + mScrollPosition.y,
2659 cursorInfo.lineHeight );
2661 // If selection handle at start of the text and other at end of the text then all text is selected.
2662 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2663 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2664 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2667 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2669 // Clamp between -space & 0.
2671 if( layoutSize.width > mVisualModel->mControlSize.width )
2673 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2674 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2675 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2677 mEventData->mDecoratorUpdated = true;
2681 mScrollPosition.x = 0.f;
2685 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2687 if( LayoutEngine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2689 // Nothing to do if the text is single line.
2693 // Clamp between -space & 0.
2694 if( layoutSize.height > mVisualModel->mControlSize.height )
2696 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2697 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2698 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2700 mEventData->mDecoratorUpdated = true;
2704 mScrollPosition.y = 0.f;
2708 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2710 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2712 // position is in actor's coords.
2713 const float positionEndX = position.x + cursorWidth;
2714 const float positionEndY = position.y + lineHeight;
2716 // Transform the position to decorator coords.
2717 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2718 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2720 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2721 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2723 if( decoratorPositionBeginX < 0.f )
2725 mScrollPosition.x = -position.x;
2727 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2729 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2732 if( decoratorPositionBeginY < 0.f )
2734 mScrollPosition.y = -position.y;
2736 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2738 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2742 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2744 // Get the current cursor position in decorator coords.
2745 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2747 // Calculate the offset to match the cursor position before the character was deleted.
2748 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2749 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2751 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2752 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2754 // Makes the new cursor position visible if needed.
2755 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2758 void Controller::Impl::RequestRelayout()
2760 if( NULL != mControlInterface )
2762 mControlInterface->RequestTextRelayout();
2768 } // namespace Toolkit