2 * Copyright (c) 2017 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/public-api/rendering/renderer.h>
24 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/color-segmentation.h>
32 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
45 * @brief Struct used to calculate the selection box.
47 struct SelectionBoxInfo
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
59 const float MAX_FLOAT = std::numeric_limits<float>::max();
60 const float MIN_FLOAT = std::numeric_limits<float>::min();
61 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
63 #define MAKE_SHADER(A)#A
65 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
66 attribute mediump vec2 aPosition;
67 attribute mediump vec4 aColor;
68 varying mediump vec4 vColor;
69 uniform highp mat4 uMvpMatrix;
73 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
74 gl_Position = uMvpMatrix * position;
79 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
80 varying mediump vec4 vColor;
81 uniform lowp vec4 uColor;
85 gl_FragColor = vColor * uColor;
89 struct BackgroundVertex
91 Vector2 mPosition; ///< Vertex posiiton
92 Vector4 mColor; ///< Vertex color
97 Vector< BackgroundVertex > mVertices; ///< container of vertices
98 Vector< unsigned short > mIndices; ///< container of indices
101 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
102 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
103 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
104 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
105 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
118 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
119 : mDecorator( decorator ),
120 mInputMethodContext( inputMethodContext ),
121 mPlaceholderFont( NULL ),
122 mPlaceholderTextActive(),
123 mPlaceholderTextInactive(),
124 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
126 mInputStyleChangedQueue(),
127 mPreviousState( INACTIVE ),
129 mPrimaryCursorPosition( 0u ),
130 mLeftSelectionPosition( 0u ),
131 mRightSelectionPosition( 0u ),
132 mPreEditStartPosition( 0u ),
133 mPreEditLength( 0u ),
134 mCursorHookPositionX( 0.f ),
135 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
136 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
137 mIsShowingPlaceholderText( false ),
138 mPreEditFlag( false ),
139 mDecoratorUpdated( false ),
140 mCursorBlinkEnabled( true ),
141 mGrabHandleEnabled( true ),
142 mGrabHandlePopupEnabled( true ),
143 mSelectionEnabled( true ),
144 mUpdateCursorHookPosition( false ),
145 mUpdateCursorPosition( false ),
146 mUpdateGrabHandlePosition( false ),
147 mUpdateLeftSelectionPosition( false ),
148 mUpdateRightSelectionPosition( false ),
149 mIsLeftHandleSelected( false ),
150 mIsRightHandleSelected( false ),
151 mUpdateHighlightBox( false ),
152 mScrollAfterUpdatePosition( false ),
153 mScrollAfterDelete( false ),
154 mAllTextSelected( false ),
155 mUpdateInputStyle( false ),
156 mPasswordInput( false ),
157 mCheckScrollAmount( false ),
158 mIsPlaceholderPixelSize( false ),
159 mIsPlaceholderElideEnabled( false ),
160 mPlaceholderEllipsisFlag( false ),
161 mShiftSelectionFlag( true ),
162 mUpdateAlignment( false )
166 EventData::~EventData()
169 bool Controller::Impl::ProcessInputEvents()
171 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
172 if( NULL == mEventData )
174 // Nothing to do if there is no text input.
175 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
179 if( mEventData->mDecorator )
181 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
182 iter != mEventData->mEventQueue.end();
187 case Event::CURSOR_KEY_EVENT:
189 OnCursorKeyEvent( *iter );
192 case Event::TAP_EVENT:
197 case Event::LONG_PRESS_EVENT:
199 OnLongPressEvent( *iter );
202 case Event::PAN_EVENT:
207 case Event::GRAB_HANDLE_EVENT:
208 case Event::LEFT_SELECTION_HANDLE_EVENT:
209 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
211 OnHandleEvent( *iter );
216 OnSelectEvent( *iter );
219 case Event::SELECT_ALL:
224 case Event::SELECT_NONE:
233 if( mEventData->mUpdateCursorPosition ||
234 mEventData->mUpdateHighlightBox )
236 NotifyInputMethodContext();
239 // The cursor must also be repositioned after inserts into the model
240 if( mEventData->mUpdateCursorPosition )
242 // Updates the cursor position and scrolls the text to make it visible.
243 CursorInfo cursorInfo;
244 // Calculate the cursor position from the new cursor index.
245 GetCursorPosition( mEventData->mPrimaryCursorPosition,
248 if( mEventData->mUpdateCursorHookPosition )
250 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
251 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
252 mEventData->mUpdateCursorHookPosition = false;
255 // Scroll first the text after delete ...
256 if( mEventData->mScrollAfterDelete )
258 ScrollTextToMatchCursor( cursorInfo );
261 // ... then, text can be scrolled to make the cursor visible.
262 if( mEventData->mScrollAfterUpdatePosition )
264 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
265 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
267 mEventData->mScrollAfterUpdatePosition = false;
268 mEventData->mScrollAfterDelete = false;
270 UpdateCursorPosition( cursorInfo );
272 mEventData->mDecoratorUpdated = true;
273 mEventData->mUpdateCursorPosition = false;
274 mEventData->mUpdateGrabHandlePosition = false;
278 CursorInfo leftHandleInfo;
279 CursorInfo rightHandleInfo;
281 if( mEventData->mUpdateHighlightBox )
283 GetCursorPosition( mEventData->mLeftSelectionPosition,
286 GetCursorPosition( mEventData->mRightSelectionPosition,
289 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
291 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
293 CursorInfo& infoLeft = leftHandleInfo;
295 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
296 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
298 CursorInfo& infoRight = rightHandleInfo;
300 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
301 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
305 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
307 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
308 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
313 if( mEventData->mUpdateLeftSelectionPosition )
315 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
319 mEventData->mDecoratorUpdated = true;
320 mEventData->mUpdateLeftSelectionPosition = false;
323 if( mEventData->mUpdateRightSelectionPosition )
325 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
329 mEventData->mDecoratorUpdated = true;
330 mEventData->mUpdateRightSelectionPosition = false;
333 if( mEventData->mUpdateHighlightBox )
335 RepositionSelectionHandles();
337 mEventData->mUpdateLeftSelectionPosition = false;
338 mEventData->mUpdateRightSelectionPosition = false;
339 mEventData->mUpdateHighlightBox = false;
340 mEventData->mIsLeftHandleSelected = false;
341 mEventData->mIsRightHandleSelected = false;
344 mEventData->mScrollAfterUpdatePosition = false;
347 if( mEventData->mUpdateInputStyle )
349 // Keep a copy of the current input style.
350 InputStyle currentInputStyle;
351 currentInputStyle.Copy( mEventData->mInputStyle );
353 // Set the default style first.
354 RetrieveDefaultInputStyle( mEventData->mInputStyle );
356 // Get the character index from the cursor index.
357 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
359 // Retrieve the style from the style runs stored in the logical model.
360 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
362 // Compare if the input style has changed.
363 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
365 if( hasInputStyleChanged )
367 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
368 // Queue the input style changed signal.
369 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
372 mEventData->mUpdateInputStyle = false;
375 mEventData->mEventQueue.clear();
377 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
379 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
380 mEventData->mDecoratorUpdated = false;
382 return decoratorUpdated;
385 void Controller::Impl::NotifyInputMethodContext()
387 if( mEventData && mEventData->mInputMethodContext )
389 CharacterIndex cursorPosition = GetLogicalCursorPosition();
391 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
393 // Update the cursor position by removing the initial white spaces.
394 if( cursorPosition < numberOfWhiteSpaces )
400 cursorPosition -= numberOfWhiteSpaces;
403 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
404 mEventData->mInputMethodContext.NotifyCursorPosition();
408 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
410 if ( mEventData && mEventData->mInputMethodContext )
412 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
413 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
417 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
419 CharacterIndex cursorPosition = 0u;
423 if( ( EventData::SELECTING == mEventData->mState ) ||
424 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
426 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
430 cursorPosition = mEventData->mPrimaryCursorPosition;
434 return cursorPosition;
437 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
439 Length numberOfWhiteSpaces = 0u;
441 // Get the buffer to the text.
442 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
444 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
445 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
447 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
453 return numberOfWhiteSpaces;
456 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
458 // Get the total number of characters.
459 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
461 // Retrieve the text.
462 if( 0u != numberOfCharacters )
464 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
468 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
470 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
471 mTextUpdateInfo.mStartGlyphIndex = 0u;
472 mTextUpdateInfo.mStartLineIndex = 0u;
473 numberOfCharacters = 0u;
475 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
476 if( 0u == numberOfParagraphs )
478 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
479 numberOfCharacters = 0u;
481 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
483 // Nothing else to do if there are no paragraphs.
487 // Find the paragraphs to be updated.
488 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
489 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
491 // Text is being added at the end of the current text.
492 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
494 // Text is being added in a new paragraph after the last character of the text.
495 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
496 numberOfCharacters = 0u;
497 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
499 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
500 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
502 // Nothing else to do;
506 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
510 Length numberOfCharactersToUpdate = 0u;
511 if( mTextUpdateInfo.mFullRelayoutNeeded )
513 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
517 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
519 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
520 numberOfCharactersToUpdate,
521 paragraphsToBeUpdated );
524 if( 0u != paragraphsToBeUpdated.Count() )
526 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
527 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
528 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
530 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
531 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
533 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
534 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
535 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
536 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
538 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
539 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
541 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
545 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
549 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
550 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
553 void Controller::Impl::ClearFullModelData( OperationsMask operations )
555 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
557 mModel->mLogicalModel->mLineBreakInfo.Clear();
558 mModel->mLogicalModel->mParagraphInfo.Clear();
561 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
563 mModel->mLogicalModel->mScriptRuns.Clear();
566 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
568 mModel->mLogicalModel->mFontRuns.Clear();
571 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
573 if( NO_OPERATION != ( BIDI_INFO & operations ) )
575 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
576 mModel->mLogicalModel->mCharacterDirections.Clear();
579 if( NO_OPERATION != ( REORDER & operations ) )
581 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
582 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
583 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
587 BidirectionalLineInfoRun& bidiLineInfo = *it;
589 free( bidiLineInfo.visualToLogicalMap );
590 bidiLineInfo.visualToLogicalMap = NULL;
592 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
596 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
598 mModel->mVisualModel->mGlyphs.Clear();
599 mModel->mVisualModel->mGlyphsToCharacters.Clear();
600 mModel->mVisualModel->mCharactersToGlyph.Clear();
601 mModel->mVisualModel->mCharactersPerGlyph.Clear();
602 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
603 mModel->mVisualModel->mGlyphPositions.Clear();
606 if( NO_OPERATION != ( LAYOUT & operations ) )
608 mModel->mVisualModel->mLines.Clear();
611 if( NO_OPERATION != ( COLOR & operations ) )
613 mModel->mVisualModel->mColorIndices.Clear();
614 mModel->mVisualModel->mBackgroundColorIndices.Clear();
618 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
620 const CharacterIndex endIndexPlusOne = endIndex + 1u;
622 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
624 // Clear the line break info.
625 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
627 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
628 lineBreakInfoBuffer + endIndexPlusOne );
630 // Clear the paragraphs.
631 ClearCharacterRuns( startIndex,
633 mModel->mLogicalModel->mParagraphInfo );
636 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
638 // Clear the scripts.
639 ClearCharacterRuns( startIndex,
641 mModel->mLogicalModel->mScriptRuns );
644 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
647 ClearCharacterRuns( startIndex,
649 mModel->mLogicalModel->mFontRuns );
652 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
654 if( NO_OPERATION != ( BIDI_INFO & operations ) )
656 // Clear the bidirectional paragraph info.
657 ClearCharacterRuns( startIndex,
659 mModel->mLogicalModel->mBidirectionalParagraphInfo );
661 // Clear the character's directions.
662 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
664 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
665 characterDirectionsBuffer + endIndexPlusOne );
668 if( NO_OPERATION != ( REORDER & operations ) )
670 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
671 uint32_t endRemoveIndex = startRemoveIndex;
672 ClearCharacterRuns( startIndex,
674 mModel->mLogicalModel->mBidirectionalLineInfo,
678 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
680 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
681 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
682 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
686 BidirectionalLineInfoRun& bidiLineInfo = *it;
688 free( bidiLineInfo.visualToLogicalMap );
689 bidiLineInfo.visualToLogicalMap = NULL;
692 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
693 bidirectionalLineInfoBuffer + endRemoveIndex );
698 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
700 const CharacterIndex endIndexPlusOne = endIndex + 1u;
701 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
703 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
704 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
705 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
707 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
708 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
710 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
712 // Update the character to glyph indices.
713 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
714 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
718 CharacterIndex& index = *it;
719 index -= numberOfGlyphsRemoved;
722 // Clear the character to glyph conversion table.
723 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
724 charactersToGlyphBuffer + endIndexPlusOne );
726 // Clear the glyphs per character table.
727 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
728 glyphsPerCharacterBuffer + endIndexPlusOne );
730 // Clear the glyphs buffer.
731 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
732 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
733 glyphsBuffer + endGlyphIndexPlusOne );
735 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
737 // Update the glyph to character indices.
738 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
739 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
743 CharacterIndex& index = *it;
744 index -= numberOfCharactersRemoved;
747 // Clear the glyphs to characters buffer.
748 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
749 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
751 // Clear the characters per glyph buffer.
752 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
753 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
754 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
756 // Clear the positions buffer.
757 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
758 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
759 positionsBuffer + endGlyphIndexPlusOne );
762 if( NO_OPERATION != ( LAYOUT & operations ) )
765 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
766 uint32_t endRemoveIndex = startRemoveIndex;
767 ClearCharacterRuns( startIndex,
769 mModel->mVisualModel->mLines,
773 // Will update the glyph runs.
774 startRemoveIndex = mModel->mVisualModel->mLines.Count();
775 endRemoveIndex = startRemoveIndex;
776 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
777 endGlyphIndexPlusOne - 1u,
778 mModel->mVisualModel->mLines,
782 // Set the line index from where to insert the new laid-out lines.
783 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
785 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
786 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
787 linesBuffer + endRemoveIndex );
790 if( NO_OPERATION != ( COLOR & operations ) )
792 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
794 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
795 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
796 colorIndexBuffer + endGlyphIndexPlusOne );
799 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
801 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
802 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
803 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
808 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
810 if( mTextUpdateInfo.mClearAll ||
811 ( ( 0u == startIndex ) &&
812 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
814 ClearFullModelData( operations );
818 // Clear the model data related with characters.
819 ClearCharacterModelData( startIndex, endIndex, operations );
821 // Clear the model data related with glyphs.
822 ClearGlyphModelData( startIndex, endIndex, operations );
825 // The estimated number of lines. Used to avoid reallocations when layouting.
826 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
828 mModel->mVisualModel->ClearCaches();
831 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
833 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
835 // Calculate the operations to be done.
836 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
838 if( NO_OPERATION == operations )
840 // Nothing to do if no operations are pending and required.
844 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
845 Vector<Character> displayCharacters;
846 bool useHiddenText = false;
847 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
849 mHiddenInput->Substitute( srcCharacters,displayCharacters );
850 useHiddenText = true;
853 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
854 const Length numberOfCharacters = utf32Characters.Count();
856 // Index to the first character of the first paragraph to be updated.
857 CharacterIndex startIndex = 0u;
858 // Number of characters of the paragraphs to be removed.
859 Length paragraphCharacters = 0u;
861 CalculateTextUpdateIndices( paragraphCharacters );
863 // Check whether the indices for updating the text is valid
864 if ( numberOfCharacters > 0u &&
865 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
866 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
868 std::string currentText;
869 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
871 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
872 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
874 // Dump mTextUpdateInfo
875 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
876 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
877 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
878 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
879 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
880 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
881 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
882 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
883 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
884 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
885 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
886 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
887 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
892 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
894 if( mTextUpdateInfo.mClearAll ||
895 ( 0u != paragraphCharacters ) )
897 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
900 mTextUpdateInfo.mClearAll = false;
902 // Whether the model is updated.
903 bool updated = false;
905 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
906 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
908 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
910 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
911 // calculate the bidirectional info for each 'paragraph'.
912 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
913 // is not shaped together).
914 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
916 SetLineBreakInfo( utf32Characters,
918 requestedNumberOfCharacters,
921 // Create the paragraph info.
922 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
923 requestedNumberOfCharacters );
927 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
928 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
930 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
931 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
933 if( getScripts || validateFonts )
935 // Validates the fonts assigned by the application or assigns default ones.
936 // It makes sure all the characters are going to be rendered by the correct font.
937 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
941 // Retrieves the scripts used in the text.
942 multilanguageSupport.SetScripts( utf32Characters,
944 requestedNumberOfCharacters,
950 // Validate the fonts set through the mark-up string.
951 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
953 // Get the default font's description.
954 TextAbstraction::FontDescription defaultFontDescription;
955 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
957 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
959 // If the placeholder font is set specifically, only placeholder font is changed.
960 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
961 if( mEventData->mPlaceholderFont->sizeDefined )
963 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
966 else if( NULL != mFontDefaults )
968 // Set the normal font and the placeholder font.
969 defaultFontDescription = mFontDefaults->mFontDescription;
971 if( mTextFitEnabled )
973 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
977 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
981 // Validates the fonts. If there is a character with no assigned font it sets a default one.
982 // After this call, fonts are validated.
983 multilanguageSupport.ValidateFonts( utf32Characters,
986 defaultFontDescription,
989 requestedNumberOfCharacters,
995 Vector<Character> mirroredUtf32Characters;
996 bool textMirrored = false;
997 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
998 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1000 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1001 bidirectionalInfo.Reserve( numberOfParagraphs );
1003 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1004 SetBidirectionalInfo( utf32Characters,
1008 requestedNumberOfCharacters,
1010 mModel->mMatchSystemLanguageDirection,
1013 if( 0u != bidirectionalInfo.Count() )
1015 // Only set the character directions if there is right to left characters.
1016 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1017 GetCharactersDirection( bidirectionalInfo,
1020 requestedNumberOfCharacters,
1023 // This paragraph has right to left text. Some characters may need to be mirrored.
1024 // TODO: consider if the mirrored string can be stored as well.
1026 textMirrored = GetMirroredText( utf32Characters,
1030 requestedNumberOfCharacters,
1031 mirroredUtf32Characters );
1035 // There is no right to left characters. Clear the directions vector.
1036 mModel->mLogicalModel->mCharacterDirections.Clear();
1041 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1042 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1043 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1044 Vector<GlyphIndex> newParagraphGlyphs;
1045 newParagraphGlyphs.Reserve( numberOfParagraphs );
1047 const Length currentNumberOfGlyphs = glyphs.Count();
1048 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1050 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1052 ShapeText( textToShape,
1057 mTextUpdateInfo.mStartGlyphIndex,
1058 requestedNumberOfCharacters,
1060 glyphsToCharactersMap,
1062 newParagraphGlyphs );
1064 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1065 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1066 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1070 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1072 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1074 GlyphInfo* glyphsBuffer = glyphs.Begin();
1075 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1077 // Update the width and advance of all new paragraph characters.
1078 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1080 const GlyphIndex index = *it;
1081 GlyphInfo& glyph = *( glyphsBuffer + index );
1083 glyph.xBearing = 0.f;
1085 glyph.advance = 0.f;
1090 if( ( NULL != mEventData ) &&
1091 mEventData->mPreEditFlag &&
1092 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1094 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1095 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1096 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1098 // Check the type of preedit and run it.
1099 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1101 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1102 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1103 type = attrData.preeditType;
1105 // Check the number of commit characters for the start position.
1106 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1107 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1111 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1113 // Add the underline for the pre-edit text.
1114 GlyphRun underlineRun;
1115 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1116 underlineRun.numberOfGlyphs = numberOfIndices;
1117 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1120 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1122 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1123 ColorRun backgroundColorRun;
1124 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1125 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1126 backgroundColorRun.color = textColor;
1127 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1129 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1130 Vector<ColorRun> colorRuns;
1131 colorRuns.Resize( 1u );
1132 ColorRun& colorRun = *( colorRuns.Begin() );
1133 colorRun.color = backgroundColor;
1134 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1135 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1137 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1140 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1142 ColorRun backgroundColorRun;
1143 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1144 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1145 backgroundColorRun.color = LIGHT_BLUE;
1146 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1149 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1151 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1152 ColorRun backgroundColorRun;
1153 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1154 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1155 backgroundColorRun.color = BACKGROUND_SUB4;
1156 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1158 GlyphRun underlineRun;
1159 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1160 underlineRun.numberOfGlyphs = numberOfIndices;
1161 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1164 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1166 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1167 ColorRun backgroundColorRun;
1168 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1169 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1170 backgroundColorRun.color = BACKGROUND_SUB5;
1171 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1173 GlyphRun underlineRun;
1174 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1175 underlineRun.numberOfGlyphs = numberOfIndices;
1176 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1179 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1181 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1182 ColorRun backgroundColorRun;
1183 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1184 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1185 backgroundColorRun.color = BACKGROUND_SUB6;
1186 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1188 GlyphRun underlineRun;
1189 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1190 underlineRun.numberOfGlyphs = numberOfIndices;
1191 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1194 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1196 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1197 ColorRun backgroundColorRun;
1198 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1199 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1200 backgroundColorRun.color = BACKGROUND_SUB7;
1201 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1203 GlyphRun underlineRun;
1204 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1205 underlineRun.numberOfGlyphs = numberOfIndices;
1206 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1209 case Dali::InputMethodContext::PreeditStyle::NONE:
1220 if( NO_OPERATION != ( COLOR & operations ) )
1222 // Set the color runs in glyphs.
1223 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1224 mModel->mVisualModel->mCharactersToGlyph,
1225 mModel->mVisualModel->mGlyphsPerCharacter,
1227 mTextUpdateInfo.mStartGlyphIndex,
1228 requestedNumberOfCharacters,
1229 mModel->mVisualModel->mColors,
1230 mModel->mVisualModel->mColorIndices );
1232 // Set the background color runs in glyphs.
1233 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1234 mModel->mVisualModel->mCharactersToGlyph,
1235 mModel->mVisualModel->mGlyphsPerCharacter,
1237 mTextUpdateInfo.mStartGlyphIndex,
1238 requestedNumberOfCharacters,
1239 mModel->mVisualModel->mBackgroundColors,
1240 mModel->mVisualModel->mBackgroundColorIndices );
1246 // The estimated number of lines. Used to avoid reallocations when layouting.
1247 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1249 // Set the previous number of characters for the next time the text is updated.
1250 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1255 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1257 // Sets the default text's color.
1258 inputStyle.textColor = mTextColor;
1259 inputStyle.isDefaultColor = true;
1261 inputStyle.familyName.clear();
1262 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1263 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1264 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1265 inputStyle.size = 0.f;
1267 inputStyle.lineSpacing = 0.f;
1269 inputStyle.underlineProperties.clear();
1270 inputStyle.shadowProperties.clear();
1271 inputStyle.embossProperties.clear();
1272 inputStyle.outlineProperties.clear();
1274 inputStyle.isFamilyDefined = false;
1275 inputStyle.isWeightDefined = false;
1276 inputStyle.isWidthDefined = false;
1277 inputStyle.isSlantDefined = false;
1278 inputStyle.isSizeDefined = false;
1280 inputStyle.isLineSpacingDefined = false;
1282 inputStyle.isUnderlineDefined = false;
1283 inputStyle.isShadowDefined = false;
1284 inputStyle.isEmbossDefined = false;
1285 inputStyle.isOutlineDefined = false;
1287 // Sets the default font's family name, weight, width, slant and size.
1290 if( mFontDefaults->familyDefined )
1292 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1293 inputStyle.isFamilyDefined = true;
1296 if( mFontDefaults->weightDefined )
1298 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1299 inputStyle.isWeightDefined = true;
1302 if( mFontDefaults->widthDefined )
1304 inputStyle.width = mFontDefaults->mFontDescription.width;
1305 inputStyle.isWidthDefined = true;
1308 if( mFontDefaults->slantDefined )
1310 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1311 inputStyle.isSlantDefined = true;
1314 if( mFontDefaults->sizeDefined )
1316 inputStyle.size = mFontDefaults->mDefaultPointSize;
1317 inputStyle.isSizeDefined = true;
1322 float Controller::Impl::GetDefaultFontLineHeight()
1324 FontId defaultFontId = 0u;
1325 if( NULL == mFontDefaults )
1327 TextAbstraction::FontDescription fontDescription;
1328 defaultFontId = mFontClient.GetFontId( fontDescription );
1332 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1335 Text::FontMetrics fontMetrics;
1336 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1338 return( fontMetrics.ascender - fontMetrics.descender );
1341 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1343 if( NULL == mEventData || !IsShowingRealText() )
1345 // Nothing to do if there is no text input.
1349 int keyCode = event.p1.mInt;
1350 bool isShiftModifier = event.p2.mBool;
1352 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1354 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1356 if( mEventData->mPrimaryCursorPosition > 0u )
1358 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1360 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1364 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1368 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1370 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1372 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1374 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1378 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1382 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1384 // Ignore Shift-Up for text selection for now.
1386 // Get first the line index of the current cursor position index.
1387 CharacterIndex characterIndex = 0u;
1389 if( mEventData->mPrimaryCursorPosition > 0u )
1391 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1394 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1395 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1397 // Retrieve the cursor position info.
1398 CursorInfo cursorInfo;
1399 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1402 // Get the line above.
1403 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1405 // Get the next hit 'y' point.
1406 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1408 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1409 bool matchedCharacter = false;
1410 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1411 mModel->mLogicalModel,
1413 mEventData->mCursorHookPositionX,
1415 CharacterHitTest::TAP,
1418 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1420 // Ignore Shift-Down for text selection for now.
1422 // Get first the line index of the current cursor position index.
1423 CharacterIndex characterIndex = 0u;
1425 if( mEventData->mPrimaryCursorPosition > 0u )
1427 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1430 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1432 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1434 // Retrieve the cursor position info.
1435 CursorInfo cursorInfo;
1436 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1439 // Get the line below.
1440 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1442 // Get the next hit 'y' point.
1443 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1445 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1446 bool matchedCharacter = false;
1447 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1448 mModel->mLogicalModel,
1450 mEventData->mCursorHookPositionX,
1452 CharacterHitTest::TAP,
1457 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1459 // Update selection position after moving the cursor
1460 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1461 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1464 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1466 // Handle text selection
1467 bool selecting = false;
1469 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1471 // Shift-Left/Right to select the text
1472 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1473 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1475 mEventData->mRightSelectionPosition += cursorPositionDelta;
1479 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1481 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1487 // Notify the cursor position to the InputMethodContext.
1488 if( mEventData->mInputMethodContext )
1490 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1491 mEventData->mInputMethodContext.NotifyCursorPosition();
1494 ChangeState( EventData::SELECTING );
1496 mEventData->mUpdateLeftSelectionPosition = true;
1497 mEventData->mUpdateRightSelectionPosition = true;
1498 mEventData->mUpdateGrabHandlePosition = true;
1499 mEventData->mUpdateHighlightBox = true;
1501 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1502 if( mEventData->mGrabHandlePopupEnabled )
1504 mEventData->mDecorator->SetPopupActive( false );
1510 // Handle normal cursor move
1511 ChangeState( EventData::EDITING );
1512 mEventData->mUpdateCursorPosition = true;
1515 mEventData->mUpdateInputStyle = true;
1516 mEventData->mScrollAfterUpdatePosition = true;
1519 void Controller::Impl::OnTapEvent( const Event& event )
1521 if( NULL != mEventData )
1523 const unsigned int tapCount = event.p1.mUint;
1525 if( 1u == tapCount )
1527 if( IsShowingRealText() )
1529 // Convert from control's coords to text's coords.
1530 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1531 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1533 // Keep the tap 'x' position. Used to move the cursor.
1534 mEventData->mCursorHookPositionX = xPosition;
1536 // Whether to touch point hits on a glyph.
1537 bool matchedCharacter = false;
1538 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1539 mModel->mLogicalModel,
1543 CharacterHitTest::TAP,
1546 // When the cursor position is changing, delay cursor blinking
1547 mEventData->mDecorator->DelayCursorBlink();
1551 mEventData->mPrimaryCursorPosition = 0u;
1554 // Update selection position after tapping
1555 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1556 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1558 mEventData->mUpdateCursorPosition = true;
1559 mEventData->mUpdateGrabHandlePosition = true;
1560 mEventData->mScrollAfterUpdatePosition = true;
1561 mEventData->mUpdateInputStyle = true;
1563 // Notify the cursor position to the InputMethodContext.
1564 if( mEventData->mInputMethodContext )
1566 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1567 mEventData->mInputMethodContext.NotifyCursorPosition();
1570 else if( 2u == tapCount )
1572 if( mEventData->mSelectionEnabled )
1574 // Convert from control's coords to text's coords.
1575 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1576 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1578 // Calculates the logical position from the x,y coords.
1579 RepositionSelectionHandles( xPosition,
1581 mEventData->mDoubleTapAction );
1587 void Controller::Impl::OnPanEvent( const Event& event )
1589 if( NULL == mEventData )
1591 // Nothing to do if there is no text input.
1595 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1596 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1598 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1600 // Nothing to do if scrolling is not enabled.
1604 const GestureState state = static_cast<GestureState>( event.p1.mInt );
1607 case GestureState::STARTED:
1609 // Will remove the cursor, handles or text's popup, ...
1610 ChangeState( EventData::TEXT_PANNING );
1613 case GestureState::CONTINUING:
1615 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1616 const Vector2 currentScroll = mModel->mScrollPosition;
1618 if( isHorizontalScrollEnabled )
1620 const float displacementX = event.p2.mFloat;
1621 mModel->mScrollPosition.x += displacementX;
1623 ClampHorizontalScroll( layoutSize );
1626 if( isVerticalScrollEnabled )
1628 const float displacementY = event.p3.mFloat;
1629 mModel->mScrollPosition.y += displacementY;
1631 ClampVerticalScroll( layoutSize );
1634 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1637 case GestureState::FINISHED:
1638 case GestureState::CANCELLED: // FALLTHROUGH
1640 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1641 ChangeState( mEventData->mPreviousState );
1649 void Controller::Impl::OnLongPressEvent( const Event& event )
1651 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1653 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1655 ChangeState( EventData::EDITING_WITH_POPUP );
1656 mEventData->mDecoratorUpdated = true;
1657 mEventData->mUpdateInputStyle = true;
1661 if( mEventData->mSelectionEnabled )
1663 // Convert from control's coords to text's coords.
1664 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1665 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1667 // Calculates the logical position from the x,y coords.
1668 RepositionSelectionHandles( xPosition,
1670 mEventData->mLongPressAction );
1675 void Controller::Impl::OnHandleEvent( const Event& event )
1677 if( NULL == mEventData )
1679 // Nothing to do if there is no text input.
1683 const unsigned int state = event.p1.mUint;
1684 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1685 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1687 if( HANDLE_PRESSED == state )
1689 // Convert from decorator's coords to text's coords.
1690 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1691 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1693 // Need to calculate the handle's new position.
1694 bool matchedCharacter = false;
1695 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1696 mModel->mLogicalModel,
1700 CharacterHitTest::SCROLL,
1703 if( Event::GRAB_HANDLE_EVENT == event.type )
1705 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1707 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1709 // Updates the cursor position if the handle's new position is different than the current one.
1710 mEventData->mUpdateCursorPosition = true;
1711 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1712 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1713 mEventData->mPrimaryCursorPosition = handleNewPosition;
1716 // 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.
1717 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1719 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1721 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1723 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1724 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1726 // Updates the highlight box if the handle's new position is different than the current one.
1727 mEventData->mUpdateHighlightBox = true;
1728 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1729 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1730 mEventData->mLeftSelectionPosition = handleNewPosition;
1733 // 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.
1734 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1736 // Will define the order to scroll the text to match the handle position.
1737 mEventData->mIsLeftHandleSelected = true;
1738 mEventData->mIsRightHandleSelected = false;
1740 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1742 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1744 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1745 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1747 // Updates the highlight box if the handle's new position is different than the current one.
1748 mEventData->mUpdateHighlightBox = true;
1749 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1750 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1751 mEventData->mRightSelectionPosition = handleNewPosition;
1754 // 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.
1755 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1757 // Will define the order to scroll the text to match the handle position.
1758 mEventData->mIsLeftHandleSelected = false;
1759 mEventData->mIsRightHandleSelected = true;
1761 } // end ( HANDLE_PRESSED == state )
1762 else if( ( HANDLE_RELEASED == state ) ||
1763 handleStopScrolling )
1765 CharacterIndex handlePosition = 0u;
1766 if( handleStopScrolling || isSmoothHandlePanEnabled )
1768 // Convert from decorator's coords to text's coords.
1769 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1770 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1772 bool matchedCharacter = false;
1773 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1774 mModel->mLogicalModel,
1778 CharacterHitTest::SCROLL,
1782 if( Event::GRAB_HANDLE_EVENT == event.type )
1784 mEventData->mUpdateCursorPosition = true;
1785 mEventData->mUpdateGrabHandlePosition = true;
1786 mEventData->mUpdateInputStyle = true;
1788 if( !IsClipboardEmpty() )
1790 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1793 if( handleStopScrolling || isSmoothHandlePanEnabled )
1795 mEventData->mScrollAfterUpdatePosition = true;
1796 mEventData->mPrimaryCursorPosition = handlePosition;
1799 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1801 ChangeState( EventData::SELECTING );
1803 mEventData->mUpdateHighlightBox = true;
1804 mEventData->mUpdateLeftSelectionPosition = true;
1805 mEventData->mUpdateRightSelectionPosition = true;
1807 if( handleStopScrolling || isSmoothHandlePanEnabled )
1809 mEventData->mScrollAfterUpdatePosition = true;
1811 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1812 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1814 mEventData->mLeftSelectionPosition = handlePosition;
1818 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1820 ChangeState( EventData::SELECTING );
1822 mEventData->mUpdateHighlightBox = true;
1823 mEventData->mUpdateRightSelectionPosition = true;
1824 mEventData->mUpdateLeftSelectionPosition = true;
1826 if( handleStopScrolling || isSmoothHandlePanEnabled )
1828 mEventData->mScrollAfterUpdatePosition = true;
1829 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1830 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1832 mEventData->mRightSelectionPosition = handlePosition;
1837 mEventData->mDecoratorUpdated = true;
1838 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1839 else if( HANDLE_SCROLLING == state )
1841 const float xSpeed = event.p2.mFloat;
1842 const float ySpeed = event.p3.mFloat;
1843 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1844 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1846 mModel->mScrollPosition.x += xSpeed;
1847 mModel->mScrollPosition.y += ySpeed;
1849 ClampHorizontalScroll( layoutSize );
1850 ClampVerticalScroll( layoutSize );
1852 bool endOfScroll = false;
1853 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1855 // Notify the decorator there is no more text to scroll.
1856 // The decorator won't send more scroll events.
1857 mEventData->mDecorator->NotifyEndOfScroll();
1858 // Still need to set the position of the handle.
1862 // Set the position of the handle.
1863 const bool scrollRightDirection = xSpeed > 0.f;
1864 const bool scrollBottomDirection = ySpeed > 0.f;
1865 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1866 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1868 if( Event::GRAB_HANDLE_EVENT == event.type )
1870 ChangeState( EventData::GRAB_HANDLE_PANNING );
1872 // Get the grab handle position in decorator coords.
1873 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1875 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1877 // Position the grag handle close to either the left or right edge.
1878 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1881 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1883 position.x = mEventData->mCursorHookPositionX;
1885 // Position the grag handle close to either the top or bottom edge.
1886 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1889 // Get the new handle position.
1890 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1891 bool matchedCharacter = false;
1892 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1893 mModel->mLogicalModel,
1895 position.x - mModel->mScrollPosition.x,
1896 position.y - mModel->mScrollPosition.y,
1897 CharacterHitTest::SCROLL,
1900 if( mEventData->mPrimaryCursorPosition != handlePosition )
1902 mEventData->mUpdateCursorPosition = true;
1903 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1904 mEventData->mScrollAfterUpdatePosition = true;
1905 mEventData->mPrimaryCursorPosition = handlePosition;
1907 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1909 // Updates the decorator if the soft handle panning is enabled.
1910 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1912 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1914 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1916 // Get the selection handle position in decorator coords.
1917 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1919 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1921 // Position the selection handle close to either the left or right edge.
1922 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1925 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1927 position.x = mEventData->mCursorHookPositionX;
1929 // Position the grag handle close to either the top or bottom edge.
1930 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1933 // Get the new handle position.
1934 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1935 bool matchedCharacter = false;
1936 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1937 mModel->mLogicalModel,
1939 position.x - mModel->mScrollPosition.x,
1940 position.y - mModel->mScrollPosition.y,
1941 CharacterHitTest::SCROLL,
1944 if( leftSelectionHandleEvent )
1946 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1948 if( differentHandles || endOfScroll )
1950 mEventData->mUpdateHighlightBox = true;
1951 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1952 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1953 mEventData->mLeftSelectionPosition = handlePosition;
1958 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1959 if( differentHandles || endOfScroll )
1961 mEventData->mUpdateHighlightBox = true;
1962 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1963 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1964 mEventData->mRightSelectionPosition = handlePosition;
1968 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1970 RepositionSelectionHandles();
1972 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1975 mEventData->mDecoratorUpdated = true;
1976 } // end ( HANDLE_SCROLLING == state )
1979 void Controller::Impl::OnSelectEvent( const Event& event )
1981 if( NULL == mEventData )
1983 // Nothing to do if there is no text.
1987 if( mEventData->mSelectionEnabled )
1989 // Convert from control's coords to text's coords.
1990 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1991 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1993 // Calculates the logical position from the x,y coords.
1994 RepositionSelectionHandles( xPosition,
1996 Controller::NoTextTap::HIGHLIGHT );
2000 void Controller::Impl::OnSelectAllEvent()
2002 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2004 if( NULL == mEventData )
2006 // Nothing to do if there is no text.
2010 if( mEventData->mSelectionEnabled )
2012 // Calculates the logical position from the start.
2013 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
2014 0.f - mModel->mScrollPosition.y,
2015 Controller::NoTextTap::HIGHLIGHT );
2017 mEventData->mLeftSelectionPosition = 0u;
2018 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
2022 void Controller::Impl::OnSelectNoneEvent()
2024 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2026 if( NULL == mEventData )
2028 // Nothing to do if there is no text.
2032 if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING)
2034 mEventData->mPrimaryCursorPosition = 0u;
2035 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
2036 ChangeState( EventData::INACTIVE );
2037 mEventData->mUpdateCursorPosition = true;
2038 mEventData->mUpdateInputStyle = true;
2039 mEventData->mScrollAfterUpdatePosition = true;
2043 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2045 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2047 // Nothing to select if handles are in the same place.
2048 selectedText.clear();
2052 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2054 //Get start and end position of selection
2055 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2056 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2058 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2059 const Length numberOfCharacters = utf32Characters.Count();
2061 // Validate the start and end selection points
2062 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2064 //Get text as a UTF8 string
2065 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2067 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2069 // Keep a copy of the current input style.
2070 InputStyle currentInputStyle;
2071 currentInputStyle.Copy( mEventData->mInputStyle );
2073 // Set as input style the style of the first deleted character.
2074 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2076 // Compare if the input style has changed.
2077 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2079 if( hasInputStyleChanged )
2081 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2082 // Queue the input style changed signal.
2083 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2086 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2088 // Mark the paragraphs to be updated.
2089 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2091 mTextUpdateInfo.mCharacterIndex = 0;
2092 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2093 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2094 mTextUpdateInfo.mClearAll = true;
2098 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2099 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2102 // Delete text between handles
2103 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2104 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2105 utf32Characters.Erase( first, last );
2107 // Will show the cursor at the first character of the selection.
2108 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2112 // Will show the cursor at the last character of the selection.
2113 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2116 mEventData->mDecoratorUpdated = true;
2120 void Controller::Impl::ShowClipboard()
2124 mClipboard.ShowClipboard();
2128 void Controller::Impl::HideClipboard()
2130 if( mClipboard && mClipboardHideEnabled )
2132 mClipboard.HideClipboard();
2136 void Controller::Impl::SetClipboardHideEnable(bool enable)
2138 mClipboardHideEnabled = enable;
2141 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2143 //Send string to clipboard
2144 return ( mClipboard && mClipboard.SetItem( source ) );
2147 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2149 std::string selectedText;
2150 RetrieveSelection( selectedText, deleteAfterSending );
2151 CopyStringToClipboard( selectedText );
2152 ChangeState( EventData::EDITING );
2155 void Controller::Impl::RequestGetTextFromClipboard()
2159 mClipboard.RequestItem();
2163 void Controller::Impl::RepositionSelectionHandles()
2165 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2166 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2168 if( selectionStart == selectionEnd )
2170 // Nothing to select if handles are in the same place.
2171 // So, deactive Highlight box.
2172 mEventData->mDecorator->SetHighlightActive( false );
2176 mEventData->mDecorator->ClearHighlights();
2178 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2179 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2180 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2181 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2182 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2183 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2184 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2186 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2187 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2188 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2190 // Swap the indices if the start is greater than the end.
2191 const bool indicesSwapped = selectionStart > selectionEnd;
2193 // Tell the decorator to flip the selection handles if needed.
2194 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2196 if( indicesSwapped )
2198 std::swap( selectionStart, selectionEnd );
2201 // Get the indices to the first and last selected glyphs.
2202 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2203 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2204 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2205 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2207 // Get the lines where the glyphs are laid-out.
2208 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2210 LineIndex lineIndex = 0u;
2211 Length numberOfLines = 0u;
2212 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2213 1u + glyphEnd - glyphStart,
2216 const LineIndex firstLineIndex = lineIndex;
2218 // Create the structure to store some selection box info.
2219 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2220 selectionBoxLinesInfo.Resize( numberOfLines );
2222 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2223 selectionBoxInfo->minX = MAX_FLOAT;
2224 selectionBoxInfo->maxX = MIN_FLOAT;
2226 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2227 float minHighlightX = std::numeric_limits<float>::max();
2228 float maxHighlightX = std::numeric_limits<float>::min();
2230 Vector2 highLightPosition; // The highlight position in decorator's coords.
2232 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2234 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2235 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2238 // Transform to decorator's (control) coords.
2239 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2241 lineRun += firstLineIndex;
2243 // The line height is the addition of the line ascender and the line descender.
2244 // However, the line descender has a negative value, hence the subtraction.
2245 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2247 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2249 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2250 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2251 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2253 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2254 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2255 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2257 // The number of quads of the selection box.
2258 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2259 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2261 // Count the actual number of quads.
2262 unsigned int actualNumberOfQuads = 0u;
2265 // Traverse the glyphs.
2266 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2268 const GlyphInfo& glyph = *( glyphsBuffer + index );
2269 const Vector2& position = *( positionsBuffer + index );
2271 if( splitStartGlyph )
2273 // 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.
2275 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2276 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2277 // Get the direction of the character.
2278 CharacterDirection isCurrentRightToLeft = false;
2279 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2281 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2284 // The end point could be in the middle of the ligature.
2285 // Calculate the number of characters selected.
2286 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2288 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2289 quad.y = selectionBoxInfo->lineOffset;
2290 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2291 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2293 // Store the min and max 'x' for each line.
2294 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2295 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2297 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2298 ++actualNumberOfQuads;
2300 splitStartGlyph = false;
2304 if( splitEndGlyph && ( index == glyphEnd ) )
2306 // 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.
2308 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2309 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2310 // Get the direction of the character.
2311 CharacterDirection isCurrentRightToLeft = false;
2312 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2314 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2317 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2319 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2320 quad.y = selectionBoxInfo->lineOffset;
2321 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2322 quad.w = quad.y + selectionBoxInfo->lineHeight;
2324 // Store the min and max 'x' for each line.
2325 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2326 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2328 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2330 ++actualNumberOfQuads;
2332 splitEndGlyph = false;
2336 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2337 quad.y = selectionBoxInfo->lineOffset;
2338 quad.z = quad.x + glyph.advance;
2339 quad.w = quad.y + selectionBoxInfo->lineHeight;
2341 // Store the min and max 'x' for each line.
2342 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2343 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2345 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2347 ++actualNumberOfQuads;
2349 // Whether to retrieve the next line.
2350 if( index == lastGlyphOfLine )
2353 if( lineIndex < firstLineIndex + numberOfLines )
2355 // Retrieve the next line.
2358 // Get the last glyph of the new line.
2359 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2361 // Keep the offset and height of the current selection box.
2362 const float currentLineOffset = selectionBoxInfo->lineOffset;
2363 const float currentLineHeight = selectionBoxInfo->lineHeight;
2365 // Get the selection box info for the next line.
2368 selectionBoxInfo->minX = MAX_FLOAT;
2369 selectionBoxInfo->maxX = MIN_FLOAT;
2371 // Update the line's vertical offset.
2372 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2374 // The line height is the addition of the line ascender and the line descender.
2375 // However, the line descender has a negative value, hence the subtraction.
2376 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2381 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2382 // The final width is calculated after 'boxifying' the selection.
2383 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2384 endIt = selectionBoxLinesInfo.End();
2388 const SelectionBoxInfo& info = *it;
2390 // Update the size of the highlighted text.
2391 highLightSize.height += info.lineHeight;
2392 minHighlightX = std::min( minHighlightX, info.minX );
2393 maxHighlightX = std::max( maxHighlightX, info.maxX );
2396 // Add extra geometry to 'boxify' the selection.
2398 if( 1u < numberOfLines )
2400 // Boxify the first line.
2401 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2402 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2404 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2405 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2410 quad.y = firstSelectionBoxLineInfo.lineOffset;
2411 quad.z = firstSelectionBoxLineInfo.minX;
2412 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2414 // Boxify at the beginning of the line.
2415 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2417 ++actualNumberOfQuads;
2419 // Update the size of the highlighted text.
2420 minHighlightX = 0.f;
2425 quad.x = firstSelectionBoxLineInfo.maxX;
2426 quad.y = firstSelectionBoxLineInfo.lineOffset;
2427 quad.z = mModel->mVisualModel->mControlSize.width;
2428 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2430 // Boxify at the end of the line.
2431 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2433 ++actualNumberOfQuads;
2435 // Update the size of the highlighted text.
2436 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2439 // Boxify the central lines.
2440 if( 2u < numberOfLines )
2442 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2443 endIt = selectionBoxLinesInfo.End() - 1u;
2447 const SelectionBoxInfo& info = *it;
2450 quad.y = info.lineOffset;
2452 quad.w = info.lineOffset + info.lineHeight;
2454 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2456 ++actualNumberOfQuads;
2459 quad.y = info.lineOffset;
2460 quad.z = mModel->mVisualModel->mControlSize.width;
2461 quad.w = info.lineOffset + info.lineHeight;
2463 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2465 ++actualNumberOfQuads;
2468 // Update the size of the highlighted text.
2469 minHighlightX = 0.f;
2470 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2473 // Boxify the last line.
2474 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2475 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2477 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2478 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2483 quad.y = lastSelectionBoxLineInfo.lineOffset;
2484 quad.z = lastSelectionBoxLineInfo.minX;
2485 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2487 // Boxify at the beginning of the line.
2488 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2490 ++actualNumberOfQuads;
2492 // Update the size of the highlighted text.
2493 minHighlightX = 0.f;
2498 quad.x = lastSelectionBoxLineInfo.maxX;
2499 quad.y = lastSelectionBoxLineInfo.lineOffset;
2500 quad.z = mModel->mVisualModel->mControlSize.width;
2501 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2503 // Boxify at the end of the line.
2504 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2506 ++actualNumberOfQuads;
2508 // Update the size of the highlighted text.
2509 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2513 // Set the actual number of quads.
2514 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2516 // Sets the highlight's size and position. In decorator's coords.
2517 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2518 highLightSize.width = maxHighlightX - minHighlightX;
2520 highLightPosition.x = minHighlightX;
2521 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2522 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2524 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2526 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2528 CursorInfo primaryCursorInfo;
2529 GetCursorPosition( mEventData->mLeftSelectionPosition,
2530 primaryCursorInfo );
2532 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2534 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2536 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2537 primaryCursorInfo.lineHeight );
2539 CursorInfo secondaryCursorInfo;
2540 GetCursorPosition( mEventData->mRightSelectionPosition,
2541 secondaryCursorInfo );
2543 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2545 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2546 secondaryPosition.x,
2547 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2548 secondaryCursorInfo.lineHeight );
2551 // Set the flag to update the decorator.
2552 mEventData->mDecoratorUpdated = true;
2555 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2557 if( NULL == mEventData )
2559 // Nothing to do if there is no text input.
2563 if( IsShowingPlaceholderText() )
2565 // Nothing to do if there is the place-holder text.
2569 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2570 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2571 if( ( 0 == numberOfGlyphs ) ||
2572 ( 0 == numberOfLines ) )
2574 // Nothing to do if there is no text.
2578 // Find which word was selected
2579 CharacterIndex selectionStart( 0 );
2580 CharacterIndex selectionEnd( 0 );
2581 CharacterIndex noTextHitIndex( 0 );
2582 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2583 mModel->mLogicalModel,
2590 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2592 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2594 ChangeState( EventData::SELECTING );
2596 mEventData->mLeftSelectionPosition = selectionStart;
2597 mEventData->mRightSelectionPosition = selectionEnd;
2599 mEventData->mUpdateLeftSelectionPosition = true;
2600 mEventData->mUpdateRightSelectionPosition = true;
2601 mEventData->mUpdateHighlightBox = true;
2603 // It may happen an InputMethodContext commit event arrives before the selection event
2604 // if the InputMethodContext is in pre-edit state. The commit event will set the
2605 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2606 // to false, the highlight box won't be updated.
2607 mEventData->mUpdateCursorPosition = false;
2609 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2611 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2612 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2614 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2616 // Nothing to select. i.e. a white space, out of bounds
2617 ChangeState( EventData::EDITING_WITH_POPUP );
2619 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2621 mEventData->mUpdateCursorPosition = true;
2622 mEventData->mUpdateGrabHandlePosition = true;
2623 mEventData->mScrollAfterUpdatePosition = true;
2624 mEventData->mUpdateInputStyle = true;
2626 else if( Controller::NoTextTap::NO_ACTION == action )
2628 // Nothing to select. i.e. a white space, out of bounds
2629 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2631 mEventData->mUpdateCursorPosition = true;
2632 mEventData->mUpdateGrabHandlePosition = true;
2633 mEventData->mScrollAfterUpdatePosition = true;
2634 mEventData->mUpdateInputStyle = true;
2638 void Controller::Impl::SetPopupButtons()
2641 * Sets the Popup buttons to be shown depending on State.
2643 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2645 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2648 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2650 if( EventData::SELECTING == mEventData->mState )
2652 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2654 if( !IsClipboardEmpty() )
2656 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2657 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2660 if( !mEventData->mAllTextSelected )
2662 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2665 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2667 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2669 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2672 if( !IsClipboardEmpty() )
2674 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2675 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2678 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2680 if ( !IsClipboardEmpty() )
2682 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2683 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2687 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2690 void Controller::Impl::ChangeState( EventData::State newState )
2692 if( NULL == mEventData )
2694 // Nothing to do if there is no text input.
2698 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2700 if( mEventData->mState != newState )
2702 mEventData->mPreviousState = mEventData->mState;
2703 mEventData->mState = newState;
2705 switch( mEventData->mState )
2707 case EventData::INACTIVE:
2709 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2710 mEventData->mDecorator->StopCursorBlink();
2711 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2712 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2713 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2714 mEventData->mDecorator->SetHighlightActive( false );
2715 mEventData->mDecorator->SetPopupActive( false );
2716 mEventData->mDecoratorUpdated = true;
2719 case EventData::INTERRUPTED:
2721 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2722 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2723 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2724 mEventData->mDecorator->SetHighlightActive( false );
2725 mEventData->mDecorator->SetPopupActive( false );
2726 mEventData->mDecoratorUpdated = true;
2729 case EventData::SELECTING:
2731 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2732 mEventData->mDecorator->StopCursorBlink();
2733 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2734 if ( mEventData->mGrabHandleEnabled )
2736 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2737 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2739 mEventData->mDecorator->SetHighlightActive( true );
2740 if( mEventData->mGrabHandlePopupEnabled )
2743 mEventData->mDecorator->SetPopupActive( true );
2745 mEventData->mDecoratorUpdated = true;
2748 case EventData::EDITING:
2750 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2751 if( mEventData->mCursorBlinkEnabled )
2753 mEventData->mDecorator->StartCursorBlink();
2755 // Grab handle is not shown until a tap is received whilst EDITING
2756 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2757 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2758 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2759 mEventData->mDecorator->SetHighlightActive( false );
2760 if( mEventData->mGrabHandlePopupEnabled )
2762 mEventData->mDecorator->SetPopupActive( false );
2764 mEventData->mDecoratorUpdated = true;
2767 case EventData::EDITING_WITH_POPUP:
2769 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2771 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2772 if( mEventData->mCursorBlinkEnabled )
2774 mEventData->mDecorator->StartCursorBlink();
2776 if( mEventData->mSelectionEnabled )
2778 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2779 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2780 mEventData->mDecorator->SetHighlightActive( false );
2782 else if ( mEventData->mGrabHandleEnabled )
2784 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2786 if( mEventData->mGrabHandlePopupEnabled )
2789 mEventData->mDecorator->SetPopupActive( true );
2791 mEventData->mDecoratorUpdated = true;
2794 case EventData::EDITING_WITH_GRAB_HANDLE:
2796 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2798 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2799 if( mEventData->mCursorBlinkEnabled )
2801 mEventData->mDecorator->StartCursorBlink();
2803 // Grab handle is not shown until a tap is received whilst EDITING
2804 if ( mEventData->mGrabHandleEnabled )
2806 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2808 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2809 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2810 mEventData->mDecorator->SetHighlightActive( false );
2811 if( mEventData->mGrabHandlePopupEnabled )
2813 mEventData->mDecorator->SetPopupActive( false );
2815 mEventData->mDecoratorUpdated = true;
2818 case EventData::SELECTION_HANDLE_PANNING:
2820 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2821 mEventData->mDecorator->StopCursorBlink();
2822 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2823 if ( mEventData->mGrabHandleEnabled )
2825 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2826 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2828 mEventData->mDecorator->SetHighlightActive( true );
2829 if( mEventData->mGrabHandlePopupEnabled )
2831 mEventData->mDecorator->SetPopupActive( false );
2833 mEventData->mDecoratorUpdated = true;
2836 case EventData::GRAB_HANDLE_PANNING:
2838 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2840 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2841 if( mEventData->mCursorBlinkEnabled )
2843 mEventData->mDecorator->StartCursorBlink();
2845 if ( mEventData->mGrabHandleEnabled )
2847 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2849 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2850 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2851 mEventData->mDecorator->SetHighlightActive( false );
2852 if( mEventData->mGrabHandlePopupEnabled )
2854 mEventData->mDecorator->SetPopupActive( false );
2856 mEventData->mDecoratorUpdated = true;
2859 case EventData::EDITING_WITH_PASTE_POPUP:
2861 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2863 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2864 if( mEventData->mCursorBlinkEnabled )
2866 mEventData->mDecorator->StartCursorBlink();
2869 if ( mEventData->mGrabHandleEnabled )
2871 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2873 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2874 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2875 mEventData->mDecorator->SetHighlightActive( false );
2877 if( mEventData->mGrabHandlePopupEnabled )
2880 mEventData->mDecorator->SetPopupActive( true );
2882 mEventData->mDecoratorUpdated = true;
2885 case EventData::TEXT_PANNING:
2887 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2888 mEventData->mDecorator->StopCursorBlink();
2889 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2890 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2891 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2893 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2894 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2895 mEventData->mDecorator->SetHighlightActive( true );
2898 if( mEventData->mGrabHandlePopupEnabled )
2900 mEventData->mDecorator->SetPopupActive( false );
2903 mEventData->mDecoratorUpdated = true;
2910 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2911 CursorInfo& cursorInfo )
2913 if( !IsShowingRealText() )
2915 // Do not want to use the place-holder text to set the cursor position.
2917 // Use the line's height of the font's family set to set the cursor's size.
2918 // If there is no font's family set, use the default font.
2919 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2921 cursorInfo.lineOffset = 0.f;
2922 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2923 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2926 if( mModel->mMatchSystemLanguageDirection )
2928 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2931 switch( mModel->mHorizontalAlignment )
2933 case Text::HorizontalAlignment::BEGIN :
2937 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2941 cursorInfo.primaryPosition.x = 0.f;
2945 case Text::HorizontalAlignment::CENTER:
2947 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2950 case Text::HorizontalAlignment::END:
2954 cursorInfo.primaryPosition.x = 0.f;
2958 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2964 // Nothing else to do.
2968 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2969 GetCursorPositionParameters parameters;
2970 parameters.visualModel = mModel->mVisualModel;
2971 parameters.logicalModel = mModel->mLogicalModel;
2972 parameters.metrics = mMetrics;
2973 parameters.logical = logical;
2974 parameters.isMultiline = isMultiLine;
2976 Text::GetCursorPosition( parameters,
2979 // Adds Outline offset.
2980 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2981 cursorInfo.primaryPosition.x += outlineWidth;
2982 cursorInfo.primaryPosition.y += outlineWidth;
2983 cursorInfo.secondaryPosition.x += outlineWidth;
2984 cursorInfo.secondaryPosition.y += outlineWidth;
2988 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2990 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2991 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2993 if( 0.f > cursorInfo.primaryPosition.x )
2995 cursorInfo.primaryPosition.x = 0.f;
2998 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2999 if( cursorInfo.primaryPosition.x > edgeWidth )
3001 cursorInfo.primaryPosition.x = edgeWidth;
3006 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
3008 if( NULL == mEventData )
3010 // Nothing to do if there is no text input.
3014 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
3016 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
3017 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
3019 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
3020 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3022 if( numberOfCharacters > 1u )
3024 const Script script = mModel->mLogicalModel->GetScript( index );
3025 if( HasLigatureMustBreak( script ) )
3027 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3028 numberOfCharacters = 1u;
3033 while( 0u == numberOfCharacters )
3036 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3040 if( index < mEventData->mPrimaryCursorPosition )
3042 cursorIndex -= numberOfCharacters;
3046 cursorIndex += numberOfCharacters;
3049 // Will update the cursor hook position.
3050 mEventData->mUpdateCursorHookPosition = true;
3055 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3057 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3058 if( NULL == mEventData )
3060 // Nothing to do if there is no text input.
3061 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3065 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3067 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3069 // Sets the cursor position.
3070 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3073 cursorInfo.primaryCursorHeight,
3074 cursorInfo.lineHeight );
3075 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3077 if( mEventData->mUpdateGrabHandlePosition )
3079 // Sets the grab handle position.
3080 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3082 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3083 cursorInfo.lineHeight );
3086 if( cursorInfo.isSecondaryCursor )
3088 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3089 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3090 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3091 cursorInfo.secondaryCursorHeight,
3092 cursorInfo.lineHeight );
3093 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3096 // Set which cursors are active according the state.
3097 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3099 if( cursorInfo.isSecondaryCursor )
3101 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3105 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3110 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3113 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3116 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3117 const CursorInfo& cursorInfo )
3119 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3120 ( RIGHT_SELECTION_HANDLE != handleType ) )
3125 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3127 // Sets the handle's position.
3128 mEventData->mDecorator->SetPosition( handleType,
3130 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3131 cursorInfo.lineHeight );
3133 // If selection handle at start of the text and other at end of the text then all text is selected.
3134 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3135 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3136 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3139 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3141 // Clamp between -space & -alignment offset.
3143 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3145 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3146 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3147 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3149 mEventData->mDecoratorUpdated = true;
3153 mModel->mScrollPosition.x = 0.f;
3157 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3159 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3161 // Nothing to do if the text is single line.
3165 // Clamp between -space & 0.
3166 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3168 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3169 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3170 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3172 mEventData->mDecoratorUpdated = true;
3176 mModel->mScrollPosition.y = 0.f;
3180 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3182 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3184 // position is in actor's coords.
3185 const float positionEndX = position.x + cursorWidth;
3186 const float positionEndY = position.y + lineHeight;
3188 // Transform the position to decorator coords.
3189 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3190 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3192 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3193 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3195 if( decoratorPositionBeginX < 0.f )
3197 mModel->mScrollPosition.x = -position.x;
3199 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3201 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3204 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3206 if( decoratorPositionBeginY < 0.f )
3208 mModel->mScrollPosition.y = -position.y;
3210 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3212 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3217 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3219 // Get the current cursor position in decorator coords.
3220 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3222 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3226 // Calculate the offset to match the cursor position before the character was deleted.
3227 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3229 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3230 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3232 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3233 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3237 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3238 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3240 // Makes the new cursor position visible if needed.
3241 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3244 void Controller::Impl::RequestRelayout()
3246 if( NULL != mControlInterface )
3248 mControlInterface->RequestTextRelayout();
3252 Actor Controller::Impl::CreateBackgroundActor()
3254 // NOTE: Currently we only support background color for one line left-to-right text,
3255 // so the following calculation is based on one line left-to-right text only!
3259 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3260 if( numberOfGlyphs > 0u )
3262 Vector<GlyphInfo> glyphs;
3263 glyphs.Resize( numberOfGlyphs );
3265 Vector<Vector2> positions;
3266 positions.Resize( numberOfGlyphs );
3268 // Get the line where the glyphs are laid-out.
3269 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3270 float alignmentOffset = lineRun->alignmentOffset;
3271 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3277 glyphs.Resize( numberOfGlyphs );
3278 positions.Resize( numberOfGlyphs );
3280 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3281 const Vector2* const positionsBuffer = positions.Begin();
3283 BackgroundMesh mesh;
3284 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3285 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3287 const Vector2 textSize = mView.GetLayoutSize();
3289 const float offsetX = textSize.width * 0.5f;
3290 const float offsetY = textSize.height * 0.5f;
3292 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3293 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3294 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3297 uint32_t numberOfQuads = 0u;
3299 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3301 const GlyphInfo& glyph = *( glyphsBuffer + i );
3303 // Get the background color of the character.
3304 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3305 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3306 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3308 // Only create quads for glyphs with a background color
3309 if ( backgroundColor != Color::TRANSPARENT )
3311 const Vector2 position = *( positionsBuffer + i );
3313 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3315 quad.x = position.x;
3317 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3318 quad.w = textSize.height;
3320 else if ( i == 0u ) // The first glyph in the whole text
3322 quad.x = position.x;
3324 quad.z = quad.x - glyph.xBearing + glyph.advance;
3325 quad.w = textSize.height;
3327 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3329 quad.x = position.x - glyph.xBearing;
3331 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3332 quad.w = textSize.height;
3334 else // The glyph in the middle of the text
3336 quad.x = position.x - glyph.xBearing;
3338 quad.z = quad.x + glyph.advance;
3339 quad.w = textSize.height;
3342 BackgroundVertex vertex;
3345 vertex.mPosition.x = quad.x - offsetX;
3346 vertex.mPosition.y = quad.y - offsetY;
3347 vertex.mColor = backgroundColor;
3348 mesh.mVertices.PushBack( vertex );
3351 vertex.mPosition.x = quad.z - offsetX;
3352 vertex.mPosition.y = quad.y - offsetY;
3353 vertex.mColor = backgroundColor;
3354 mesh.mVertices.PushBack( vertex );
3357 vertex.mPosition.x = quad.x - offsetX;
3358 vertex.mPosition.y = quad.w - offsetY;
3359 vertex.mColor = backgroundColor;
3360 mesh.mVertices.PushBack( vertex );
3363 vertex.mPosition.x = quad.z - offsetX;
3364 vertex.mPosition.y = quad.w - offsetY;
3365 vertex.mColor = backgroundColor;
3366 mesh.mVertices.PushBack( vertex );
3368 // Six indices in counter clockwise winding
3369 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3370 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3371 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3372 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3373 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3374 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3380 // Only create the background actor if there are glyphs with background color
3381 if ( mesh.mVertices.Count() > 0u )
3383 Property::Map quadVertexFormat;
3384 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3385 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3387 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
3388 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3390 Geometry quadGeometry = Geometry::New();
3391 quadGeometry.AddVertexBuffer( quadVertices );
3392 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3394 if( !mShaderBackground )
3396 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3399 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3400 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3401 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3403 actor = Actor::New();
3404 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
3405 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
3406 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
3407 actor.SetProperty( Actor::Property::SIZE, textSize );
3408 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
3409 actor.AddRenderer( renderer );
3418 } // namespace Toolkit