2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-run-container.h>
40 * @brief Struct used to calculate the selection box.
42 struct SelectionBoxInfo
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
54 const float MAX_FLOAT = std::numeric_limits<float>::max();
55 const float MIN_FLOAT = std::numeric_limits<float>::min();
56 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
69 EventData::EventData( DecoratorPtr decorator )
70 : mDecorator( decorator ),
72 mPlaceholderTextActive(),
73 mPlaceholderTextInactive(),
74 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77 mPrimaryCursorPosition( 0u ),
78 mLeftSelectionPosition( 0u ),
79 mRightSelectionPosition( 0u ),
80 mPreEditStartPosition( 0u ),
82 mCursorHookPositionX( 0.f ),
83 mIsShowingPlaceholderText( false ),
84 mPreEditFlag( false ),
85 mDecoratorUpdated( false ),
86 mCursorBlinkEnabled( true ),
87 mGrabHandleEnabled( true ),
88 mGrabHandlePopupEnabled( true ),
89 mSelectionEnabled( true ),
90 mUpdateCursorPosition( false ),
91 mUpdateGrabHandlePosition( false ),
92 mUpdateLeftSelectionPosition( false ),
93 mUpdateRightSelectionPosition( false ),
94 mIsLeftHandleSelected( false ),
95 mUpdateHighlightBox( false ),
96 mScrollAfterUpdatePosition( false ),
97 mScrollAfterDelete( false ),
98 mAllTextSelected( false ),
99 mUpdateInputStyle( false )
101 mImfManager = ImfManager::Get();
104 EventData::~EventData()
107 bool Controller::Impl::ProcessInputEvents()
109 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
110 if( NULL == mEventData )
112 // Nothing to do if there is no text input.
113 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
117 if( mEventData->mDecorator )
119 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
120 iter != mEventData->mEventQueue.end();
125 case Event::CURSOR_KEY_EVENT:
127 OnCursorKeyEvent( *iter );
130 case Event::TAP_EVENT:
135 case Event::LONG_PRESS_EVENT:
137 OnLongPressEvent( *iter );
140 case Event::PAN_EVENT:
145 case Event::GRAB_HANDLE_EVENT:
146 case Event::LEFT_SELECTION_HANDLE_EVENT:
147 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
149 OnHandleEvent( *iter );
154 OnSelectEvent( *iter );
157 case Event::SELECT_ALL:
166 if( mEventData->mUpdateCursorPosition ||
167 mEventData->mUpdateHighlightBox )
172 // The cursor must also be repositioned after inserts into the model
173 if( mEventData->mUpdateCursorPosition )
175 // Updates the cursor position and scrolls the text to make it visible.
176 CursorInfo cursorInfo;
177 // Calculate the cursor position from the new cursor index.
178 GetCursorPosition( mEventData->mPrimaryCursorPosition,
181 if( mEventData->mUpdateCursorHookPosition )
183 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
184 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
185 mEventData->mUpdateCursorHookPosition = false;
188 // Scroll first the text after delete ...
189 if( mEventData->mScrollAfterDelete )
191 ScrollTextToMatchCursor( cursorInfo );
194 // ... then, text can be scrolled to make the cursor visible.
195 if( mEventData->mScrollAfterUpdatePosition )
197 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
198 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
200 mEventData->mScrollAfterUpdatePosition = false;
201 mEventData->mScrollAfterDelete = false;
203 UpdateCursorPosition( cursorInfo );
205 mEventData->mDecoratorUpdated = true;
206 mEventData->mUpdateCursorPosition = false;
207 mEventData->mUpdateGrabHandlePosition = false;
211 CursorInfo leftHandleInfo;
212 CursorInfo rightHandleInfo;
214 if( mEventData->mUpdateHighlightBox )
216 GetCursorPosition( mEventData->mLeftSelectionPosition,
219 GetCursorPosition( mEventData->mRightSelectionPosition,
222 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
224 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
226 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
227 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
231 if( mEventData->mUpdateLeftSelectionPosition )
233 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
237 mEventData->mDecoratorUpdated = true;
238 mEventData->mUpdateLeftSelectionPosition = false;
241 if( mEventData->mUpdateRightSelectionPosition )
243 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
247 mEventData->mDecoratorUpdated = true;
248 mEventData->mUpdateRightSelectionPosition = false;
251 if( mEventData->mUpdateHighlightBox )
253 RepositionSelectionHandles();
255 mEventData->mUpdateLeftSelectionPosition = false;
256 mEventData->mUpdateRightSelectionPosition = false;
257 mEventData->mUpdateHighlightBox = false;
260 mEventData->mScrollAfterUpdatePosition = false;
263 if( mEventData->mUpdateInputStyle )
265 // Set the default style first.
266 RetrieveDefaultInputStyle( mEventData->mInputStyle );
268 // Get the character index from the cursor index.
269 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
271 // Retrieve the style from the style runs stored in the logical model.
272 mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
274 mEventData->mUpdateInputStyle = false;
277 mEventData->mEventQueue.clear();
279 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
281 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
282 mEventData->mDecoratorUpdated = false;
284 return decoratorUpdated;
287 void Controller::Impl::NotifyImfManager()
289 if( mEventData && mEventData->mImfManager )
291 CharacterIndex cursorPosition = GetLogicalCursorPosition();
293 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
295 // Update the cursor position by removing the initial white spaces.
296 if( cursorPosition < numberOfWhiteSpaces )
302 cursorPosition -= numberOfWhiteSpaces;
305 mEventData->mImfManager.SetCursorPosition( cursorPosition );
306 mEventData->mImfManager.NotifyCursorPosition();
310 void Controller::Impl::NotifyImfMultiLineStatus()
314 LayoutEngine::Layout layout = mLayoutEngine.GetLayout();
315 mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX );
319 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
321 CharacterIndex cursorPosition = 0u;
325 if( ( EventData::SELECTING == mEventData->mState ) ||
326 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
328 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
332 cursorPosition = mEventData->mPrimaryCursorPosition;
336 return cursorPosition;
339 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
341 Length numberOfWhiteSpaces = 0u;
343 // Get the buffer to the text.
344 Character* utf32CharacterBuffer = mLogicalModel->mText.Begin();
346 const Length totalNumberOfCharacters = mLogicalModel->mText.Count();
347 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
349 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
355 return numberOfWhiteSpaces;
358 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
360 // Get the total number of characters.
361 Length numberOfCharacters = mLogicalModel->mText.Count();
363 // Retrieve the text.
364 if( 0u != numberOfCharacters )
366 Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
370 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
372 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
373 mTextUpdateInfo.mStartGlyphIndex = 0u;
374 mTextUpdateInfo.mStartLineIndex = 0u;
375 numberOfCharacters = 0u;
377 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
378 if( 0u == numberOfParagraphs )
380 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
381 numberOfCharacters = 0u;
383 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
385 // Nothing else to do if there are no paragraphs.
389 // Find the paragraphs to be updated.
390 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
391 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
393 // Text is being added at the end of the current text.
394 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
396 // Text is being added in a new paragraph after the last character of the text.
397 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
398 numberOfCharacters = 0u;
399 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
401 mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count();
402 mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u;
404 // Nothing else to do;
408 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
412 Length numberOfCharactersToUpdate = 0u;
413 if( mTextUpdateInfo.mFullRelayoutNeeded )
415 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
419 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
421 mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
422 numberOfCharactersToUpdate,
423 paragraphsToBeUpdated );
426 if( 0u != paragraphsToBeUpdated.Count() )
428 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
429 const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
430 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
432 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
433 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
435 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
436 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
437 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
438 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
440 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
441 const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
443 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
447 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
451 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
452 mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
455 void Controller::Impl::ClearFullModelData( OperationsMask operations )
457 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
459 mLogicalModel->mLineBreakInfo.Clear();
460 mLogicalModel->mParagraphInfo.Clear();
463 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
465 mLogicalModel->mLineBreakInfo.Clear();
468 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
470 mLogicalModel->mScriptRuns.Clear();
473 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
475 mLogicalModel->mFontRuns.Clear();
478 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
480 if( NO_OPERATION != ( BIDI_INFO & operations ) )
482 mLogicalModel->mBidirectionalParagraphInfo.Clear();
483 mLogicalModel->mCharacterDirections.Clear();
486 if( NO_OPERATION != ( REORDER & operations ) )
488 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
489 for( Vector<BidirectionalLineInfoRun>::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(),
490 endIt = mLogicalModel->mBidirectionalLineInfo.End();
494 BidirectionalLineInfoRun& bidiLineInfo = *it;
496 free( bidiLineInfo.visualToLogicalMap );
497 bidiLineInfo.visualToLogicalMap = NULL;
499 mLogicalModel->mBidirectionalLineInfo.Clear();
503 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
505 mVisualModel->mGlyphs.Clear();
506 mVisualModel->mGlyphsToCharacters.Clear();
507 mVisualModel->mCharactersToGlyph.Clear();
508 mVisualModel->mCharactersPerGlyph.Clear();
509 mVisualModel->mGlyphsPerCharacter.Clear();
510 mVisualModel->mGlyphPositions.Clear();
513 if( NO_OPERATION != ( LAYOUT & operations ) )
515 mVisualModel->mLines.Clear();
518 if( NO_OPERATION != ( COLOR & operations ) )
520 mVisualModel->mColorIndices.Clear();
524 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
526 const CharacterIndex endIndexPlusOne = endIndex + 1u;
528 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
530 // Clear the line break info.
531 LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin();
533 mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
534 lineBreakInfoBuffer + endIndexPlusOne );
536 // Clear the paragraphs.
537 ClearCharacterRuns( startIndex,
539 mLogicalModel->mParagraphInfo );
542 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
544 // Clear the word break info.
545 WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin();
547 mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
548 wordBreakInfoBuffer + endIndexPlusOne );
551 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
553 // Clear the scripts.
554 ClearCharacterRuns( startIndex,
556 mLogicalModel->mScriptRuns );
559 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
562 ClearCharacterRuns( startIndex,
564 mLogicalModel->mFontRuns );
567 if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() )
569 if( NO_OPERATION != ( BIDI_INFO & operations ) )
571 // Clear the bidirectional paragraph info.
572 ClearCharacterRuns( startIndex,
574 mLogicalModel->mBidirectionalParagraphInfo );
576 // Clear the character's directions.
577 CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin();
579 mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
580 characterDirectionsBuffer + endIndexPlusOne );
583 if( NO_OPERATION != ( REORDER & operations ) )
585 uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count();
586 uint32_t endRemoveIndex = startRemoveIndex;
587 ClearCharacterRuns( startIndex,
589 mLogicalModel->mBidirectionalLineInfo,
593 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin();
595 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
596 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
597 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
601 BidirectionalLineInfoRun& bidiLineInfo = *it;
603 free( bidiLineInfo.visualToLogicalMap );
604 bidiLineInfo.visualToLogicalMap = NULL;
607 mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
608 bidirectionalLineInfoBuffer + endRemoveIndex );
613 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
615 const CharacterIndex endIndexPlusOne = endIndex + 1u;
616 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
618 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
619 GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
620 Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
622 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
623 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
625 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
627 // Update the character to glyph indices.
628 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
629 endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count();
633 CharacterIndex& index = *it;
634 index -= numberOfGlyphsRemoved;
637 // Clear the character to glyph conversion table.
638 mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
639 charactersToGlyphBuffer + endIndexPlusOne );
641 // Clear the glyphs per character table.
642 mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
643 glyphsPerCharacterBuffer + endIndexPlusOne );
645 // Clear the glyphs buffer.
646 GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
647 mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
648 glyphsBuffer + endGlyphIndexPlusOne );
650 CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
652 // Update the glyph to character indices.
653 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
654 endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count();
658 CharacterIndex& index = *it;
659 index -= numberOfCharactersRemoved;
662 // Clear the glyphs to characters buffer.
663 mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
664 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
666 // Clear the characters per glyph buffer.
667 Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
668 mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
669 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
671 // Clear the positions buffer.
672 Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin();
673 mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
674 positionsBuffer + endGlyphIndexPlusOne );
677 if( NO_OPERATION != ( LAYOUT & operations ) )
680 uint32_t startRemoveIndex = mVisualModel->mLines.Count();
681 uint32_t endRemoveIndex = startRemoveIndex;
682 ClearCharacterRuns( startIndex,
684 mVisualModel->mLines,
688 // Will update the glyph runs.
689 startRemoveIndex = mVisualModel->mLines.Count();
690 endRemoveIndex = startRemoveIndex;
691 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
692 endGlyphIndexPlusOne - 1u,
693 mVisualModel->mLines,
697 // Set the line index from where to insert the new laid-out lines.
698 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
700 LineRun* linesBuffer = mVisualModel->mLines.Begin();
701 mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
702 linesBuffer + endRemoveIndex );
705 if( NO_OPERATION != ( COLOR & operations ) )
707 if( 0u != mVisualModel->mColorIndices.Count() )
709 ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin();
710 mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
711 colorIndexBuffer + endGlyphIndexPlusOne );
716 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
718 if( mTextUpdateInfo.mClearAll ||
719 ( ( 0u == startIndex ) &&
720 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
722 ClearFullModelData( operations );
726 // Clear the model data related with characters.
727 ClearCharacterModelData( startIndex, endIndex, operations );
729 // Clear the model data related with glyphs.
730 ClearGlyphModelData( startIndex, endIndex, operations );
733 // The estimated number of lines. Used to avoid reallocations when layouting.
734 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
736 mVisualModel->ClearCaches();
739 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
741 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
743 // Calculate the operations to be done.
744 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
746 if( NO_OPERATION == operations )
748 // Nothing to do if no operations are pending and required.
752 Vector<Character>& utf32Characters = mLogicalModel->mText;
754 const Length numberOfCharacters = utf32Characters.Count();
756 // Index to the first character of the first paragraph to be updated.
757 CharacterIndex startIndex = 0u;
758 // Number of characters of the paragraphs to be removed.
759 Length paragraphCharacters = 0u;
761 CalculateTextUpdateIndices( paragraphCharacters );
762 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
764 if( mTextUpdateInfo.mClearAll ||
765 ( 0u != paragraphCharacters ) )
767 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
770 mTextUpdateInfo.mClearAll = false;
772 // Whether the model is updated.
773 bool updated = false;
775 Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
776 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
778 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
780 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
781 // calculate the bidirectional info for each 'paragraph'.
782 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
783 // is not shaped together).
784 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
786 SetLineBreakInfo( utf32Characters,
788 requestedNumberOfCharacters,
791 // Create the paragraph info.
792 mLogicalModel->CreateParagraphInfo( startIndex,
793 requestedNumberOfCharacters );
797 Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
798 if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
800 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
801 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
803 SetWordBreakInfo( utf32Characters,
805 requestedNumberOfCharacters,
810 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
811 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
813 Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
814 Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
816 if( getScripts || validateFonts )
818 // Validates the fonts assigned by the application or assigns default ones.
819 // It makes sure all the characters are going to be rendered by the correct font.
820 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
824 // Retrieves the scripts used in the text.
825 multilanguageSupport.SetScripts( utf32Characters,
827 requestedNumberOfCharacters,
833 // Validate the fonts set through the mark-up string.
834 Vector<FontDescriptionRun>& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns;
836 // Get the default font's description.
837 TextAbstraction::FontDescription defaultFontDescription;
838 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
839 if( NULL != mFontDefaults )
841 defaultFontDescription = mFontDefaults->mFontDescription;
842 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
845 // Validates the fonts. If there is a character with no assigned font it sets a default one.
846 // After this call, fonts are validated.
847 multilanguageSupport.ValidateFonts( utf32Characters,
850 defaultFontDescription,
853 requestedNumberOfCharacters,
859 Vector<Character> mirroredUtf32Characters;
860 bool textMirrored = false;
861 const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count();
862 if( NO_OPERATION != ( BIDI_INFO & operations ) )
864 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
865 bidirectionalInfo.Reserve( numberOfParagraphs );
867 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
868 SetBidirectionalInfo( utf32Characters,
872 requestedNumberOfCharacters,
875 if( 0u != bidirectionalInfo.Count() )
877 // Only set the character directions if there is right to left characters.
878 Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
879 GetCharactersDirection( bidirectionalInfo,
882 requestedNumberOfCharacters,
885 // This paragraph has right to left text. Some characters may need to be mirrored.
886 // TODO: consider if the mirrored string can be stored as well.
888 textMirrored = GetMirroredText( utf32Characters,
892 requestedNumberOfCharacters,
893 mirroredUtf32Characters );
897 // There is no right to left characters. Clear the directions vector.
898 mLogicalModel->mCharacterDirections.Clear();
903 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
904 Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
905 Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
906 Vector<GlyphIndex> newParagraphGlyphs;
907 newParagraphGlyphs.Reserve( numberOfParagraphs );
909 const Length currentNumberOfGlyphs = glyphs.Count();
910 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
912 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
914 ShapeText( textToShape,
919 mTextUpdateInfo.mStartGlyphIndex,
920 requestedNumberOfCharacters,
922 glyphsToCharactersMap,
924 newParagraphGlyphs );
926 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
927 mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
928 mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
932 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
934 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
936 GlyphInfo* glyphsBuffer = glyphs.Begin();
937 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
939 // Update the width and advance of all new paragraph characters.
940 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
942 const GlyphIndex index = *it;
943 GlyphInfo& glyph = *( glyphsBuffer + index );
945 glyph.xBearing = 0.f;
952 if( NO_OPERATION != ( COLOR & operations ) )
954 // Set the color runs in glyphs.
955 SetColorSegmentationInfo( mLogicalModel->mColorRuns,
956 mVisualModel->mCharactersToGlyph,
957 mVisualModel->mGlyphsPerCharacter,
959 mTextUpdateInfo.mStartGlyphIndex,
960 requestedNumberOfCharacters,
961 mVisualModel->mColors,
962 mVisualModel->mColorIndices );
967 if( ( NULL != mEventData ) &&
968 mEventData->mPreEditFlag &&
969 ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
971 // Add the underline for the pre-edit text.
972 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
973 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
975 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
976 const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
977 const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
978 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
980 GlyphRun underlineRun;
981 underlineRun.glyphIndex = glyphStart;
982 underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
984 // TODO: At the moment the underline runs are only for pre-edit.
985 mVisualModel->mUnderlineRuns.PushBack( underlineRun );
988 // The estimated number of lines. Used to avoid reallocations when layouting.
989 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() );
991 // Set the previous number of characters for the next time the text is updated.
992 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
997 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
999 // Sets the default text's color.
1000 inputStyle.textColor = mTextColor;
1001 inputStyle.isDefaultColor = true;
1003 inputStyle.familyName.clear();
1004 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1005 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1006 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1007 inputStyle.size = 0.f;
1009 inputStyle.familyDefined = false;
1010 inputStyle.weightDefined = false;
1011 inputStyle.widthDefined = false;
1012 inputStyle.slantDefined = false;
1013 inputStyle.sizeDefined = false;
1015 // Sets the default font's family name, weight, width, slant and size.
1018 if( mFontDefaults->familyDefined )
1020 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1021 inputStyle.familyDefined = true;
1024 if( mFontDefaults->weightDefined )
1026 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1027 inputStyle.weightDefined = true;
1030 if( mFontDefaults->widthDefined )
1032 inputStyle.width = mFontDefaults->mFontDescription.width;
1033 inputStyle.widthDefined = true;
1036 if( mFontDefaults->slantDefined )
1038 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1039 inputStyle.slantDefined = true;
1042 if( mFontDefaults->sizeDefined )
1044 inputStyle.size = mFontDefaults->mDefaultPointSize;
1045 inputStyle.sizeDefined = true;
1050 float Controller::Impl::GetDefaultFontLineHeight()
1052 FontId defaultFontId = 0u;
1053 if( NULL == mFontDefaults )
1055 TextAbstraction::FontDescription fontDescription;
1056 defaultFontId = mFontClient.GetFontId( fontDescription );
1060 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1063 Text::FontMetrics fontMetrics;
1064 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1066 return( fontMetrics.ascender - fontMetrics.descender );
1069 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1071 if( NULL == mEventData )
1073 // Nothing to do if there is no text input.
1077 int keyCode = event.p1.mInt;
1079 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1081 if( mEventData->mPrimaryCursorPosition > 0u )
1083 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1086 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1088 if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1090 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1093 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1095 // Get first the line index of the current cursor position index.
1096 CharacterIndex characterIndex = 0u;
1098 if( mEventData->mPrimaryCursorPosition > 0u )
1100 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1103 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1105 if( lineIndex > 0u )
1107 // Retrieve the cursor position info.
1108 CursorInfo cursorInfo;
1109 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1112 // Get the line above.
1113 const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1115 // Get the next hit 'y' point.
1116 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1118 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1119 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1122 mEventData->mCursorHookPositionX,
1126 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1128 // Get first the line index of the current cursor position index.
1129 CharacterIndex characterIndex = 0u;
1131 if( mEventData->mPrimaryCursorPosition > 0u )
1133 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1136 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1138 if( lineIndex + 1u < mVisualModel->mLines.Count() )
1140 // Retrieve the cursor position info.
1141 CursorInfo cursorInfo;
1142 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1145 // Get the line below.
1146 const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u );
1148 // Get the next hit 'y' point.
1149 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1151 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1152 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1155 mEventData->mCursorHookPositionX,
1160 mEventData->mUpdateCursorPosition = true;
1161 mEventData->mUpdateInputStyle = true;
1162 mEventData->mScrollAfterUpdatePosition = true;
1165 void Controller::Impl::OnTapEvent( const Event& event )
1167 if( NULL != mEventData )
1169 const unsigned int tapCount = event.p1.mUint;
1171 if( 1u == tapCount )
1173 if( IsShowingRealText() )
1175 // Convert from control's coords to text's coords.
1176 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1177 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1179 // Keep the tap 'x' position. Used to move the cursor.
1180 mEventData->mCursorHookPositionX = xPosition;
1182 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel,
1188 // When the cursor position is changing, delay cursor blinking
1189 mEventData->mDecorator->DelayCursorBlink();
1193 mEventData->mPrimaryCursorPosition = 0u;
1196 mEventData->mUpdateCursorPosition = true;
1197 mEventData->mUpdateGrabHandlePosition = true;
1198 mEventData->mScrollAfterUpdatePosition = true;
1199 mEventData->mUpdateInputStyle = true;
1201 // Notify the cursor position to the imf manager.
1202 if( mEventData->mImfManager )
1204 mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1205 mEventData->mImfManager.NotifyCursorPosition();
1211 void Controller::Impl::OnPanEvent( const Event& event )
1213 if( NULL == mEventData )
1215 // Nothing to do if there is no text input.
1219 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1220 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1222 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1224 // Nothing to do if scrolling is not enabled.
1228 const int state = event.p1.mInt;
1232 case Gesture::Started:
1234 // Will remove the cursor, handles or text's popup, ...
1235 ChangeState( EventData::TEXT_PANNING );
1238 case Gesture::Continuing:
1240 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1241 const Vector2 currentScroll = mScrollPosition;
1243 if( isHorizontalScrollEnabled )
1245 const float displacementX = event.p2.mFloat;
1246 mScrollPosition.x += displacementX;
1248 ClampHorizontalScroll( layoutSize );
1251 if( isVerticalScrollEnabled )
1253 const float displacementY = event.p3.mFloat;
1254 mScrollPosition.y += displacementY;
1256 ClampVerticalScroll( layoutSize );
1259 mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll );
1262 case Gesture::Finished:
1263 case Gesture::Cancelled: // FALLTHROUGH
1265 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1266 ChangeState( mEventData->mPreviousState );
1274 void Controller::Impl::OnLongPressEvent( const Event& event )
1276 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1278 if( EventData::EDITING == mEventData->mState )
1280 ChangeState ( EventData::EDITING_WITH_POPUP );
1281 mEventData->mDecoratorUpdated = true;
1285 void Controller::Impl::OnHandleEvent( const Event& event )
1287 if( NULL == mEventData )
1289 // Nothing to do if there is no text input.
1293 const unsigned int state = event.p1.mUint;
1294 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1295 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1297 if( HANDLE_PRESSED == state )
1299 // Convert from decorator's coords to text's coords.
1300 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1301 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1303 // Need to calculate the handle's new position.
1304 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel,
1310 if( Event::GRAB_HANDLE_EVENT == event.type )
1312 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1314 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1316 // Updates the cursor position if the handle's new position is different than the current one.
1317 mEventData->mUpdateCursorPosition = true;
1318 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1319 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1320 mEventData->mPrimaryCursorPosition = handleNewPosition;
1323 // 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.
1324 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1326 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1328 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1330 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1331 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1333 // Updates the highlight box if the handle's new position is different than the current one.
1334 mEventData->mUpdateHighlightBox = true;
1335 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1336 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1337 mEventData->mLeftSelectionPosition = handleNewPosition;
1340 // 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.
1341 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1343 // Will define the order to scroll the text to match the handle position.
1344 mEventData->mIsLeftHandleSelected = true;
1346 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1348 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1350 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1351 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1353 // Updates the highlight box if the handle's new position is different than the current one.
1354 mEventData->mUpdateHighlightBox = true;
1355 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1356 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1357 mEventData->mRightSelectionPosition = handleNewPosition;
1360 // 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.
1361 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1363 // Will define the order to scroll the text to match the handle position.
1364 mEventData->mIsLeftHandleSelected = false;
1366 } // end ( HANDLE_PRESSED == state )
1367 else if( ( HANDLE_RELEASED == state ) ||
1368 handleStopScrolling )
1370 CharacterIndex handlePosition = 0u;
1371 if( handleStopScrolling || isSmoothHandlePanEnabled )
1373 // Convert from decorator's coords to text's coords.
1374 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1375 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1377 handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1384 if( Event::GRAB_HANDLE_EVENT == event.type )
1386 mEventData->mUpdateCursorPosition = true;
1387 mEventData->mUpdateGrabHandlePosition = true;
1388 mEventData->mUpdateInputStyle = true;
1390 if( !IsClipboardEmpty() )
1392 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1395 if( handleStopScrolling || isSmoothHandlePanEnabled )
1397 mEventData->mScrollAfterUpdatePosition = true;
1398 mEventData->mPrimaryCursorPosition = handlePosition;
1401 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1403 ChangeState( EventData::SELECTING );
1405 mEventData->mUpdateHighlightBox = true;
1406 mEventData->mUpdateLeftSelectionPosition = true;
1407 mEventData->mUpdateRightSelectionPosition = true;
1409 if( handleStopScrolling || isSmoothHandlePanEnabled )
1411 mEventData->mScrollAfterUpdatePosition = true;
1413 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1414 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1416 mEventData->mLeftSelectionPosition = handlePosition;
1420 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1422 ChangeState( EventData::SELECTING );
1424 mEventData->mUpdateHighlightBox = true;
1425 mEventData->mUpdateRightSelectionPosition = true;
1426 mEventData->mUpdateLeftSelectionPosition = true;
1428 if( handleStopScrolling || isSmoothHandlePanEnabled )
1430 mEventData->mScrollAfterUpdatePosition = true;
1431 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1432 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1434 mEventData->mRightSelectionPosition = handlePosition;
1439 mEventData->mDecoratorUpdated = true;
1440 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1441 else if( HANDLE_SCROLLING == state )
1443 const float xSpeed = event.p2.mFloat;
1444 const float ySpeed = event.p3.mFloat;
1445 const Vector2& layoutSize = mVisualModel->GetLayoutSize();
1446 const Vector2 currentScrollPosition = mScrollPosition;
1448 mScrollPosition.x += xSpeed;
1449 mScrollPosition.y += ySpeed;
1451 ClampHorizontalScroll( layoutSize );
1452 ClampVerticalScroll( layoutSize );
1454 bool endOfScroll = false;
1455 if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) )
1457 // Notify the decorator there is no more text to scroll.
1458 // The decorator won't send more scroll events.
1459 mEventData->mDecorator->NotifyEndOfScroll();
1460 // Still need to set the position of the handle.
1464 // Set the position of the handle.
1465 const bool scrollRightDirection = xSpeed > 0.f;
1466 const bool scrollBottomDirection = ySpeed > 0.f;
1467 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1468 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1470 if( Event::GRAB_HANDLE_EVENT == event.type )
1472 ChangeState( EventData::GRAB_HANDLE_PANNING );
1474 // Get the grab handle position in decorator coords.
1475 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1477 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1479 // Position the grag handle close to either the left or right edge.
1480 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1483 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1485 position.x = mEventData->mCursorHookPositionX;
1487 // Position the grag handle close to either the top or bottom edge.
1488 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1491 // Get the new handle position.
1492 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1493 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1496 position.x - mScrollPosition.x,
1497 position.y - mScrollPosition.y );
1499 if( mEventData->mPrimaryCursorPosition != handlePosition )
1501 mEventData->mUpdateCursorPosition = true;
1502 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1503 mEventData->mScrollAfterUpdatePosition = true;
1504 mEventData->mPrimaryCursorPosition = handlePosition;
1506 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1508 // Updates the decorator if the soft handle panning is enabled.
1509 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1511 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1513 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1515 // Get the selection handle position in decorator coords.
1516 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1518 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1520 // Position the selection handle close to either the left or right edge.
1521 position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
1524 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1526 position.x = mEventData->mCursorHookPositionX;
1528 // Position the grag handle close to either the top or bottom edge.
1529 position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height;
1532 // Get the new handle position.
1533 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1534 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel,
1537 position.x - mScrollPosition.x,
1538 position.y - mScrollPosition.y );
1540 if( leftSelectionHandleEvent )
1542 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1544 if( differentHandles || endOfScroll )
1546 mEventData->mUpdateHighlightBox = true;
1547 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1548 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1549 mEventData->mLeftSelectionPosition = handlePosition;
1554 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1555 if( differentHandles || endOfScroll )
1557 mEventData->mUpdateHighlightBox = true;
1558 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1559 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1560 mEventData->mRightSelectionPosition = handlePosition;
1564 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1566 RepositionSelectionHandles();
1568 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1571 mEventData->mDecoratorUpdated = true;
1572 } // end ( HANDLE_SCROLLING == state )
1575 void Controller::Impl::OnSelectEvent( const Event& event )
1577 if( NULL == mEventData )
1579 // Nothing to do if there is no text.
1583 if( mEventData->mSelectionEnabled )
1585 // Convert from control's coords to text's coords.
1586 const float xPosition = event.p2.mFloat - mScrollPosition.x;
1587 const float yPosition = event.p3.mFloat - mScrollPosition.y;
1589 // Calculates the logical position from the x,y coords.
1590 RepositionSelectionHandles( xPosition,
1595 void Controller::Impl::OnSelectAllEvent()
1597 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1599 if( NULL == mEventData )
1601 // Nothing to do if there is no text.
1605 if( mEventData->mSelectionEnabled )
1607 ChangeState( EventData::SELECTING );
1609 mEventData->mLeftSelectionPosition = 0u;
1610 mEventData->mRightSelectionPosition = mLogicalModel->mText.Count();
1612 mEventData->mScrollAfterUpdatePosition = true;
1613 mEventData->mUpdateLeftSelectionPosition = true;
1614 mEventData->mUpdateRightSelectionPosition = true;
1615 mEventData->mUpdateHighlightBox = true;
1619 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1621 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1623 // Nothing to select if handles are in the same place.
1624 selectedText.clear();
1628 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1630 //Get start and end position of selection
1631 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1632 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1634 Vector<Character>& utf32Characters = mLogicalModel->mText;
1635 const Length numberOfCharacters = utf32Characters.Count();
1637 // Validate the start and end selection points
1638 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1640 //Get text as a UTF8 string
1641 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1643 if( deleteAfterRetrieval ) // Only delete text if copied successfully
1645 // Set as input style the style of the first deleted character.
1646 mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1648 mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1650 // Mark the paragraphs to be updated.
1651 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1652 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1654 // Delete text between handles
1655 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1656 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1657 utf32Characters.Erase( first, last );
1659 // Will show the cursor at the first character of the selection.
1660 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1664 // Will show the cursor at the last character of the selection.
1665 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1668 mEventData->mDecoratorUpdated = true;
1672 void Controller::Impl::ShowClipboard()
1676 mClipboard.ShowClipboard();
1680 void Controller::Impl::HideClipboard()
1682 if( mClipboard && mClipboardHideEnabled )
1684 mClipboard.HideClipboard();
1688 void Controller::Impl::SetClipboardHideEnable(bool enable)
1690 mClipboardHideEnabled = enable;
1693 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1695 //Send string to clipboard
1696 return ( mClipboard && mClipboard.SetItem( source ) );
1699 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1701 std::string selectedText;
1702 RetrieveSelection( selectedText, deleteAfterSending );
1703 CopyStringToClipboard( selectedText );
1704 ChangeState( EventData::EDITING );
1707 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString )
1711 retrievedString = mClipboard.GetItem( itemIndex );
1715 void Controller::Impl::RepositionSelectionHandles()
1717 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1718 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1720 if( selectionStart == selectionEnd )
1722 // Nothing to select if handles are in the same place.
1726 mEventData->mDecorator->ClearHighlights();
1728 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1729 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1730 const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
1731 const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
1732 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1733 const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
1734 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1736 const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
1737 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1738 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1740 // Swap the indices if the start is greater than the end.
1741 const bool indicesSwapped = selectionStart > selectionEnd;
1743 // Tell the decorator to flip the selection handles if needed.
1744 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1746 if( indicesSwapped )
1748 std::swap( selectionStart, selectionEnd );
1751 // Get the indices to the first and last selected glyphs.
1752 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1753 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1754 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1755 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1757 // Get the lines where the glyphs are laid-out.
1758 const LineRun* lineRun = mVisualModel->mLines.Begin();
1760 LineIndex lineIndex = 0u;
1761 Length numberOfLines = 0u;
1762 mVisualModel->GetNumberOfLines( glyphStart,
1763 1u + glyphEnd - glyphStart,
1766 const LineIndex firstLineIndex = lineIndex;
1768 // Create the structure to store some selection box info.
1769 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1770 selectionBoxLinesInfo.Resize( numberOfLines );
1772 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1773 selectionBoxInfo->minX = MAX_FLOAT;
1774 selectionBoxInfo->maxX = MIN_FLOAT;
1776 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1777 float minHighlightX = std::numeric_limits<float>::max();
1778 float maxHighlightX = std::numeric_limits<float>::min();
1780 Vector2 highLightPosition; // The highlight position in decorator's coords.
1782 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1784 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1785 selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines,
1788 // Transform to decorator's (control) coords.
1789 selectionBoxInfo->lineOffset += mScrollPosition.y;
1791 lineRun += firstLineIndex;
1793 // The line height is the addition of the line ascender and the line descender.
1794 // However, the line descender has a negative value, hence the subtraction.
1795 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1797 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1799 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1800 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1801 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
1803 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
1804 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1805 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
1807 // The number of quads of the selection box.
1808 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1809 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1811 // Count the actual number of quads.
1812 unsigned int actualNumberOfQuads = 0u;
1815 // Traverse the glyphs.
1816 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1818 const GlyphInfo& glyph = *( glyphsBuffer + index );
1819 const Vector2& position = *( positionsBuffer + index );
1821 if( splitStartGlyph )
1823 // 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.
1825 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1826 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1827 // Get the direction of the character.
1828 CharacterDirection isCurrentRightToLeft = false;
1829 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1831 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1834 // The end point could be in the middle of the ligature.
1835 // Calculate the number of characters selected.
1836 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1838 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1839 quad.y = selectionBoxInfo->lineOffset;
1840 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1841 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1843 // Store the min and max 'x' for each line.
1844 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1845 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1847 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1848 ++actualNumberOfQuads;
1850 splitStartGlyph = false;
1854 if( splitEndGlyph && ( index == glyphEnd ) )
1856 // 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.
1858 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1859 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1860 // Get the direction of the character.
1861 CharacterDirection isCurrentRightToLeft = false;
1862 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1864 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1867 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1869 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1870 quad.y = selectionBoxInfo->lineOffset;
1871 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1872 quad.w = quad.y + selectionBoxInfo->lineHeight;
1874 // Store the min and max 'x' for each line.
1875 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1876 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1878 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1880 ++actualNumberOfQuads;
1882 splitEndGlyph = false;
1886 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x;
1887 quad.y = selectionBoxInfo->lineOffset;
1888 quad.z = quad.x + glyph.advance;
1889 quad.w = quad.y + selectionBoxInfo->lineHeight;
1891 // Store the min and max 'x' for each line.
1892 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1893 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1895 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1897 ++actualNumberOfQuads;
1899 // Whether to retrieve the next line.
1900 if( index == lastGlyphOfLine )
1902 // Retrieve the next line.
1905 // Get the last glyph of the new line.
1906 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1909 if( lineIndex < firstLineIndex + numberOfLines )
1911 // Keep the offset and height of the current selection box.
1912 const float currentLineOffset = selectionBoxInfo->lineOffset;
1913 const float currentLineHeight = selectionBoxInfo->lineHeight;
1915 // Get the selection box info for the next line.
1918 selectionBoxInfo->minX = MAX_FLOAT;
1919 selectionBoxInfo->maxX = MIN_FLOAT;
1921 // Update the line's vertical offset.
1922 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1924 // The line height is the addition of the line ascender and the line descender.
1925 // However, the line descender has a negative value, hence the subtraction.
1926 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1931 // Traverses all the lines and updates the min and max 'x' positions and the total height.
1932 // The final width is calculated after 'boxifying' the selection.
1933 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1934 endIt = selectionBoxLinesInfo.End();
1938 const SelectionBoxInfo& info = *it;
1940 // Update the size of the highlighted text.
1941 highLightSize.height += info.lineHeight;
1942 minHighlightX = std::min( minHighlightX, info.minX );
1943 maxHighlightX = std::max( maxHighlightX, info.maxX );
1946 // Add extra geometry to 'boxify' the selection.
1948 if( 1u < numberOfLines )
1950 // Boxify the first line.
1951 lineRun = mVisualModel->mLines.Begin() + firstLineIndex;
1952 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
1954 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
1955 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
1960 quad.y = firstSelectionBoxLineInfo.lineOffset;
1961 quad.z = firstSelectionBoxLineInfo.minX;
1962 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1964 // Boxify at the beginning of the line.
1965 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1967 ++actualNumberOfQuads;
1969 // Update the size of the highlighted text.
1970 minHighlightX = 0.f;
1975 quad.x = firstSelectionBoxLineInfo.maxX;
1976 quad.y = firstSelectionBoxLineInfo.lineOffset;
1977 quad.z = mVisualModel->mControlSize.width;
1978 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
1980 // Boxify at the end of the line.
1981 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1983 ++actualNumberOfQuads;
1985 // Update the size of the highlighted text.
1986 maxHighlightX = mVisualModel->mControlSize.width;
1989 // Boxify the central lines.
1990 if( 2u < numberOfLines )
1992 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
1993 endIt = selectionBoxLinesInfo.End() - 1u;
1997 const SelectionBoxInfo& info = *it;
2000 quad.y = info.lineOffset;
2002 quad.w = info.lineOffset + info.lineHeight;
2004 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2006 ++actualNumberOfQuads;
2009 quad.y = info.lineOffset;
2010 quad.z = mVisualModel->mControlSize.width;
2011 quad.w = info.lineOffset + info.lineHeight;
2013 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2015 ++actualNumberOfQuads;
2018 // Update the size of the highlighted text.
2019 minHighlightX = 0.f;
2020 maxHighlightX = mVisualModel->mControlSize.width;
2023 // Boxify the last line.
2024 lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2025 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2027 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2028 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2033 quad.y = lastSelectionBoxLineInfo.lineOffset;
2034 quad.z = lastSelectionBoxLineInfo.minX;
2035 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2037 // Boxify at the beginning of the line.
2038 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2040 ++actualNumberOfQuads;
2042 // Update the size of the highlighted text.
2043 minHighlightX = 0.f;
2048 quad.x = lastSelectionBoxLineInfo.maxX;
2049 quad.y = lastSelectionBoxLineInfo.lineOffset;
2050 quad.z = mVisualModel->mControlSize.width;
2051 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2053 // Boxify at the end of the line.
2054 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2056 ++actualNumberOfQuads;
2058 // Update the size of the highlighted text.
2059 maxHighlightX = mVisualModel->mControlSize.width;
2063 // Set the actual number of quads.
2064 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2066 // Sets the highlight's size and position. In decorator's coords.
2067 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2068 highLightSize.width = maxHighlightX - minHighlightX;
2070 highLightPosition.x = minHighlightX;
2071 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2072 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2074 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2076 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2078 CursorInfo primaryCursorInfo;
2079 GetCursorPosition( mEventData->mLeftSelectionPosition,
2080 primaryCursorInfo );
2082 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition;
2084 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2086 primaryCursorInfo.lineOffset + mScrollPosition.y,
2087 primaryCursorInfo.lineHeight );
2089 CursorInfo secondaryCursorInfo;
2090 GetCursorPosition( mEventData->mRightSelectionPosition,
2091 secondaryCursorInfo );
2093 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition;
2095 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2096 secondaryPosition.x,
2097 secondaryCursorInfo.lineOffset + mScrollPosition.y,
2098 secondaryCursorInfo.lineHeight );
2101 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2102 mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2104 // Set the flag to update the decorator.
2105 mEventData->mDecoratorUpdated = true;
2108 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2110 if( NULL == mEventData )
2112 // Nothing to do if there is no text input.
2116 if( IsShowingPlaceholderText() )
2118 // Nothing to do if there is the place-holder text.
2122 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
2123 const Length numberOfLines = mVisualModel->mLines.Count();
2124 if( ( 0 == numberOfGlyphs ) ||
2125 ( 0 == numberOfLines ) )
2127 // Nothing to do if there is no text.
2131 // Find which word was selected
2132 CharacterIndex selectionStart( 0 );
2133 CharacterIndex selectionEnd( 0 );
2134 const bool indicesFound = FindSelectionIndices( mVisualModel,
2141 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2145 ChangeState( EventData::SELECTING );
2147 mEventData->mLeftSelectionPosition = selectionStart;
2148 mEventData->mRightSelectionPosition = selectionEnd;
2150 mEventData->mUpdateLeftSelectionPosition = true;
2151 mEventData->mUpdateRightSelectionPosition = true;
2152 mEventData->mUpdateHighlightBox = true;
2154 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2158 // Nothing to select. i.e. a white space, out of bounds
2159 ChangeState( EventData::EDITING );
2161 mEventData->mPrimaryCursorPosition = selectionEnd;
2163 mEventData->mUpdateCursorPosition = true;
2164 mEventData->mUpdateGrabHandlePosition = true;
2165 mEventData->mScrollAfterUpdatePosition = true;
2166 mEventData->mUpdateInputStyle = true;
2170 void Controller::Impl::SetPopupButtons()
2173 * Sets the Popup buttons to be shown depending on State.
2175 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2177 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2180 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2182 if( EventData::SELECTING == mEventData->mState )
2184 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2186 if( !IsClipboardEmpty() )
2188 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2189 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2192 if( !mEventData->mAllTextSelected )
2194 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2197 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2199 if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2201 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2204 if( !IsClipboardEmpty() )
2206 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2207 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2210 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2212 if ( !IsClipboardEmpty() )
2214 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2215 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2219 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2222 void Controller::Impl::ChangeState( EventData::State newState )
2224 if( NULL == mEventData )
2226 // Nothing to do if there is no text input.
2230 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2232 if( mEventData->mState != newState )
2234 mEventData->mPreviousState = mEventData->mState;
2235 mEventData->mState = newState;
2237 switch( mEventData->mState )
2239 case EventData::INACTIVE:
2241 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2242 mEventData->mDecorator->StopCursorBlink();
2243 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2244 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2245 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2246 mEventData->mDecorator->SetHighlightActive( false );
2247 mEventData->mDecorator->SetPopupActive( false );
2248 mEventData->mDecoratorUpdated = true;
2252 case EventData::INTERRUPTED:
2254 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2255 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2256 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2257 mEventData->mDecorator->SetHighlightActive( false );
2258 mEventData->mDecorator->SetPopupActive( false );
2259 mEventData->mDecoratorUpdated = true;
2263 case EventData::SELECTING:
2265 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2266 mEventData->mDecorator->StopCursorBlink();
2267 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2268 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2269 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2270 mEventData->mDecorator->SetHighlightActive( true );
2271 if( mEventData->mGrabHandlePopupEnabled )
2274 mEventData->mDecorator->SetPopupActive( true );
2276 mEventData->mDecoratorUpdated = true;
2279 case EventData::EDITING:
2281 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2282 if( mEventData->mCursorBlinkEnabled )
2284 mEventData->mDecorator->StartCursorBlink();
2286 // Grab handle is not shown until a tap is received whilst EDITING
2287 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2288 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2289 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2290 mEventData->mDecorator->SetHighlightActive( false );
2291 if( mEventData->mGrabHandlePopupEnabled )
2293 mEventData->mDecorator->SetPopupActive( false );
2295 mEventData->mDecoratorUpdated = true;
2299 case EventData::EDITING_WITH_POPUP:
2301 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2303 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2304 if( mEventData->mCursorBlinkEnabled )
2306 mEventData->mDecorator->StartCursorBlink();
2308 if( mEventData->mSelectionEnabled )
2310 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2311 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2312 mEventData->mDecorator->SetHighlightActive( false );
2316 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2318 if( mEventData->mGrabHandlePopupEnabled )
2321 mEventData->mDecorator->SetPopupActive( true );
2324 mEventData->mDecoratorUpdated = true;
2327 case EventData::EDITING_WITH_GRAB_HANDLE:
2329 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2331 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2332 if( mEventData->mCursorBlinkEnabled )
2334 mEventData->mDecorator->StartCursorBlink();
2336 // Grab handle is not shown until a tap is received whilst EDITING
2337 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2338 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2339 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2340 mEventData->mDecorator->SetHighlightActive( false );
2341 if( mEventData->mGrabHandlePopupEnabled )
2343 mEventData->mDecorator->SetPopupActive( false );
2345 mEventData->mDecoratorUpdated = true;
2349 case EventData::SELECTION_HANDLE_PANNING:
2351 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2352 mEventData->mDecorator->StopCursorBlink();
2353 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2354 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2355 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2356 mEventData->mDecorator->SetHighlightActive( true );
2357 if( mEventData->mGrabHandlePopupEnabled )
2359 mEventData->mDecorator->SetPopupActive( false );
2361 mEventData->mDecoratorUpdated = true;
2364 case EventData::GRAB_HANDLE_PANNING:
2366 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2368 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2369 if( mEventData->mCursorBlinkEnabled )
2371 mEventData->mDecorator->StartCursorBlink();
2373 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2374 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2375 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2376 mEventData->mDecorator->SetHighlightActive( false );
2377 if( mEventData->mGrabHandlePopupEnabled )
2379 mEventData->mDecorator->SetPopupActive( false );
2381 mEventData->mDecoratorUpdated = true;
2384 case EventData::EDITING_WITH_PASTE_POPUP:
2386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2388 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2389 if( mEventData->mCursorBlinkEnabled )
2391 mEventData->mDecorator->StartCursorBlink();
2394 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2395 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2396 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2397 mEventData->mDecorator->SetHighlightActive( false );
2399 if( mEventData->mGrabHandlePopupEnabled )
2402 mEventData->mDecorator->SetPopupActive( true );
2405 mEventData->mDecoratorUpdated = true;
2408 case EventData::TEXT_PANNING:
2410 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2411 mEventData->mDecorator->StopCursorBlink();
2412 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2413 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2414 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2416 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2417 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2418 mEventData->mDecorator->SetHighlightActive( true );
2421 if( mEventData->mGrabHandlePopupEnabled )
2423 mEventData->mDecorator->SetPopupActive( false );
2426 mEventData->mDecoratorUpdated = true;
2433 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2434 CursorInfo& cursorInfo )
2436 if( !IsShowingRealText() )
2438 // Do not want to use the place-holder text to set the cursor position.
2440 // Use the line's height of the font's family set to set the cursor's size.
2441 // If there is no font's family set, use the default font.
2442 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2444 cursorInfo.lineOffset = 0.f;
2445 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2446 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2448 switch( mLayoutEngine.GetHorizontalAlignment() )
2450 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
2452 cursorInfo.primaryPosition.x = 0.f;
2455 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
2457 cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width );
2460 case LayoutEngine::HORIZONTAL_ALIGN_END:
2462 cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2467 // Nothing else to do.
2471 Text::GetCursorPosition( mVisualModel,
2477 if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2479 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2481 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2482 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2484 if( 0.f > cursorInfo.primaryPosition.x )
2486 cursorInfo.primaryPosition.x = 0.f;
2489 const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2490 if( cursorInfo.primaryPosition.x > edgeWidth )
2492 cursorInfo.primaryPosition.x = edgeWidth;
2497 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2499 if( NULL == mEventData )
2501 // Nothing to do if there is no text input.
2505 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2507 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
2508 const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
2510 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2511 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2513 if( numberOfCharacters > 1u )
2515 const Script script = mLogicalModel->GetScript( index );
2516 if( HasLigatureMustBreak( script ) )
2518 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
2519 numberOfCharacters = 1u;
2524 while( 0u == numberOfCharacters )
2527 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2531 if( index < mEventData->mPrimaryCursorPosition )
2533 cursorIndex -= numberOfCharacters;
2537 cursorIndex += numberOfCharacters;
2540 // Will update the cursor hook position.
2541 mEventData->mUpdateCursorHookPosition = true;
2546 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2548 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2549 if( NULL == mEventData )
2551 // Nothing to do if there is no text input.
2552 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2556 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2558 // Sets the cursor position.
2559 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2562 cursorInfo.primaryCursorHeight,
2563 cursorInfo.lineHeight );
2564 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2566 if( mEventData->mUpdateGrabHandlePosition )
2568 // Sets the grab handle position.
2569 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2571 cursorInfo.lineOffset + mScrollPosition.y,
2572 cursorInfo.lineHeight );
2575 if( cursorInfo.isSecondaryCursor )
2577 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2578 cursorInfo.secondaryPosition.x + mScrollPosition.x,
2579 cursorInfo.secondaryPosition.y + mScrollPosition.y,
2580 cursorInfo.secondaryCursorHeight,
2581 cursorInfo.lineHeight );
2582 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y );
2585 // Set which cursors are active according the state.
2586 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2588 if( cursorInfo.isSecondaryCursor )
2590 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2594 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2599 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2602 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2605 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2606 const CursorInfo& cursorInfo )
2608 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2609 ( RIGHT_SELECTION_HANDLE != handleType ) )
2614 const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition;
2616 // Sets the handle's position.
2617 mEventData->mDecorator->SetPosition( handleType,
2619 cursorInfo.lineOffset + mScrollPosition.y,
2620 cursorInfo.lineHeight );
2622 // If selection handle at start of the text and other at end of the text then all text is selected.
2623 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2624 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2625 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
2628 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2630 // Clamp between -space & 0.
2632 if( layoutSize.width > mVisualModel->mControlSize.width )
2634 const float space = ( layoutSize.width - mVisualModel->mControlSize.width );
2635 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
2636 mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x;
2638 mEventData->mDecoratorUpdated = true;
2642 mScrollPosition.x = 0.f;
2646 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2648 // Clamp between -space & 0.
2649 if( layoutSize.height > mVisualModel->mControlSize.height )
2651 const float space = ( layoutSize.height - mVisualModel->mControlSize.height );
2652 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
2653 mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y;
2655 mEventData->mDecoratorUpdated = true;
2659 mScrollPosition.y = 0.f;
2663 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2665 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2667 // position is in actor's coords.
2668 const float positionEndX = position.x + cursorWidth;
2669 const float positionEndY = position.y + lineHeight;
2671 // Transform the position to decorator coords.
2672 const float decoratorPositionBeginX = position.x + mScrollPosition.x;
2673 const float decoratorPositionEndX = positionEndX + mScrollPosition.x;
2675 const float decoratorPositionBeginY = position.y + mScrollPosition.y;
2676 const float decoratorPositionEndY = positionEndY + mScrollPosition.y;
2678 if( decoratorPositionBeginX < 0.f )
2680 mScrollPosition.x = -position.x;
2682 else if( decoratorPositionEndX > mVisualModel->mControlSize.width )
2684 mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX;
2687 if( decoratorPositionBeginY < 0.f )
2689 mScrollPosition.y = -position.y;
2691 else if( decoratorPositionEndY > mVisualModel->mControlSize.height )
2693 mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY;
2697 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2699 // Get the current cursor position in decorator coords.
2700 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2702 // Calculate the offset to match the cursor position before the character was deleted.
2703 mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2704 mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2706 ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
2707 ClampVerticalScroll( mVisualModel->GetLayoutSize() );
2709 // Makes the new cursor position visible if needed.
2710 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2713 void Controller::Impl::RequestRelayout()
2715 mControlInterface.RequestTextRelayout();
2720 } // namespace Toolkit