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;
1313 mEventData->mUpdateInputStyle = true;
1317 void Controller::Impl::OnHandleEvent( const Event& event )
1319 if( NULL == mEventData )
1321 // Nothing to do if there is no text input.
1325 const unsigned int state = event.p1.mUint;
1326 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1327 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1329 if( HANDLE_PRESSED == state )
1331 // Convert from decorator's coords to text's coords.
1332 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1333 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1335 // Need to calculate the handle's new position.
1336 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1342 if( Event::GRAB_HANDLE_EVENT == event.type )
1344 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1346 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1348 // Updates the cursor position if the handle's new position is different than the current one.
1349 mEventData->mUpdateCursorPosition = true;
1350 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1351 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1352 mEventData->mPrimaryCursorPosition = handleNewPosition;
1355 // 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.
1356 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1358 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1360 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1362 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1363 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1365 // Updates the highlight box if the handle's new position is different than the current one.
1366 mEventData->mUpdateHighlightBox = true;
1367 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1368 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1369 mEventData->mLeftSelectionPosition = handleNewPosition;
1372 // 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.
1373 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1375 // Will define the order to scroll the text to match the handle position.
1376 mEventData->mIsLeftHandleSelected = true;
1378 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1380 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1382 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1383 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1385 // Updates the highlight box if the handle's new position is different than the current one.
1386 mEventData->mUpdateHighlightBox = true;
1387 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1388 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1389 mEventData->mRightSelectionPosition = handleNewPosition;
1392 // 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.
1393 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1395 // Will define the order to scroll the text to match the handle position.
1396 mEventData->mIsLeftHandleSelected = false;
1398 } // end ( HANDLE_PRESSED == state )
1399 else if( ( HANDLE_RELEASED == state ) ||
1400 handleStopScrolling )
1402 CharacterIndex handlePosition = 0u;
1403 if( handleStopScrolling || isSmoothHandlePanEnabled )
1405 // Convert from decorator's coords to text's coords.
1406 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1407 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1409 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1416 if( Event::GRAB_HANDLE_EVENT == event.type )
1418 mEventData->mUpdateCursorPosition = true;
1419 mEventData->mUpdateGrabHandlePosition = true;
1420 mEventData->mUpdateInputStyle = true;
1422 if( !IsClipboardEmpty() )
1424 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1427 if( handleStopScrolling || isSmoothHandlePanEnabled )
1429 mEventData->mScrollAfterUpdatePosition = true;
1430 mEventData->mPrimaryCursorPosition = handlePosition;
1433 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1435 ChangeState( EventData::SELECTING );
1437 mEventData->mUpdateHighlightBox = true;
1438 mEventData->mUpdateLeftSelectionPosition = true;
1439 mEventData->mUpdateRightSelectionPosition = true;
1441 if( handleStopScrolling || isSmoothHandlePanEnabled )
1443 mEventData->mScrollAfterUpdatePosition = true;
1445 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1446 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1448 mEventData->mLeftSelectionPosition = handlePosition;
1452 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1454 ChangeState( EventData::SELECTING );
1456 mEventData->mUpdateHighlightBox = true;
1457 mEventData->mUpdateRightSelectionPosition = true;
1458 mEventData->mUpdateLeftSelectionPosition = true;
1460 if( handleStopScrolling || isSmoothHandlePanEnabled )
1462 mEventData->mScrollAfterUpdatePosition = true;
1463 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1464 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1466 mEventData->mRightSelectionPosition = handlePosition;
1471 mEventData->mDecoratorUpdated = true;
1472 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1473 else if( HANDLE_SCROLLING == state )
1475 const float xSpeed = event.p2.mFloat;
1476 const float ySpeed = event.p3.mFloat;
1477 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1478 const Vector2 currentScrollPosition = mScrollPosition;
1480 mScrollPosition.x += xSpeed;
1481 mScrollPosition.y += ySpeed;
1483 ClampHorizontalScroll( layoutSize );
1484 ClampVerticalScroll( layoutSize );
1486 bool endOfScroll = false;
1487 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1489 // Notify the decorator there is no more text to scroll.
1490 // The decorator won't send more scroll events.
1491 mEventData->mDecorator->NotifyEndOfScroll();
1492 // Still need to set the position of the handle.
1496 // Set the position of the handle.
1497 const bool scrollRightDirection = xSpeed > 0.f;
1498 const bool scrollBottomDirection = ySpeed > 0.f;
1499 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1500 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1502 if( Event::GRAB_HANDLE_EVENT == event.type )
1504 ChangeState( EventData::GRAB_HANDLE_PANNING );
1506 // Get the grab handle position in decorator coords.
1507 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1509 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1511 // Position the grag handle close to either the left or right edge.
1512 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1515 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1517 position.x = mEventData->mCursorHookPositionX;
1519 // Position the grag handle close to either the top or bottom edge.
1520 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1523 // Get the new handle position.
1524 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1525 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1528 position.x - mScrollPosition.x,
1529 position.y - mScrollPosition.y );
1531 if( mEventData->mPrimaryCursorPosition != handlePosition )
1533 mEventData->mUpdateCursorPosition = true;
1534 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1535 mEventData->mScrollAfterUpdatePosition = true;
1536 mEventData->mPrimaryCursorPosition = handlePosition;
1538 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1540 // Updates the decorator if the soft handle panning is enabled.
1541 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1543 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1545 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1547 // Get the selection handle position in decorator coords.
1548 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1550 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1552 // Position the selection handle close to either the left or right edge.
1553 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1556 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1558 position.x = mEventData->mCursorHookPositionX;
1560 // Position the grag handle close to either the top or bottom edge.
1561 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1564 // Get the new handle position.
1565 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1566 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1569 position.x - mScrollPosition.x,
1570 position.y - mScrollPosition.y );
1572 if( leftSelectionHandleEvent )
1574 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1576 if( differentHandles || endOfScroll )
1578 mEventData->mUpdateHighlightBox = true;
1579 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1580 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1581 mEventData->mLeftSelectionPosition = handlePosition;
1586 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1587 if( differentHandles || endOfScroll )
1589 mEventData->mUpdateHighlightBox = true;
1590 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1591 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1592 mEventData->mRightSelectionPosition = handlePosition;
1596 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1598 RepositionSelectionHandles();
1600 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1603 mEventData->mDecoratorUpdated = true;
1604 } // end ( HANDLE_SCROLLING == state )
1607 void Controller::Impl::OnSelectEvent( const Event& event )
1609 if( NULL == mEventData )
1611 // Nothing to do if there is no text.
1615 if( mEventData->mSelectionEnabled )
1617 // Convert from control's coords to text's coords.
1618 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1619 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1621 // Calculates the logical position from the x,y coords.
1622 RepositionSelectionHandles( xPosition,
1627 void Controller::Impl::OnSelectAllEvent()
1629 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1631 if( NULL == mEventData )
1633 // Nothing to do if there is no text.
1637 if( mEventData->mSelectionEnabled )
1639 ChangeState( EventData::SELECTING );
1641 mEventData->mLeftSelectionPosition = 0u;
1642 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1644 mEventData->mScrollAfterUpdatePosition = true;
1645 mEventData->mUpdateLeftSelectionPosition = true;
1646 mEventData->mUpdateRightSelectionPosition = true;
1647 mEventData->mUpdateHighlightBox = true;
1651 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1653 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1655 // Nothing to select if handles are in the same place.
1656 selectedText.clear();
1660 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1662 //Get start and end position of selection
1663 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1664 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1666 Vector<Character>& utf32Characters = mLogicalModel->mText;
1667 const Length numberOfCharacters = utf32Characters.Count();
1669 // Validate the start and end selection points
1670 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1672 //Get text as a UTF8 string
1673 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1675 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1677 // Keep a copy of the current input style.
1678 InputStyle currentInputStyle;
1679 currentInputStyle.Copy( mEventData->mInputStyle );
1681 // Set as input style the style of the first deleted character.
1682 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1684 // Compare if the input style has changed.
1685 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1687 if( hasInputStyleChanged )
1689 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1690 // Queue the input style changed signal.
1691 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1694 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1696 // Mark the paragraphs to be updated.
1697 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1698 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1700 // Delete text between handles
1701 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1702 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1703 utf32Characters.Erase( first, last );
1705 // Will show the cursor at the first character of the selection.
1706 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1710 // Will show the cursor at the last character of the selection.
1711 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1714 mEventData->mDecoratorUpdated = true;
1718 void Controller::Impl::ShowClipboard()
1722 mClipboard.ShowClipboard();
1726 void Controller::Impl::HideClipboard()
1728 if( mClipboard && mClipboardHideEnabled )
1730 mClipboard.HideClipboard();
1734 void Controller::Impl::SetClipboardHideEnable(bool enable)
1736 mClipboardHideEnabled = enable;
1739 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1741 //Send string to clipboard
1742 return ( mClipboard && mClipboard.SetItem( source ) );
1745 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1747 std::string selectedText;
1748 RetrieveSelection( selectedText, deleteAfterSending );
1749 CopyStringToClipboard( selectedText );
1750 ChangeState( EventData::EDITING );
1753 void Controller::Impl::RequestGetTextFromClipboard()
1757 mClipboard.RequestItem();
1761 void Controller::Impl::RepositionSelectionHandles()
1763 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1764 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1766 if( selectionStart == selectionEnd )
1768 // Nothing to select if handles are in the same place.
1772 mEventData->mDecorator->ClearHighlights();
1774 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1775 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1776 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1777 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1778 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1779 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1780 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1782 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1783 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1784 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1786 // Swap the indices if the start is greater than the end.
1787 const bool indicesSwapped = selectionStart > selectionEnd;
1789 // Tell the decorator to flip the selection handles if needed.
1790 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1792 if( indicesSwapped )
1794 std::swap( selectionStart, selectionEnd );
1797 // Get the indices to the first and last selected glyphs.
1798 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1799 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1800 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1801 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1803 // Get the lines where the glyphs are laid-out.
1804 const LineRun* lineRun = mVisualModel->mLines.Begin();
1806 LineIndex lineIndex = 0u;
1807 Length numberOfLines = 0u;
1808 mVisualModel->GetNumberOfLines( glyphStart,
1809 1u + glyphEnd - glyphStart,
1812 const LineIndex firstLineIndex = lineIndex;
1814 // Create the structure to store some selection box info.
1815 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1816 selectionBoxLinesInfo.Resize( numberOfLines );
1818 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1819 selectionBoxInfo->minX = MAX_FLOAT;
1820 selectionBoxInfo->maxX = MIN_FLOAT;
1822 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1823 float minHighlightX = std::numeric_limits<float>::max();
1824 float maxHighlightX = std::numeric_limits<float>::min();
1826 Vector2 highLightPosition; // The highlight position in decorator's coords.
1828 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1830 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1831 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1834 // Transform to decorator's (control) coords.
1835 selectionBoxInfo->lineOffset += mScrollPosition.y;
1837 lineRun += firstLineIndex;
1839 // The line height is the addition of the line ascender and the line descender.
1840 // However, the line descender has a negative value, hence the subtraction.
1841 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1843 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1845 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1846 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1847 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1849 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1850 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1851 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1853 // The number of quads of the selection box.
1854 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1855 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1857 // Count the actual number of quads.
1858 unsigned int actualNumberOfQuads = 0u;
1861 // Traverse the glyphs.
1862 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1864 const GlyphInfo& glyph = *( glyphsBuffer + index );
1865 const Vector2& position = *( positionsBuffer + index );
1867 if( splitStartGlyph )
1869 // 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.
1871 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1872 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1873 // Get the direction of the character.
1874 CharacterDirection isCurrentRightToLeft = false;
1875 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1877 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1880 // The end point could be in the middle of the ligature.
1881 // Calculate the number of characters selected.
1882 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1884 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1885 quad.y = selectionBoxInfo->lineOffset;
1886 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1887 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1889 // Store the min and max 'x' for each line.
1890 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1891 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1893 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1894 ++actualNumberOfQuads;
1896 splitStartGlyph = false;
1900 if( splitEndGlyph && ( index == glyphEnd ) )
1902 // 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.
1904 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1905 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1906 // Get the direction of the character.
1907 CharacterDirection isCurrentRightToLeft = false;
1908 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1910 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1913 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1915 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1916 quad.y = selectionBoxInfo->lineOffset;
1917 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1918 quad.w = quad.y + selectionBoxInfo->lineHeight;
1920 // Store the min and max 'x' for each line.
1921 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1922 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1924 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1926 ++actualNumberOfQuads;
1928 splitEndGlyph = false;
1932 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1933 quad.y = selectionBoxInfo->lineOffset;
1934 quad.z = quad.x + glyph.advance;
1935 quad.w = quad.y + selectionBoxInfo->lineHeight;
1937 // Store the min and max 'x' for each line.
1938 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1939 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1941 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1943 ++actualNumberOfQuads;
1945 // Whether to retrieve the next line.
1946 if( index == lastGlyphOfLine )
1948 // Retrieve the next line.
1951 // Get the last glyph of the new line.
1952 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1955 if( lineIndex < firstLineIndex + numberOfLines )
1957 // Keep the offset and height of the current selection box.
1958 const float currentLineOffset = selectionBoxInfo->lineOffset;
1959 const float currentLineHeight = selectionBoxInfo->lineHeight;
1961 // Get the selection box info for the next line.
1964 selectionBoxInfo->minX = MAX_FLOAT;
1965 selectionBoxInfo->maxX = MIN_FLOAT;
1967 // Update the line's vertical offset.
1968 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1970 // The line height is the addition of the line ascender and the line descender.
1971 // However, the line descender has a negative value, hence the subtraction.
1972 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1977 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1978 // The final width is calculated after 'boxifying' the selection.
1979 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1980 endIt = selectionBoxLinesInfo.End();
1984 const SelectionBoxInfo& info = *it;
1986 // Update the size of the highlighted text.
1987 highLightSize.height += info.lineHeight;
1988 minHighlightX = std::min( minHighlightX, info.minX );
1989 maxHighlightX = std::max( maxHighlightX, info.maxX );
1992 // Add extra geometry to 'boxify' the selection.
1994 if( 1u < numberOfLines )
1996 // Boxify the first line.
1997 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1998 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2000 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2001 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2006 quad.y = firstSelectionBoxLineInfo.lineOffset;
2007 quad.z = firstSelectionBoxLineInfo.minX;
2008 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2010 // Boxify at the beginning of the line.
2011 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2013 ++actualNumberOfQuads;
2015 // Update the size of the highlighted text.
2016 minHighlightX = 0.f;
2021 quad.x = firstSelectionBoxLineInfo.maxX;
2022 quad.y = firstSelectionBoxLineInfo.lineOffset;
2023 quad.z = mVisualModel->mControlSize.width;
2024 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2026 // Boxify at the end of the line.
2027 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2029 ++actualNumberOfQuads;
2031 // Update the size of the highlighted text.
2032 maxHighlightX = mVisualModel->mControlSize.width;
2035 // Boxify the central lines.
2036 if( 2u < numberOfLines )
2038 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2039 endIt = selectionBoxLinesInfo.End() - 1u;
2043 const SelectionBoxInfo& info = *it;
2046 quad.y = info.lineOffset;
2048 quad.w = info.lineOffset + info.lineHeight;
2050 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2052 ++actualNumberOfQuads;
2055 quad.y = info.lineOffset;
2056 quad.z = mVisualModel->mControlSize.width;
2057 quad.w = info.lineOffset + info.lineHeight;
2059 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2061 ++actualNumberOfQuads;
2064 // Update the size of the highlighted text.
2065 minHighlightX = 0.f;
2066 maxHighlightX = mVisualModel->mControlSize.width;
2069 // Boxify the last line.
2070 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2071 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2073 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2074 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2079 quad.y = lastSelectionBoxLineInfo.lineOffset;
2080 quad.z = lastSelectionBoxLineInfo.minX;
2081 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2083 // Boxify at the beginning of the line.
2084 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2086 ++actualNumberOfQuads;
2088 // Update the size of the highlighted text.
2089 minHighlightX = 0.f;
2094 quad.x = lastSelectionBoxLineInfo.maxX;
2095 quad.y = lastSelectionBoxLineInfo.lineOffset;
2096 quad.z = mVisualModel->mControlSize.width;
2097 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2099 // Boxify at the end of the line.
2100 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2102 ++actualNumberOfQuads;
2104 // Update the size of the highlighted text.
2105 maxHighlightX = mVisualModel->mControlSize.width;
2109 // Set the actual number of quads.
2110 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2112 // Sets the highlight's size and position. In decorator's coords.
2113 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2114 highLightSize.width = maxHighlightX - minHighlightX;
2116 highLightPosition.x = minHighlightX;
2117 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2118 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2120 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2122 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2124 CursorInfo primaryCursorInfo;
2125 GetCursorPosition( mEventData->mLeftSelectionPosition,
2126 primaryCursorInfo );
2128 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2130 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2132 primaryCursorInfo.lineOffset + mScrollPosition.y,
2133 primaryCursorInfo.lineHeight );
2135 CursorInfo secondaryCursorInfo;
2136 GetCursorPosition( mEventData->mRightSelectionPosition,
2137 secondaryCursorInfo );
2139 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2141 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2142 secondaryPosition.x,
2143 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2144 secondaryCursorInfo.lineHeight );
2147 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2148 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2150 // Set the flag to update the decorator.
2151 mEventData->mDecoratorUpdated = true;
2154 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2156 if( NULL == mEventData )
2158 // Nothing to do if there is no text input.
2162 if( IsShowingPlaceholderText() )
2164 // Nothing to do if there is the place-holder text.
2168 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2169 const Length numberOfLines = mVisualModel->mLines.Count();
2170 if( ( 0 == numberOfGlyphs ) ||
2171 ( 0 == numberOfLines ) )
2173 // Nothing to do if there is no text.
2177 // Find which word was selected
2178 CharacterIndex selectionStart( 0 );
2179 CharacterIndex selectionEnd( 0 );
2180 const bool indicesFound = FindSelectionIndices( mVisualModel,
2187 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2191 ChangeState( EventData::SELECTING );
2193 mEventData->mLeftSelectionPosition = selectionStart;
2194 mEventData->mRightSelectionPosition = selectionEnd;
2196 mEventData->mUpdateLeftSelectionPosition = true;
2197 mEventData->mUpdateRightSelectionPosition = true;
2198 mEventData->mUpdateHighlightBox = true;
2200 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2204 // Nothing to select. i.e. a white space, out of bounds
2205 ChangeState( EventData::EDITING );
2207 mEventData->mPrimaryCursorPosition = selectionEnd;
2209 mEventData->mUpdateCursorPosition = true;
2210 mEventData->mUpdateGrabHandlePosition = true;
2211 mEventData->mScrollAfterUpdatePosition = true;
2212 mEventData->mUpdateInputStyle = true;
2216 void Controller::Impl::SetPopupButtons()
2219 * Sets the Popup buttons to be shown depending on State.
2221 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2223 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2226 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2228 if( EventData::SELECTING == mEventData->mState )
2230 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2232 if( !IsClipboardEmpty() )
2234 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2235 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2238 if( !mEventData->mAllTextSelected )
2240 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2243 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2245 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2247 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2250 if( !IsClipboardEmpty() )
2252 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2253 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2256 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2258 if ( !IsClipboardEmpty() )
2260 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2261 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2265 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2268 void Controller::Impl::ChangeState( EventData::State newState )
2270 if( NULL == mEventData )
2272 // Nothing to do if there is no text input.
2276 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2278 if( mEventData->mState != newState )
2280 mEventData->mPreviousState = mEventData->mState;
2281 mEventData->mState = newState;
2283 switch( mEventData->mState )
2285 case EventData::INACTIVE:
2287 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2288 mEventData->mDecorator->StopCursorBlink();
2289 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2290 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2291 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2292 mEventData->mDecorator->SetHighlightActive( false );
2293 mEventData->mDecorator->SetPopupActive( false );
2294 mEventData->mDecoratorUpdated = true;
2297 case EventData::INTERRUPTED:
2299 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2300 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2301 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2302 mEventData->mDecorator->SetHighlightActive( false );
2303 mEventData->mDecorator->SetPopupActive( false );
2304 mEventData->mDecoratorUpdated = true;
2307 case EventData::SELECTING:
2309 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2310 mEventData->mDecorator->StopCursorBlink();
2311 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2312 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2313 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2314 mEventData->mDecorator->SetHighlightActive( true );
2315 if( mEventData->mGrabHandlePopupEnabled )
2318 mEventData->mDecorator->SetPopupActive( true );
2320 mEventData->mDecoratorUpdated = true;
2323 case EventData::EDITING:
2325 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2326 if( mEventData->mCursorBlinkEnabled )
2328 mEventData->mDecorator->StartCursorBlink();
2330 // Grab handle is not shown until a tap is received whilst EDITING
2331 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2332 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2333 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2334 mEventData->mDecorator->SetHighlightActive( false );
2335 if( mEventData->mGrabHandlePopupEnabled )
2337 mEventData->mDecorator->SetPopupActive( false );
2339 mEventData->mDecoratorUpdated = true;
2342 case EventData::EDITING_WITH_POPUP:
2344 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2346 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2347 if( mEventData->mCursorBlinkEnabled )
2349 mEventData->mDecorator->StartCursorBlink();
2351 if( mEventData->mSelectionEnabled )
2353 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2354 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2355 mEventData->mDecorator->SetHighlightActive( false );
2359 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2361 if( mEventData->mGrabHandlePopupEnabled )
2364 mEventData->mDecorator->SetPopupActive( true );
2366 mEventData->mDecoratorUpdated = true;
2369 case EventData::EDITING_WITH_GRAB_HANDLE:
2371 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2373 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2374 if( mEventData->mCursorBlinkEnabled )
2376 mEventData->mDecorator->StartCursorBlink();
2378 // Grab handle is not shown until a tap is received whilst EDITING
2379 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2380 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2381 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2382 mEventData->mDecorator->SetHighlightActive( false );
2383 if( mEventData->mGrabHandlePopupEnabled )
2385 mEventData->mDecorator->SetPopupActive( false );
2387 mEventData->mDecoratorUpdated = true;
2390 case EventData::SELECTION_HANDLE_PANNING:
2392 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2393 mEventData->mDecorator->StopCursorBlink();
2394 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2395 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2396 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2397 mEventData->mDecorator->SetHighlightActive( true );
2398 if( mEventData->mGrabHandlePopupEnabled )
2400 mEventData->mDecorator->SetPopupActive( false );
2402 mEventData->mDecoratorUpdated = true;
2405 case EventData::GRAB_HANDLE_PANNING:
2407 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2409 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2410 if( mEventData->mCursorBlinkEnabled )
2412 mEventData->mDecorator->StartCursorBlink();
2414 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2415 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2416 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2417 mEventData->mDecorator->SetHighlightActive( false );
2418 if( mEventData->mGrabHandlePopupEnabled )
2420 mEventData->mDecorator->SetPopupActive( false );
2422 mEventData->mDecoratorUpdated = true;
2425 case EventData::EDITING_WITH_PASTE_POPUP:
2427 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2429 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2430 if( mEventData->mCursorBlinkEnabled )
2432 mEventData->mDecorator->StartCursorBlink();
2435 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2436 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2437 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2438 mEventData->mDecorator->SetHighlightActive( false );
2440 if( mEventData->mGrabHandlePopupEnabled )
2443 mEventData->mDecorator->SetPopupActive( true );
2445 mEventData->mDecoratorUpdated = true;
2448 case EventData::TEXT_PANNING:
2450 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2451 mEventData->mDecorator->StopCursorBlink();
2452 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2453 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2454 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2456 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2457 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2458 mEventData->mDecorator->SetHighlightActive( true );
2461 if( mEventData->mGrabHandlePopupEnabled )
2463 mEventData->mDecorator->SetPopupActive( false );
2466 mEventData->mDecoratorUpdated = true;
2473 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2474 CursorInfo& cursorInfo )
2476 if( !IsShowingRealText() )
2478 // Do not want to use the place-holder text to set the cursor position.
2480 // Use the line's height of the font's family set to set the cursor's size.
2481 // If there is no font's family set, use the default font.
2482 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2484 cursorInfo.lineOffset = 0.f;
2485 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2486 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2488 switch( mLayoutEngine.GetHorizontalAlignment() )
2490 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2492 cursorInfo.primaryPosition.x = 0.f;
2495 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2497 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2500 case LayoutEngine::HORIZONTAL_ALIGN_END:
2502 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2507 // Nothing else to do.
2511 Text::GetCursorPosition( mVisualModel,
2517 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2519 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2521 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2522 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2524 if( 0.f > cursorInfo.primaryPosition.x )
2526 cursorInfo.primaryPosition.x = 0.f;
2529 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2530 if( cursorInfo.primaryPosition.x > edgeWidth )
2532 cursorInfo.primaryPosition.x = edgeWidth;
2537 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2539 if( NULL == mEventData )
2541 // Nothing to do if there is no text input.
2545 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2547 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2548 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2550 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2551 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2553 if( numberOfCharacters > 1u )
2555 const Script script = mLogicalModel->GetScript( index );
2556 if( HasLigatureMustBreak( script ) )
2558 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2559 numberOfCharacters = 1u;
2564 while( 0u == numberOfCharacters )
2567 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2571 if( index < mEventData->mPrimaryCursorPosition )
2573 cursorIndex -= numberOfCharacters;
2577 cursorIndex += numberOfCharacters;
2580 // Will update the cursor hook position.
2581 mEventData->mUpdateCursorHookPosition = true;
2586 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2588 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2589 if( NULL == mEventData )
2591 // Nothing to do if there is no text input.
2592 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2596 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2598 // Sets the cursor position.
2599 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2602 cursorInfo.primaryCursorHeight,
2603 cursorInfo.lineHeight );
2604 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2606 if( mEventData->mUpdateGrabHandlePosition )
2608 // Sets the grab handle position.
2609 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2611 cursorInfo.lineOffset + mScrollPosition.y,
2612 cursorInfo.lineHeight );
2615 if( cursorInfo.isSecondaryCursor )
2617 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2618 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2619 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2620 cursorInfo.secondaryCursorHeight,
2621 cursorInfo.lineHeight );
2622 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2625 // Set which cursors are active according the state.
2626 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2628 if( cursorInfo.isSecondaryCursor )
2630 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2634 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2639 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2642 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2645 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2646 const CursorInfo& cursorInfo )
2648 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2649 ( RIGHT_SELECTION_HANDLE != handleType ) )
2654 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2656 // Sets the handle's position.
2657 mEventData->mDecorator->SetPosition( handleType,
2659 cursorInfo.lineOffset + mScrollPosition.y,
2660 cursorInfo.lineHeight );
2662 // If selection handle at start of the text and other at end of the text then all text is selected.
2663 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2664 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2665 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2668 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2670 // Clamp between -space & 0.
2672 if( layoutSize.width > mVisualModel->mControlSize.width )
2674 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2675 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2676 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2678 mEventData->mDecoratorUpdated = true;
2682 mScrollPosition.x = 0.f;
2686 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2688 if( LayoutEngine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2690 // Nothing to do if the text is single line.
2694 // Clamp between -space & 0.
2695 if( layoutSize.height > mVisualModel->mControlSize.height )
2697 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2698 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2699 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2701 mEventData->mDecoratorUpdated = true;
2705 mScrollPosition.y = 0.f;
2709 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2711 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2713 // position is in actor's coords.
2714 const float positionEndX = position.x + cursorWidth;
2715 const float positionEndY = position.y + lineHeight;
2717 // Transform the position to decorator coords.
2718 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2719 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2721 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2722 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2724 if( decoratorPositionBeginX < 0.f )
2726 mScrollPosition.x = -position.x;
2728 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2730 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2733 if( decoratorPositionBeginY < 0.f )
2735 mScrollPosition.y = -position.y;
2737 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2739 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2743 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2745 // Get the current cursor position in decorator coords.
2746 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2748 // Calculate the offset to match the cursor position before the character was deleted.
2749 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2750 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2752 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2753 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2755 // Makes the new cursor position visible if needed.
2756 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2759 void Controller::Impl::RequestRelayout()
2761 if( NULL != mControlInterface )
2763 mControlInterface->RequestTextRelayout();
2769 } // namespace Toolkit