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::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
2045 if( nullptr == mEventData )
2047 // Nothing to do if there is no text.
2051 if( mEventData->mSelectionEnabled && (pStart || pEnd))
2053 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
2057 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
2061 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
2064 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
2066 ChangeState( EventData::EDITING );
2067 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
2068 mEventData->mUpdateCursorPosition = true;
2072 ChangeState( EventData::SELECTING );
2073 mEventData->mUpdateHighlightBox = true;
2078 Uint32Pair Controller::Impl::GetTextSelectionRange() const
2084 range.first = mEventData->mLeftSelectionPosition;
2085 range.second = mEventData->mRightSelectionPosition;
2091 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2093 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2095 // Nothing to select if handles are in the same place.
2096 selectedText.clear();
2100 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2102 //Get start and end position of selection
2103 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2104 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2106 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2107 const Length numberOfCharacters = utf32Characters.Count();
2109 // Validate the start and end selection points
2110 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2112 //Get text as a UTF8 string
2113 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2115 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2117 // Keep a copy of the current input style.
2118 InputStyle currentInputStyle;
2119 currentInputStyle.Copy( mEventData->mInputStyle );
2121 // Set as input style the style of the first deleted character.
2122 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2124 // Compare if the input style has changed.
2125 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2127 if( hasInputStyleChanged )
2129 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2130 // Queue the input style changed signal.
2131 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2134 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2136 // Mark the paragraphs to be updated.
2137 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2139 mTextUpdateInfo.mCharacterIndex = 0;
2140 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2141 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2142 mTextUpdateInfo.mClearAll = true;
2146 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2147 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2150 // Delete text between handles
2151 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2152 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2153 utf32Characters.Erase( first, last );
2155 // Will show the cursor at the first character of the selection.
2156 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2160 // Will show the cursor at the last character of the selection.
2161 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2164 mEventData->mDecoratorUpdated = true;
2168 void Controller::Impl::ShowClipboard()
2172 mClipboard.ShowClipboard();
2176 void Controller::Impl::HideClipboard()
2178 if( mClipboard && mClipboardHideEnabled )
2180 mClipboard.HideClipboard();
2184 void Controller::Impl::SetClipboardHideEnable(bool enable)
2186 mClipboardHideEnabled = enable;
2189 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2191 //Send string to clipboard
2192 return ( mClipboard && mClipboard.SetItem( source ) );
2195 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2197 std::string selectedText;
2198 RetrieveSelection( selectedText, deleteAfterSending );
2199 CopyStringToClipboard( selectedText );
2200 ChangeState( EventData::EDITING );
2203 void Controller::Impl::RequestGetTextFromClipboard()
2207 mClipboard.RequestItem();
2211 void Controller::Impl::RepositionSelectionHandles()
2213 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2214 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2216 if( selectionStart == selectionEnd )
2218 // Nothing to select if handles are in the same place.
2219 // So, deactive Highlight box.
2220 mEventData->mDecorator->SetHighlightActive( false );
2224 mEventData->mDecorator->ClearHighlights();
2226 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2227 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2228 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2229 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2230 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2231 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2232 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2234 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2235 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2236 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2238 // Swap the indices if the start is greater than the end.
2239 const bool indicesSwapped = selectionStart > selectionEnd;
2241 // Tell the decorator to flip the selection handles if needed.
2242 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2244 if( indicesSwapped )
2246 std::swap( selectionStart, selectionEnd );
2249 // Get the indices to the first and last selected glyphs.
2250 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2251 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2252 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2253 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2255 // Get the lines where the glyphs are laid-out.
2256 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2258 LineIndex lineIndex = 0u;
2259 Length numberOfLines = 0u;
2260 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2261 1u + glyphEnd - glyphStart,
2264 const LineIndex firstLineIndex = lineIndex;
2266 // Create the structure to store some selection box info.
2267 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2268 selectionBoxLinesInfo.Resize( numberOfLines );
2270 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2271 selectionBoxInfo->minX = MAX_FLOAT;
2272 selectionBoxInfo->maxX = MIN_FLOAT;
2274 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2275 float minHighlightX = std::numeric_limits<float>::max();
2276 float maxHighlightX = std::numeric_limits<float>::min();
2278 Vector2 highLightPosition; // The highlight position in decorator's coords.
2280 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2282 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2283 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2286 // Transform to decorator's (control) coords.
2287 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2289 lineRun += firstLineIndex;
2291 // The line height is the addition of the line ascender and the line descender.
2292 // However, the line descender has a negative value, hence the subtraction.
2293 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2295 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2297 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2298 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2299 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2301 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2302 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2303 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2305 // The number of quads of the selection box.
2306 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2307 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2309 // Count the actual number of quads.
2310 unsigned int actualNumberOfQuads = 0u;
2313 // Traverse the glyphs.
2314 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2316 const GlyphInfo& glyph = *( glyphsBuffer + index );
2317 const Vector2& position = *( positionsBuffer + index );
2319 if( splitStartGlyph )
2321 // 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.
2323 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2324 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2325 // Get the direction of the character.
2326 CharacterDirection isCurrentRightToLeft = false;
2327 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2329 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2332 // The end point could be in the middle of the ligature.
2333 // Calculate the number of characters selected.
2334 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2336 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2337 quad.y = selectionBoxInfo->lineOffset;
2338 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2339 quad.w = selectionBoxInfo->lineOffset + 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, quad );
2346 ++actualNumberOfQuads;
2348 splitStartGlyph = false;
2352 if( splitEndGlyph && ( index == glyphEnd ) )
2354 // 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.
2356 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2357 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2358 // Get the direction of the character.
2359 CharacterDirection isCurrentRightToLeft = false;
2360 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2362 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2365 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2367 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2368 quad.y = selectionBoxInfo->lineOffset;
2369 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2370 quad.w = quad.y + selectionBoxInfo->lineHeight;
2372 // Store the min and max 'x' for each line.
2373 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2374 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2376 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2378 ++actualNumberOfQuads;
2380 splitEndGlyph = false;
2384 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2385 quad.y = selectionBoxInfo->lineOffset;
2386 quad.z = quad.x + glyph.advance;
2387 quad.w = quad.y + selectionBoxInfo->lineHeight;
2389 // Store the min and max 'x' for each line.
2390 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2391 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2393 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2395 ++actualNumberOfQuads;
2397 // Whether to retrieve the next line.
2398 if( index == lastGlyphOfLine )
2401 if( lineIndex < firstLineIndex + numberOfLines )
2403 // Retrieve the next line.
2406 // Get the last glyph of the new line.
2407 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2409 // Keep the offset and height of the current selection box.
2410 const float currentLineOffset = selectionBoxInfo->lineOffset;
2411 const float currentLineHeight = selectionBoxInfo->lineHeight;
2413 // Get the selection box info for the next line.
2416 selectionBoxInfo->minX = MAX_FLOAT;
2417 selectionBoxInfo->maxX = MIN_FLOAT;
2419 // Update the line's vertical offset.
2420 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2422 // The line height is the addition of the line ascender and the line descender.
2423 // However, the line descender has a negative value, hence the subtraction.
2424 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2429 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2430 // The final width is calculated after 'boxifying' the selection.
2431 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2432 endIt = selectionBoxLinesInfo.End();
2436 const SelectionBoxInfo& info = *it;
2438 // Update the size of the highlighted text.
2439 highLightSize.height += info.lineHeight;
2440 minHighlightX = std::min( minHighlightX, info.minX );
2441 maxHighlightX = std::max( maxHighlightX, info.maxX );
2444 // Add extra geometry to 'boxify' the selection.
2446 if( 1u < numberOfLines )
2448 // Boxify the first line.
2449 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2450 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2452 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2453 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2458 quad.y = firstSelectionBoxLineInfo.lineOffset;
2459 quad.z = firstSelectionBoxLineInfo.minX;
2460 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2462 // Boxify at the beginning of the line.
2463 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2465 ++actualNumberOfQuads;
2467 // Update the size of the highlighted text.
2468 minHighlightX = 0.f;
2473 quad.x = firstSelectionBoxLineInfo.maxX;
2474 quad.y = firstSelectionBoxLineInfo.lineOffset;
2475 quad.z = mModel->mVisualModel->mControlSize.width;
2476 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2478 // Boxify at the end of the line.
2479 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2481 ++actualNumberOfQuads;
2483 // Update the size of the highlighted text.
2484 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2487 // Boxify the central lines.
2488 if( 2u < numberOfLines )
2490 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2491 endIt = selectionBoxLinesInfo.End() - 1u;
2495 const SelectionBoxInfo& info = *it;
2498 quad.y = info.lineOffset;
2500 quad.w = info.lineOffset + info.lineHeight;
2502 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2504 ++actualNumberOfQuads;
2507 quad.y = info.lineOffset;
2508 quad.z = mModel->mVisualModel->mControlSize.width;
2509 quad.w = info.lineOffset + info.lineHeight;
2511 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2513 ++actualNumberOfQuads;
2516 // Update the size of the highlighted text.
2517 minHighlightX = 0.f;
2518 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2521 // Boxify the last line.
2522 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2523 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2525 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2526 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2531 quad.y = lastSelectionBoxLineInfo.lineOffset;
2532 quad.z = lastSelectionBoxLineInfo.minX;
2533 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2535 // Boxify at the beginning of the line.
2536 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2538 ++actualNumberOfQuads;
2540 // Update the size of the highlighted text.
2541 minHighlightX = 0.f;
2546 quad.x = lastSelectionBoxLineInfo.maxX;
2547 quad.y = lastSelectionBoxLineInfo.lineOffset;
2548 quad.z = mModel->mVisualModel->mControlSize.width;
2549 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2551 // Boxify at the end of the line.
2552 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2554 ++actualNumberOfQuads;
2556 // Update the size of the highlighted text.
2557 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2561 // Set the actual number of quads.
2562 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2564 // Sets the highlight's size and position. In decorator's coords.
2565 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2566 highLightSize.width = maxHighlightX - minHighlightX;
2568 highLightPosition.x = minHighlightX;
2569 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2570 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2572 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2574 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2576 CursorInfo primaryCursorInfo;
2577 GetCursorPosition( mEventData->mLeftSelectionPosition,
2578 primaryCursorInfo );
2580 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2582 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2584 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2585 primaryCursorInfo.lineHeight );
2587 CursorInfo secondaryCursorInfo;
2588 GetCursorPosition( mEventData->mRightSelectionPosition,
2589 secondaryCursorInfo );
2591 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2593 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2594 secondaryPosition.x,
2595 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2596 secondaryCursorInfo.lineHeight );
2599 // Set the flag to update the decorator.
2600 mEventData->mDecoratorUpdated = true;
2603 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2605 if( NULL == mEventData )
2607 // Nothing to do if there is no text input.
2611 if( IsShowingPlaceholderText() )
2613 // Nothing to do if there is the place-holder text.
2617 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2618 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2619 if( ( 0 == numberOfGlyphs ) ||
2620 ( 0 == numberOfLines ) )
2622 // Nothing to do if there is no text.
2626 // Find which word was selected
2627 CharacterIndex selectionStart( 0 );
2628 CharacterIndex selectionEnd( 0 );
2629 CharacterIndex noTextHitIndex( 0 );
2630 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2631 mModel->mLogicalModel,
2638 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2640 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2642 ChangeState( EventData::SELECTING );
2644 mEventData->mLeftSelectionPosition = selectionStart;
2645 mEventData->mRightSelectionPosition = selectionEnd;
2647 mEventData->mUpdateLeftSelectionPosition = true;
2648 mEventData->mUpdateRightSelectionPosition = true;
2649 mEventData->mUpdateHighlightBox = true;
2651 // It may happen an InputMethodContext commit event arrives before the selection event
2652 // if the InputMethodContext is in pre-edit state. The commit event will set the
2653 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2654 // to false, the highlight box won't be updated.
2655 mEventData->mUpdateCursorPosition = false;
2657 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2659 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2660 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2662 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2664 // Nothing to select. i.e. a white space, out of bounds
2665 ChangeState( EventData::EDITING_WITH_POPUP );
2667 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2669 mEventData->mUpdateCursorPosition = true;
2670 mEventData->mUpdateGrabHandlePosition = true;
2671 mEventData->mScrollAfterUpdatePosition = true;
2672 mEventData->mUpdateInputStyle = true;
2674 else if( Controller::NoTextTap::NO_ACTION == action )
2676 // Nothing to select. i.e. a white space, out of bounds
2677 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2679 mEventData->mUpdateCursorPosition = true;
2680 mEventData->mUpdateGrabHandlePosition = true;
2681 mEventData->mScrollAfterUpdatePosition = true;
2682 mEventData->mUpdateInputStyle = true;
2686 void Controller::Impl::SetPopupButtons()
2689 * Sets the Popup buttons to be shown depending on State.
2691 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2693 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2696 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2698 if( EventData::SELECTING == mEventData->mState )
2700 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2702 if( !IsClipboardEmpty() )
2704 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2705 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2708 if( !mEventData->mAllTextSelected )
2710 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2713 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2715 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2717 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2720 if( !IsClipboardEmpty() )
2722 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2723 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2726 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2728 if ( !IsClipboardEmpty() )
2730 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2731 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2735 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2738 void Controller::Impl::ChangeState( EventData::State newState )
2740 if( NULL == mEventData )
2742 // Nothing to do if there is no text input.
2746 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2748 if( mEventData->mState != newState )
2750 mEventData->mPreviousState = mEventData->mState;
2751 mEventData->mState = newState;
2753 switch( mEventData->mState )
2755 case EventData::INACTIVE:
2757 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2758 mEventData->mDecorator->StopCursorBlink();
2759 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2760 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2761 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2762 mEventData->mDecorator->SetHighlightActive( false );
2763 mEventData->mDecorator->SetPopupActive( false );
2764 mEventData->mDecoratorUpdated = true;
2767 case EventData::INTERRUPTED:
2769 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2770 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2771 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2772 mEventData->mDecorator->SetHighlightActive( false );
2773 mEventData->mDecorator->SetPopupActive( false );
2774 mEventData->mDecoratorUpdated = true;
2777 case EventData::SELECTING:
2779 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2780 mEventData->mDecorator->StopCursorBlink();
2781 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2782 if ( mEventData->mGrabHandleEnabled )
2784 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2785 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2787 mEventData->mDecorator->SetHighlightActive( true );
2788 if( mEventData->mGrabHandlePopupEnabled )
2791 mEventData->mDecorator->SetPopupActive( true );
2793 mEventData->mDecoratorUpdated = true;
2796 case EventData::EDITING:
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 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2805 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2806 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2807 mEventData->mDecorator->SetHighlightActive( false );
2808 if( mEventData->mGrabHandlePopupEnabled )
2810 mEventData->mDecorator->SetPopupActive( false );
2812 mEventData->mDecoratorUpdated = true;
2815 case EventData::EDITING_WITH_POPUP:
2817 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2819 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2820 if( mEventData->mCursorBlinkEnabled )
2822 mEventData->mDecorator->StartCursorBlink();
2824 if( mEventData->mSelectionEnabled )
2826 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2827 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2828 mEventData->mDecorator->SetHighlightActive( false );
2830 else if ( mEventData->mGrabHandleEnabled )
2832 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2834 if( mEventData->mGrabHandlePopupEnabled )
2837 mEventData->mDecorator->SetPopupActive( true );
2839 mEventData->mDecoratorUpdated = true;
2842 case EventData::EDITING_WITH_GRAB_HANDLE:
2844 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2846 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2847 if( mEventData->mCursorBlinkEnabled )
2849 mEventData->mDecorator->StartCursorBlink();
2851 // Grab handle is not shown until a tap is received whilst EDITING
2852 if ( mEventData->mGrabHandleEnabled )
2854 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2856 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2857 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2858 mEventData->mDecorator->SetHighlightActive( false );
2859 if( mEventData->mGrabHandlePopupEnabled )
2861 mEventData->mDecorator->SetPopupActive( false );
2863 mEventData->mDecoratorUpdated = true;
2866 case EventData::SELECTION_HANDLE_PANNING:
2868 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2869 mEventData->mDecorator->StopCursorBlink();
2870 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2871 if ( mEventData->mGrabHandleEnabled )
2873 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2874 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2876 mEventData->mDecorator->SetHighlightActive( true );
2877 if( mEventData->mGrabHandlePopupEnabled )
2879 mEventData->mDecorator->SetPopupActive( false );
2881 mEventData->mDecoratorUpdated = true;
2884 case EventData::GRAB_HANDLE_PANNING:
2886 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2888 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2889 if( mEventData->mCursorBlinkEnabled )
2891 mEventData->mDecorator->StartCursorBlink();
2893 if ( mEventData->mGrabHandleEnabled )
2895 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2897 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2898 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2899 mEventData->mDecorator->SetHighlightActive( false );
2900 if( mEventData->mGrabHandlePopupEnabled )
2902 mEventData->mDecorator->SetPopupActive( false );
2904 mEventData->mDecoratorUpdated = true;
2907 case EventData::EDITING_WITH_PASTE_POPUP:
2909 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2911 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2912 if( mEventData->mCursorBlinkEnabled )
2914 mEventData->mDecorator->StartCursorBlink();
2917 if ( mEventData->mGrabHandleEnabled )
2919 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2921 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2922 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2923 mEventData->mDecorator->SetHighlightActive( false );
2925 if( mEventData->mGrabHandlePopupEnabled )
2928 mEventData->mDecorator->SetPopupActive( true );
2930 mEventData->mDecoratorUpdated = true;
2933 case EventData::TEXT_PANNING:
2935 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2936 mEventData->mDecorator->StopCursorBlink();
2937 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2938 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2939 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2941 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2942 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2943 mEventData->mDecorator->SetHighlightActive( true );
2946 if( mEventData->mGrabHandlePopupEnabled )
2948 mEventData->mDecorator->SetPopupActive( false );
2951 mEventData->mDecoratorUpdated = true;
2958 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2959 CursorInfo& cursorInfo )
2961 if( !IsShowingRealText() )
2963 // Do not want to use the place-holder text to set the cursor position.
2965 // Use the line's height of the font's family set to set the cursor's size.
2966 // If there is no font's family set, use the default font.
2967 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2969 cursorInfo.lineOffset = 0.f;
2970 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2971 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2974 if( mModel->mMatchSystemLanguageDirection )
2976 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2979 switch( mModel->mHorizontalAlignment )
2981 case Text::HorizontalAlignment::BEGIN :
2985 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2989 cursorInfo.primaryPosition.x = 0.f;
2993 case Text::HorizontalAlignment::CENTER:
2995 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2998 case Text::HorizontalAlignment::END:
3002 cursorInfo.primaryPosition.x = 0.f;
3006 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
3012 // Nothing else to do.
3016 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
3017 GetCursorPositionParameters parameters;
3018 parameters.visualModel = mModel->mVisualModel;
3019 parameters.logicalModel = mModel->mLogicalModel;
3020 parameters.metrics = mMetrics;
3021 parameters.logical = logical;
3022 parameters.isMultiline = isMultiLine;
3024 Text::GetCursorPosition( parameters,
3027 // Adds Outline offset.
3028 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
3029 cursorInfo.primaryPosition.x += outlineWidth;
3030 cursorInfo.primaryPosition.y += outlineWidth;
3031 cursorInfo.secondaryPosition.x += outlineWidth;
3032 cursorInfo.secondaryPosition.y += outlineWidth;
3036 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
3038 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
3039 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
3041 if( 0.f > cursorInfo.primaryPosition.x )
3043 cursorInfo.primaryPosition.x = 0.f;
3046 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
3047 if( cursorInfo.primaryPosition.x > edgeWidth )
3049 cursorInfo.primaryPosition.x = edgeWidth;
3054 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
3056 if( NULL == mEventData )
3058 // Nothing to do if there is no text input.
3062 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
3064 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
3065 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
3067 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
3068 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3070 if( numberOfCharacters > 1u )
3072 const Script script = mModel->mLogicalModel->GetScript( index );
3073 if( HasLigatureMustBreak( script ) )
3075 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3076 numberOfCharacters = 1u;
3081 while( 0u == numberOfCharacters )
3084 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3088 if( index < mEventData->mPrimaryCursorPosition )
3090 cursorIndex -= numberOfCharacters;
3094 cursorIndex += numberOfCharacters;
3097 // Will update the cursor hook position.
3098 mEventData->mUpdateCursorHookPosition = true;
3103 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3105 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3106 if( NULL == mEventData )
3108 // Nothing to do if there is no text input.
3109 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3113 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3115 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3117 // Sets the cursor position.
3118 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3121 cursorInfo.primaryCursorHeight,
3122 cursorInfo.lineHeight );
3123 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3125 if( mEventData->mUpdateGrabHandlePosition )
3127 // Sets the grab handle position.
3128 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3130 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3131 cursorInfo.lineHeight );
3134 if( cursorInfo.isSecondaryCursor )
3136 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3137 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3138 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3139 cursorInfo.secondaryCursorHeight,
3140 cursorInfo.lineHeight );
3141 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3144 // Set which cursors are active according the state.
3145 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3147 if( cursorInfo.isSecondaryCursor )
3149 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3153 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3158 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3161 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3164 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3165 const CursorInfo& cursorInfo )
3167 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3168 ( RIGHT_SELECTION_HANDLE != handleType ) )
3173 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3175 // Sets the handle's position.
3176 mEventData->mDecorator->SetPosition( handleType,
3178 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3179 cursorInfo.lineHeight );
3181 // If selection handle at start of the text and other at end of the text then all text is selected.
3182 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3183 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3184 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3187 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3189 // Clamp between -space & -alignment offset.
3191 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3193 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3194 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3195 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3197 mEventData->mDecoratorUpdated = true;
3201 mModel->mScrollPosition.x = 0.f;
3205 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3207 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3209 // Nothing to do if the text is single line.
3213 // Clamp between -space & 0.
3214 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3216 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3217 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3218 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3220 mEventData->mDecoratorUpdated = true;
3224 mModel->mScrollPosition.y = 0.f;
3228 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3230 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3232 // position is in actor's coords.
3233 const float positionEndX = position.x + cursorWidth;
3234 const float positionEndY = position.y + lineHeight;
3236 // Transform the position to decorator coords.
3237 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3238 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3240 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3241 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3243 if( decoratorPositionBeginX < 0.f )
3245 mModel->mScrollPosition.x = -position.x;
3247 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3249 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3252 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3254 if( decoratorPositionBeginY < 0.f )
3256 mModel->mScrollPosition.y = -position.y;
3258 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3260 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3265 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3267 // Get the current cursor position in decorator coords.
3268 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3270 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3274 // Calculate the offset to match the cursor position before the character was deleted.
3275 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3277 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3278 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3280 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3281 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3285 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3286 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3288 // Makes the new cursor position visible if needed.
3289 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3292 void Controller::Impl::RequestRelayout()
3294 if( NULL != mControlInterface )
3296 mControlInterface->RequestTextRelayout();
3300 Actor Controller::Impl::CreateBackgroundActor()
3302 // NOTE: Currently we only support background color for one line left-to-right text,
3303 // so the following calculation is based on one line left-to-right text only!
3307 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3308 if( numberOfGlyphs > 0u )
3310 Vector<GlyphInfo> glyphs;
3311 glyphs.Resize( numberOfGlyphs );
3313 Vector<Vector2> positions;
3314 positions.Resize( numberOfGlyphs );
3316 // Get the line where the glyphs are laid-out.
3317 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3318 float alignmentOffset = lineRun->alignmentOffset;
3319 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3325 glyphs.Resize( numberOfGlyphs );
3326 positions.Resize( numberOfGlyphs );
3328 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3329 const Vector2* const positionsBuffer = positions.Begin();
3331 BackgroundMesh mesh;
3332 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3333 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3335 const Vector2 textSize = mView.GetLayoutSize();
3337 const float offsetX = textSize.width * 0.5f;
3338 const float offsetY = textSize.height * 0.5f;
3340 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3341 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3342 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3345 uint32_t numberOfQuads = 0u;
3347 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3349 const GlyphInfo& glyph = *( glyphsBuffer + i );
3351 // Get the background color of the character.
3352 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3353 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3354 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3356 // Only create quads for glyphs with a background color
3357 if ( backgroundColor != Color::TRANSPARENT )
3359 const Vector2 position = *( positionsBuffer + i );
3361 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3363 quad.x = position.x;
3365 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3366 quad.w = textSize.height;
3368 else if ( i == 0u ) // The first glyph in the whole text
3370 quad.x = position.x;
3372 quad.z = quad.x - glyph.xBearing + glyph.advance;
3373 quad.w = textSize.height;
3375 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3377 quad.x = position.x - glyph.xBearing;
3379 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3380 quad.w = textSize.height;
3382 else // The glyph in the middle of the text
3384 quad.x = position.x - glyph.xBearing;
3386 quad.z = quad.x + glyph.advance;
3387 quad.w = textSize.height;
3390 BackgroundVertex vertex;
3393 vertex.mPosition.x = quad.x - offsetX;
3394 vertex.mPosition.y = quad.y - offsetY;
3395 vertex.mColor = backgroundColor;
3396 mesh.mVertices.PushBack( vertex );
3399 vertex.mPosition.x = quad.z - offsetX;
3400 vertex.mPosition.y = quad.y - offsetY;
3401 vertex.mColor = backgroundColor;
3402 mesh.mVertices.PushBack( vertex );
3405 vertex.mPosition.x = quad.x - offsetX;
3406 vertex.mPosition.y = quad.w - offsetY;
3407 vertex.mColor = backgroundColor;
3408 mesh.mVertices.PushBack( vertex );
3411 vertex.mPosition.x = quad.z - offsetX;
3412 vertex.mPosition.y = quad.w - offsetY;
3413 vertex.mColor = backgroundColor;
3414 mesh.mVertices.PushBack( vertex );
3416 // Six indices in counter clockwise winding
3417 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3418 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3419 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3420 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3421 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3422 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3428 // Only create the background actor if there are glyphs with background color
3429 if ( mesh.mVertices.Count() > 0u )
3431 Property::Map quadVertexFormat;
3432 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3433 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3435 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
3436 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3438 Geometry quadGeometry = Geometry::New();
3439 quadGeometry.AddVertexBuffer( quadVertices );
3440 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3442 if( !mShaderBackground )
3444 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3447 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3448 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3449 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3451 actor = Actor::New();
3452 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
3453 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
3454 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
3455 actor.SetProperty( Actor::Property::SIZE, textSize );
3456 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
3457 actor.AddRenderer( renderer );
3466 } // namespace Toolkit