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 int state = event.p1.mInt;
1608 case Gesture::Started:
1610 // Will remove the cursor, handles or text's popup, ...
1611 ChangeState( EventData::TEXT_PANNING );
1614 case Gesture::Continuing:
1616 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1617 const Vector2 currentScroll = mModel->mScrollPosition;
1619 if( isHorizontalScrollEnabled )
1621 const float displacementX = event.p2.mFloat;
1622 mModel->mScrollPosition.x += displacementX;
1624 ClampHorizontalScroll( layoutSize );
1627 if( isVerticalScrollEnabled )
1629 const float displacementY = event.p3.mFloat;
1630 mModel->mScrollPosition.y += displacementY;
1632 ClampVerticalScroll( layoutSize );
1635 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1638 case Gesture::Finished:
1639 case Gesture::Cancelled: // FALLTHROUGH
1641 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1642 ChangeState( mEventData->mPreviousState );
1650 void Controller::Impl::OnLongPressEvent( const Event& event )
1652 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1654 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1656 ChangeState( EventData::EDITING_WITH_POPUP );
1657 mEventData->mDecoratorUpdated = true;
1658 mEventData->mUpdateInputStyle = true;
1662 if( mEventData->mSelectionEnabled )
1664 // Convert from control's coords to text's coords.
1665 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1666 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1668 // Calculates the logical position from the x,y coords.
1669 RepositionSelectionHandles( xPosition,
1671 mEventData->mLongPressAction );
1676 void Controller::Impl::OnHandleEvent( const Event& event )
1678 if( NULL == mEventData )
1680 // Nothing to do if there is no text input.
1684 const unsigned int state = event.p1.mUint;
1685 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1686 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1688 if( HANDLE_PRESSED == state )
1690 // Convert from decorator's coords to text's coords.
1691 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1692 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1694 // Need to calculate the handle's new position.
1695 bool matchedCharacter = false;
1696 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1697 mModel->mLogicalModel,
1701 CharacterHitTest::SCROLL,
1704 if( Event::GRAB_HANDLE_EVENT == event.type )
1706 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1708 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1710 // Updates the cursor position if the handle's new position is different than the current one.
1711 mEventData->mUpdateCursorPosition = true;
1712 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1713 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1714 mEventData->mPrimaryCursorPosition = handleNewPosition;
1717 // 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.
1718 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1720 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1722 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1724 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1725 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1727 // Updates the highlight box if the handle's new position is different than the current one.
1728 mEventData->mUpdateHighlightBox = true;
1729 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1730 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1731 mEventData->mLeftSelectionPosition = handleNewPosition;
1734 // 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.
1735 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1737 // Will define the order to scroll the text to match the handle position.
1738 mEventData->mIsLeftHandleSelected = true;
1739 mEventData->mIsRightHandleSelected = false;
1741 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1743 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1745 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1746 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1748 // Updates the highlight box if the handle's new position is different than the current one.
1749 mEventData->mUpdateHighlightBox = true;
1750 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1751 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1752 mEventData->mRightSelectionPosition = handleNewPosition;
1755 // 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.
1756 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1758 // Will define the order to scroll the text to match the handle position.
1759 mEventData->mIsLeftHandleSelected = false;
1760 mEventData->mIsRightHandleSelected = true;
1762 } // end ( HANDLE_PRESSED == state )
1763 else if( ( HANDLE_RELEASED == state ) ||
1764 handleStopScrolling )
1766 CharacterIndex handlePosition = 0u;
1767 if( handleStopScrolling || isSmoothHandlePanEnabled )
1769 // Convert from decorator's coords to text's coords.
1770 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1771 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1773 bool matchedCharacter = false;
1774 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1775 mModel->mLogicalModel,
1779 CharacterHitTest::SCROLL,
1783 if( Event::GRAB_HANDLE_EVENT == event.type )
1785 mEventData->mUpdateCursorPosition = true;
1786 mEventData->mUpdateGrabHandlePosition = true;
1787 mEventData->mUpdateInputStyle = true;
1789 if( !IsClipboardEmpty() )
1791 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1794 if( handleStopScrolling || isSmoothHandlePanEnabled )
1796 mEventData->mScrollAfterUpdatePosition = true;
1797 mEventData->mPrimaryCursorPosition = handlePosition;
1800 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1802 ChangeState( EventData::SELECTING );
1804 mEventData->mUpdateHighlightBox = true;
1805 mEventData->mUpdateLeftSelectionPosition = true;
1806 mEventData->mUpdateRightSelectionPosition = true;
1808 if( handleStopScrolling || isSmoothHandlePanEnabled )
1810 mEventData->mScrollAfterUpdatePosition = true;
1812 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1813 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1815 mEventData->mLeftSelectionPosition = handlePosition;
1819 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1821 ChangeState( EventData::SELECTING );
1823 mEventData->mUpdateHighlightBox = true;
1824 mEventData->mUpdateRightSelectionPosition = true;
1825 mEventData->mUpdateLeftSelectionPosition = true;
1827 if( handleStopScrolling || isSmoothHandlePanEnabled )
1829 mEventData->mScrollAfterUpdatePosition = true;
1830 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1831 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1833 mEventData->mRightSelectionPosition = handlePosition;
1838 mEventData->mDecoratorUpdated = true;
1839 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1840 else if( HANDLE_SCROLLING == state )
1842 const float xSpeed = event.p2.mFloat;
1843 const float ySpeed = event.p3.mFloat;
1844 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1845 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1847 mModel->mScrollPosition.x += xSpeed;
1848 mModel->mScrollPosition.y += ySpeed;
1850 ClampHorizontalScroll( layoutSize );
1851 ClampVerticalScroll( layoutSize );
1853 bool endOfScroll = false;
1854 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1856 // Notify the decorator there is no more text to scroll.
1857 // The decorator won't send more scroll events.
1858 mEventData->mDecorator->NotifyEndOfScroll();
1859 // Still need to set the position of the handle.
1863 // Set the position of the handle.
1864 const bool scrollRightDirection = xSpeed > 0.f;
1865 const bool scrollBottomDirection = ySpeed > 0.f;
1866 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1867 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1869 if( Event::GRAB_HANDLE_EVENT == event.type )
1871 ChangeState( EventData::GRAB_HANDLE_PANNING );
1873 // Get the grab handle position in decorator coords.
1874 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1876 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1878 // Position the grag handle close to either the left or right edge.
1879 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1882 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1884 position.x = mEventData->mCursorHookPositionX;
1886 // Position the grag handle close to either the top or bottom edge.
1887 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1890 // Get the new handle position.
1891 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1892 bool matchedCharacter = false;
1893 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1894 mModel->mLogicalModel,
1896 position.x - mModel->mScrollPosition.x,
1897 position.y - mModel->mScrollPosition.y,
1898 CharacterHitTest::SCROLL,
1901 if( mEventData->mPrimaryCursorPosition != handlePosition )
1903 mEventData->mUpdateCursorPosition = true;
1904 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1905 mEventData->mScrollAfterUpdatePosition = true;
1906 mEventData->mPrimaryCursorPosition = handlePosition;
1908 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1910 // Updates the decorator if the soft handle panning is enabled.
1911 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1913 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1915 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1917 // Get the selection handle position in decorator coords.
1918 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1920 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1922 // Position the selection handle close to either the left or right edge.
1923 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1926 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1928 position.x = mEventData->mCursorHookPositionX;
1930 // Position the grag handle close to either the top or bottom edge.
1931 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1934 // Get the new handle position.
1935 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1936 bool matchedCharacter = false;
1937 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1938 mModel->mLogicalModel,
1940 position.x - mModel->mScrollPosition.x,
1941 position.y - mModel->mScrollPosition.y,
1942 CharacterHitTest::SCROLL,
1945 if( leftSelectionHandleEvent )
1947 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1949 if( differentHandles || endOfScroll )
1951 mEventData->mUpdateHighlightBox = true;
1952 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1953 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1954 mEventData->mLeftSelectionPosition = handlePosition;
1959 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1960 if( differentHandles || endOfScroll )
1962 mEventData->mUpdateHighlightBox = true;
1963 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1964 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1965 mEventData->mRightSelectionPosition = handlePosition;
1969 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1971 RepositionSelectionHandles();
1973 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1976 mEventData->mDecoratorUpdated = true;
1977 } // end ( HANDLE_SCROLLING == state )
1980 void Controller::Impl::OnSelectEvent( const Event& event )
1982 if( NULL == mEventData )
1984 // Nothing to do if there is no text.
1988 if( mEventData->mSelectionEnabled )
1990 // Convert from control's coords to text's coords.
1991 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1992 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1994 // Calculates the logical position from the x,y coords.
1995 RepositionSelectionHandles( xPosition,
1997 Controller::NoTextTap::HIGHLIGHT );
2001 void Controller::Impl::OnSelectAllEvent()
2003 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2005 if( NULL == mEventData )
2007 // Nothing to do if there is no text.
2011 if( mEventData->mSelectionEnabled )
2013 // Calculates the logical position from the start.
2014 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
2015 0.f - mModel->mScrollPosition.y,
2016 Controller::NoTextTap::HIGHLIGHT );
2018 mEventData->mLeftSelectionPosition = 0u;
2019 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
2023 void Controller::Impl::OnSelectNoneEvent()
2025 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2027 if( NULL == mEventData )
2029 // Nothing to do if there is no text.
2033 if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING)
2035 mEventData->mPrimaryCursorPosition = 0u;
2036 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
2037 ChangeState( EventData::INACTIVE );
2038 mEventData->mUpdateCursorPosition = true;
2039 mEventData->mUpdateInputStyle = true;
2040 mEventData->mScrollAfterUpdatePosition = true;
2044 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2046 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2048 // Nothing to select if handles are in the same place.
2049 selectedText.clear();
2053 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2055 //Get start and end position of selection
2056 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2057 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2059 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2060 const Length numberOfCharacters = utf32Characters.Count();
2062 // Validate the start and end selection points
2063 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2065 //Get text as a UTF8 string
2066 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2068 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2070 // Keep a copy of the current input style.
2071 InputStyle currentInputStyle;
2072 currentInputStyle.Copy( mEventData->mInputStyle );
2074 // Set as input style the style of the first deleted character.
2075 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2077 // Compare if the input style has changed.
2078 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2080 if( hasInputStyleChanged )
2082 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2083 // Queue the input style changed signal.
2084 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2087 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2089 // Mark the paragraphs to be updated.
2090 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2092 mTextUpdateInfo.mCharacterIndex = 0;
2093 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2094 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2095 mTextUpdateInfo.mClearAll = true;
2099 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2100 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2103 // Delete text between handles
2104 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2105 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2106 utf32Characters.Erase( first, last );
2108 // Will show the cursor at the first character of the selection.
2109 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2113 // Will show the cursor at the last character of the selection.
2114 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2117 mEventData->mDecoratorUpdated = true;
2121 void Controller::Impl::ShowClipboard()
2125 mClipboard.ShowClipboard();
2129 void Controller::Impl::HideClipboard()
2131 if( mClipboard && mClipboardHideEnabled )
2133 mClipboard.HideClipboard();
2137 void Controller::Impl::SetClipboardHideEnable(bool enable)
2139 mClipboardHideEnabled = enable;
2142 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2144 //Send string to clipboard
2145 return ( mClipboard && mClipboard.SetItem( source ) );
2148 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2150 std::string selectedText;
2151 RetrieveSelection( selectedText, deleteAfterSending );
2152 CopyStringToClipboard( selectedText );
2153 ChangeState( EventData::EDITING );
2156 void Controller::Impl::RequestGetTextFromClipboard()
2160 mClipboard.RequestItem();
2164 void Controller::Impl::RepositionSelectionHandles()
2166 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2167 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2169 if( selectionStart == selectionEnd )
2171 // Nothing to select if handles are in the same place.
2172 // So, deactive Highlight box.
2173 mEventData->mDecorator->SetHighlightActive( false );
2177 mEventData->mDecorator->ClearHighlights();
2179 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2180 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2181 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2182 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2183 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2184 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2185 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2187 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2188 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2189 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2191 // Swap the indices if the start is greater than the end.
2192 const bool indicesSwapped = selectionStart > selectionEnd;
2194 // Tell the decorator to flip the selection handles if needed.
2195 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2197 if( indicesSwapped )
2199 std::swap( selectionStart, selectionEnd );
2202 // Get the indices to the first and last selected glyphs.
2203 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2204 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2205 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2206 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2208 // Get the lines where the glyphs are laid-out.
2209 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2211 LineIndex lineIndex = 0u;
2212 Length numberOfLines = 0u;
2213 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2214 1u + glyphEnd - glyphStart,
2217 const LineIndex firstLineIndex = lineIndex;
2219 // Create the structure to store some selection box info.
2220 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2221 selectionBoxLinesInfo.Resize( numberOfLines );
2223 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2224 selectionBoxInfo->minX = MAX_FLOAT;
2225 selectionBoxInfo->maxX = MIN_FLOAT;
2227 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2228 float minHighlightX = std::numeric_limits<float>::max();
2229 float maxHighlightX = std::numeric_limits<float>::min();
2231 Vector2 highLightPosition; // The highlight position in decorator's coords.
2233 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2235 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2236 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2239 // Transform to decorator's (control) coords.
2240 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2242 lineRun += firstLineIndex;
2244 // The line height is the addition of the line ascender and the line descender.
2245 // However, the line descender has a negative value, hence the subtraction.
2246 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2248 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2250 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2251 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2252 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2254 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2255 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2256 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2258 // The number of quads of the selection box.
2259 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2260 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2262 // Count the actual number of quads.
2263 unsigned int actualNumberOfQuads = 0u;
2266 // Traverse the glyphs.
2267 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2269 const GlyphInfo& glyph = *( glyphsBuffer + index );
2270 const Vector2& position = *( positionsBuffer + index );
2272 if( splitStartGlyph )
2274 // 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.
2276 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2277 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2278 // Get the direction of the character.
2279 CharacterDirection isCurrentRightToLeft = false;
2280 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2282 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2285 // The end point could be in the middle of the ligature.
2286 // Calculate the number of characters selected.
2287 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2289 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2290 quad.y = selectionBoxInfo->lineOffset;
2291 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2292 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2294 // Store the min and max 'x' for each line.
2295 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2296 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2298 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2299 ++actualNumberOfQuads;
2301 splitStartGlyph = false;
2305 if( splitEndGlyph && ( index == glyphEnd ) )
2307 // 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.
2309 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2310 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2311 // Get the direction of the character.
2312 CharacterDirection isCurrentRightToLeft = false;
2313 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2315 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2318 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2320 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2321 quad.y = selectionBoxInfo->lineOffset;
2322 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2323 quad.w = quad.y + selectionBoxInfo->lineHeight;
2325 // Store the min and max 'x' for each line.
2326 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2327 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2329 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2331 ++actualNumberOfQuads;
2333 splitEndGlyph = false;
2337 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2338 quad.y = selectionBoxInfo->lineOffset;
2339 quad.z = quad.x + glyph.advance;
2340 quad.w = quad.y + selectionBoxInfo->lineHeight;
2342 // Store the min and max 'x' for each line.
2343 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2344 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2346 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2348 ++actualNumberOfQuads;
2350 // Whether to retrieve the next line.
2351 if( index == lastGlyphOfLine )
2354 if( lineIndex < firstLineIndex + numberOfLines )
2356 // Retrieve the next line.
2359 // Get the last glyph of the new line.
2360 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2362 // Keep the offset and height of the current selection box.
2363 const float currentLineOffset = selectionBoxInfo->lineOffset;
2364 const float currentLineHeight = selectionBoxInfo->lineHeight;
2366 // Get the selection box info for the next line.
2369 selectionBoxInfo->minX = MAX_FLOAT;
2370 selectionBoxInfo->maxX = MIN_FLOAT;
2372 // Update the line's vertical offset.
2373 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2375 // The line height is the addition of the line ascender and the line descender.
2376 // However, the line descender has a negative value, hence the subtraction.
2377 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2382 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2383 // The final width is calculated after 'boxifying' the selection.
2384 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2385 endIt = selectionBoxLinesInfo.End();
2389 const SelectionBoxInfo& info = *it;
2391 // Update the size of the highlighted text.
2392 highLightSize.height += info.lineHeight;
2393 minHighlightX = std::min( minHighlightX, info.minX );
2394 maxHighlightX = std::max( maxHighlightX, info.maxX );
2397 // Add extra geometry to 'boxify' the selection.
2399 if( 1u < numberOfLines )
2401 // Boxify the first line.
2402 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2403 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2405 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2406 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2411 quad.y = firstSelectionBoxLineInfo.lineOffset;
2412 quad.z = firstSelectionBoxLineInfo.minX;
2413 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2415 // Boxify at the beginning of the line.
2416 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2418 ++actualNumberOfQuads;
2420 // Update the size of the highlighted text.
2421 minHighlightX = 0.f;
2426 quad.x = firstSelectionBoxLineInfo.maxX;
2427 quad.y = firstSelectionBoxLineInfo.lineOffset;
2428 quad.z = mModel->mVisualModel->mControlSize.width;
2429 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2431 // Boxify at the end of the line.
2432 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2434 ++actualNumberOfQuads;
2436 // Update the size of the highlighted text.
2437 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2440 // Boxify the central lines.
2441 if( 2u < numberOfLines )
2443 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2444 endIt = selectionBoxLinesInfo.End() - 1u;
2448 const SelectionBoxInfo& info = *it;
2451 quad.y = info.lineOffset;
2453 quad.w = info.lineOffset + info.lineHeight;
2455 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2457 ++actualNumberOfQuads;
2460 quad.y = info.lineOffset;
2461 quad.z = mModel->mVisualModel->mControlSize.width;
2462 quad.w = info.lineOffset + info.lineHeight;
2464 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2466 ++actualNumberOfQuads;
2469 // Update the size of the highlighted text.
2470 minHighlightX = 0.f;
2471 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2474 // Boxify the last line.
2475 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2476 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2478 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2479 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2484 quad.y = lastSelectionBoxLineInfo.lineOffset;
2485 quad.z = lastSelectionBoxLineInfo.minX;
2486 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2488 // Boxify at the beginning of the line.
2489 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2491 ++actualNumberOfQuads;
2493 // Update the size of the highlighted text.
2494 minHighlightX = 0.f;
2499 quad.x = lastSelectionBoxLineInfo.maxX;
2500 quad.y = lastSelectionBoxLineInfo.lineOffset;
2501 quad.z = mModel->mVisualModel->mControlSize.width;
2502 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2504 // Boxify at the end of the line.
2505 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2507 ++actualNumberOfQuads;
2509 // Update the size of the highlighted text.
2510 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2514 // Set the actual number of quads.
2515 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2517 // Sets the highlight's size and position. In decorator's coords.
2518 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2519 highLightSize.width = maxHighlightX - minHighlightX;
2521 highLightPosition.x = minHighlightX;
2522 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2523 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2525 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2527 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2529 CursorInfo primaryCursorInfo;
2530 GetCursorPosition( mEventData->mLeftSelectionPosition,
2531 primaryCursorInfo );
2533 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2535 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2537 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2538 primaryCursorInfo.lineHeight );
2540 CursorInfo secondaryCursorInfo;
2541 GetCursorPosition( mEventData->mRightSelectionPosition,
2542 secondaryCursorInfo );
2544 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2546 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2547 secondaryPosition.x,
2548 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2549 secondaryCursorInfo.lineHeight );
2552 // Set the flag to update the decorator.
2553 mEventData->mDecoratorUpdated = true;
2556 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2558 if( NULL == mEventData )
2560 // Nothing to do if there is no text input.
2564 if( IsShowingPlaceholderText() )
2566 // Nothing to do if there is the place-holder text.
2570 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2571 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2572 if( ( 0 == numberOfGlyphs ) ||
2573 ( 0 == numberOfLines ) )
2575 // Nothing to do if there is no text.
2579 // Find which word was selected
2580 CharacterIndex selectionStart( 0 );
2581 CharacterIndex selectionEnd( 0 );
2582 CharacterIndex noTextHitIndex( 0 );
2583 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2584 mModel->mLogicalModel,
2591 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2593 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2595 ChangeState( EventData::SELECTING );
2597 mEventData->mLeftSelectionPosition = selectionStart;
2598 mEventData->mRightSelectionPosition = selectionEnd;
2600 mEventData->mUpdateLeftSelectionPosition = true;
2601 mEventData->mUpdateRightSelectionPosition = true;
2602 mEventData->mUpdateHighlightBox = true;
2604 // It may happen an InputMethodContext commit event arrives before the selection event
2605 // if the InputMethodContext is in pre-edit state. The commit event will set the
2606 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2607 // to false, the highlight box won't be updated.
2608 mEventData->mUpdateCursorPosition = false;
2610 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2612 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2613 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2615 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2617 // Nothing to select. i.e. a white space, out of bounds
2618 ChangeState( EventData::EDITING_WITH_POPUP );
2620 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2622 mEventData->mUpdateCursorPosition = true;
2623 mEventData->mUpdateGrabHandlePosition = true;
2624 mEventData->mScrollAfterUpdatePosition = true;
2625 mEventData->mUpdateInputStyle = true;
2627 else if( Controller::NoTextTap::NO_ACTION == action )
2629 // Nothing to select. i.e. a white space, out of bounds
2630 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2632 mEventData->mUpdateCursorPosition = true;
2633 mEventData->mUpdateGrabHandlePosition = true;
2634 mEventData->mScrollAfterUpdatePosition = true;
2635 mEventData->mUpdateInputStyle = true;
2639 void Controller::Impl::SetPopupButtons()
2642 * Sets the Popup buttons to be shown depending on State.
2644 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2646 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2649 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2651 if( EventData::SELECTING == mEventData->mState )
2653 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2655 if( !IsClipboardEmpty() )
2657 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2658 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2661 if( !mEventData->mAllTextSelected )
2663 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2666 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2668 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2670 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2673 if( !IsClipboardEmpty() )
2675 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2676 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2679 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2681 if ( !IsClipboardEmpty() )
2683 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2684 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2688 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2691 void Controller::Impl::ChangeState( EventData::State newState )
2693 if( NULL == mEventData )
2695 // Nothing to do if there is no text input.
2699 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2701 if( mEventData->mState != newState )
2703 mEventData->mPreviousState = mEventData->mState;
2704 mEventData->mState = newState;
2706 switch( mEventData->mState )
2708 case EventData::INACTIVE:
2710 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2711 mEventData->mDecorator->StopCursorBlink();
2712 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2713 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2714 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2715 mEventData->mDecorator->SetHighlightActive( false );
2716 mEventData->mDecorator->SetPopupActive( false );
2717 mEventData->mDecoratorUpdated = true;
2720 case EventData::INTERRUPTED:
2722 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2723 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2724 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2725 mEventData->mDecorator->SetHighlightActive( false );
2726 mEventData->mDecorator->SetPopupActive( false );
2727 mEventData->mDecoratorUpdated = true;
2730 case EventData::SELECTING:
2732 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2733 mEventData->mDecorator->StopCursorBlink();
2734 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2735 if ( mEventData->mGrabHandleEnabled )
2737 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2738 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2740 mEventData->mDecorator->SetHighlightActive( true );
2741 if( mEventData->mGrabHandlePopupEnabled )
2744 mEventData->mDecorator->SetPopupActive( true );
2746 mEventData->mDecoratorUpdated = true;
2749 case EventData::EDITING:
2751 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2752 if( mEventData->mCursorBlinkEnabled )
2754 mEventData->mDecorator->StartCursorBlink();
2756 // Grab handle is not shown until a tap is received whilst EDITING
2757 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2758 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2759 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2760 mEventData->mDecorator->SetHighlightActive( false );
2761 if( mEventData->mGrabHandlePopupEnabled )
2763 mEventData->mDecorator->SetPopupActive( false );
2765 mEventData->mDecoratorUpdated = true;
2768 case EventData::EDITING_WITH_POPUP:
2770 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2772 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2773 if( mEventData->mCursorBlinkEnabled )
2775 mEventData->mDecorator->StartCursorBlink();
2777 if( mEventData->mSelectionEnabled )
2779 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2780 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2781 mEventData->mDecorator->SetHighlightActive( false );
2783 else if ( mEventData->mGrabHandleEnabled )
2785 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2787 if( mEventData->mGrabHandlePopupEnabled )
2790 mEventData->mDecorator->SetPopupActive( true );
2792 mEventData->mDecoratorUpdated = true;
2795 case EventData::EDITING_WITH_GRAB_HANDLE:
2797 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2799 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2800 if( mEventData->mCursorBlinkEnabled )
2802 mEventData->mDecorator->StartCursorBlink();
2804 // Grab handle is not shown until a tap is received whilst EDITING
2805 if ( mEventData->mGrabHandleEnabled )
2807 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2809 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2810 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2811 mEventData->mDecorator->SetHighlightActive( false );
2812 if( mEventData->mGrabHandlePopupEnabled )
2814 mEventData->mDecorator->SetPopupActive( false );
2816 mEventData->mDecoratorUpdated = true;
2819 case EventData::SELECTION_HANDLE_PANNING:
2821 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2822 mEventData->mDecorator->StopCursorBlink();
2823 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2824 if ( mEventData->mGrabHandleEnabled )
2826 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2827 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2829 mEventData->mDecorator->SetHighlightActive( true );
2830 if( mEventData->mGrabHandlePopupEnabled )
2832 mEventData->mDecorator->SetPopupActive( false );
2834 mEventData->mDecoratorUpdated = true;
2837 case EventData::GRAB_HANDLE_PANNING:
2839 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2841 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2842 if( mEventData->mCursorBlinkEnabled )
2844 mEventData->mDecorator->StartCursorBlink();
2846 if ( mEventData->mGrabHandleEnabled )
2848 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2850 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2851 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2852 mEventData->mDecorator->SetHighlightActive( false );
2853 if( mEventData->mGrabHandlePopupEnabled )
2855 mEventData->mDecorator->SetPopupActive( false );
2857 mEventData->mDecoratorUpdated = true;
2860 case EventData::EDITING_WITH_PASTE_POPUP:
2862 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2864 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2865 if( mEventData->mCursorBlinkEnabled )
2867 mEventData->mDecorator->StartCursorBlink();
2870 if ( mEventData->mGrabHandleEnabled )
2872 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2874 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2875 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2876 mEventData->mDecorator->SetHighlightActive( false );
2878 if( mEventData->mGrabHandlePopupEnabled )
2881 mEventData->mDecorator->SetPopupActive( true );
2883 mEventData->mDecoratorUpdated = true;
2886 case EventData::TEXT_PANNING:
2888 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2889 mEventData->mDecorator->StopCursorBlink();
2890 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2891 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2892 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2894 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2895 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2896 mEventData->mDecorator->SetHighlightActive( true );
2899 if( mEventData->mGrabHandlePopupEnabled )
2901 mEventData->mDecorator->SetPopupActive( false );
2904 mEventData->mDecoratorUpdated = true;
2911 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2912 CursorInfo& cursorInfo )
2914 if( !IsShowingRealText() )
2916 // Do not want to use the place-holder text to set the cursor position.
2918 // Use the line's height of the font's family set to set the cursor's size.
2919 // If there is no font's family set, use the default font.
2920 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2922 cursorInfo.lineOffset = 0.f;
2923 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2924 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2927 if( mModel->mMatchSystemLanguageDirection )
2929 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2932 switch( mModel->mHorizontalAlignment )
2934 case Text::HorizontalAlignment::BEGIN :
2938 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2942 cursorInfo.primaryPosition.x = 0.f;
2946 case Text::HorizontalAlignment::CENTER:
2948 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2951 case Text::HorizontalAlignment::END:
2955 cursorInfo.primaryPosition.x = 0.f;
2959 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2965 // Nothing else to do.
2969 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2970 GetCursorPositionParameters parameters;
2971 parameters.visualModel = mModel->mVisualModel;
2972 parameters.logicalModel = mModel->mLogicalModel;
2973 parameters.metrics = mMetrics;
2974 parameters.logical = logical;
2975 parameters.isMultiline = isMultiLine;
2977 Text::GetCursorPosition( parameters,
2980 // Adds Outline offset.
2981 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2982 cursorInfo.primaryPosition.x += outlineWidth;
2983 cursorInfo.primaryPosition.y += outlineWidth;
2984 cursorInfo.secondaryPosition.x += outlineWidth;
2985 cursorInfo.secondaryPosition.y += outlineWidth;
2989 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2991 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2992 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2994 if( 0.f > cursorInfo.primaryPosition.x )
2996 cursorInfo.primaryPosition.x = 0.f;
2999 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
3000 if( cursorInfo.primaryPosition.x > edgeWidth )
3002 cursorInfo.primaryPosition.x = edgeWidth;
3007 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
3009 if( NULL == mEventData )
3011 // Nothing to do if there is no text input.
3015 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
3017 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
3018 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
3020 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
3021 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3023 if( numberOfCharacters > 1u )
3025 const Script script = mModel->mLogicalModel->GetScript( index );
3026 if( HasLigatureMustBreak( script ) )
3028 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3029 numberOfCharacters = 1u;
3034 while( 0u == numberOfCharacters )
3037 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3041 if( index < mEventData->mPrimaryCursorPosition )
3043 cursorIndex -= numberOfCharacters;
3047 cursorIndex += numberOfCharacters;
3050 // Will update the cursor hook position.
3051 mEventData->mUpdateCursorHookPosition = true;
3056 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3058 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3059 if( NULL == mEventData )
3061 // Nothing to do if there is no text input.
3062 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3066 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3068 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3070 // Sets the cursor position.
3071 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3074 cursorInfo.primaryCursorHeight,
3075 cursorInfo.lineHeight );
3076 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3078 if( mEventData->mUpdateGrabHandlePosition )
3080 // Sets the grab handle position.
3081 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3083 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3084 cursorInfo.lineHeight );
3087 if( cursorInfo.isSecondaryCursor )
3089 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3090 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3091 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3092 cursorInfo.secondaryCursorHeight,
3093 cursorInfo.lineHeight );
3094 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3097 // Set which cursors are active according the state.
3098 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3100 if( cursorInfo.isSecondaryCursor )
3102 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3106 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3111 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3114 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3117 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3118 const CursorInfo& cursorInfo )
3120 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3121 ( RIGHT_SELECTION_HANDLE != handleType ) )
3126 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3128 // Sets the handle's position.
3129 mEventData->mDecorator->SetPosition( handleType,
3131 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3132 cursorInfo.lineHeight );
3134 // If selection handle at start of the text and other at end of the text then all text is selected.
3135 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3136 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3137 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3140 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3142 // Clamp between -space & -alignment offset.
3144 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3146 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3147 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3148 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3150 mEventData->mDecoratorUpdated = true;
3154 mModel->mScrollPosition.x = 0.f;
3158 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3160 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3162 // Nothing to do if the text is single line.
3166 // Clamp between -space & 0.
3167 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3169 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3170 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3171 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3173 mEventData->mDecoratorUpdated = true;
3177 mModel->mScrollPosition.y = 0.f;
3181 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3183 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3185 // position is in actor's coords.
3186 const float positionEndX = position.x + cursorWidth;
3187 const float positionEndY = position.y + lineHeight;
3189 // Transform the position to decorator coords.
3190 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3191 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3193 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3194 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3196 if( decoratorPositionBeginX < 0.f )
3198 mModel->mScrollPosition.x = -position.x;
3200 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3202 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3205 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3207 if( decoratorPositionBeginY < 0.f )
3209 mModel->mScrollPosition.y = -position.y;
3211 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3213 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3218 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3220 // Get the current cursor position in decorator coords.
3221 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3223 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3227 // Calculate the offset to match the cursor position before the character was deleted.
3228 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3230 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3231 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3233 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3234 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3238 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3239 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3241 // Makes the new cursor position visible if needed.
3242 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3245 void Controller::Impl::RequestRelayout()
3247 if( NULL != mControlInterface )
3249 mControlInterface->RequestTextRelayout();
3253 Actor Controller::Impl::CreateBackgroundActor()
3255 // NOTE: Currently we only support background color for one line left-to-right text,
3256 // so the following calculation is based on one line left-to-right text only!
3260 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3261 if( numberOfGlyphs > 0u )
3263 Vector<GlyphInfo> glyphs;
3264 glyphs.Resize( numberOfGlyphs );
3266 Vector<Vector2> positions;
3267 positions.Resize( numberOfGlyphs );
3269 // Get the line where the glyphs are laid-out.
3270 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3271 float alignmentOffset = lineRun->alignmentOffset;
3272 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3278 glyphs.Resize( numberOfGlyphs );
3279 positions.Resize( numberOfGlyphs );
3281 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3282 const Vector2* const positionsBuffer = positions.Begin();
3284 BackgroundMesh mesh;
3285 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3286 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3288 const Vector2 textSize = mView.GetLayoutSize();
3290 const float offsetX = textSize.width * 0.5f;
3291 const float offsetY = textSize.height * 0.5f;
3293 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3294 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3295 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3298 uint32_t numberOfQuads = 0u;
3300 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3302 const GlyphInfo& glyph = *( glyphsBuffer + i );
3304 // Get the background color of the character.
3305 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3306 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3307 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3309 // Only create quads for glyphs with a background color
3310 if ( backgroundColor != Color::TRANSPARENT )
3312 const Vector2 position = *( positionsBuffer + i );
3314 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3316 quad.x = position.x;
3318 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3319 quad.w = textSize.height;
3321 else if ( i == 0u ) // The first glyph in the whole text
3323 quad.x = position.x;
3325 quad.z = quad.x - glyph.xBearing + glyph.advance;
3326 quad.w = textSize.height;
3328 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3330 quad.x = position.x - glyph.xBearing;
3332 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3333 quad.w = textSize.height;
3335 else // The glyph in the middle of the text
3337 quad.x = position.x - glyph.xBearing;
3339 quad.z = quad.x + glyph.advance;
3340 quad.w = textSize.height;
3343 BackgroundVertex vertex;
3346 vertex.mPosition.x = quad.x - offsetX;
3347 vertex.mPosition.y = quad.y - offsetY;
3348 vertex.mColor = backgroundColor;
3349 mesh.mVertices.PushBack( vertex );
3352 vertex.mPosition.x = quad.z - offsetX;
3353 vertex.mPosition.y = quad.y - offsetY;
3354 vertex.mColor = backgroundColor;
3355 mesh.mVertices.PushBack( vertex );
3358 vertex.mPosition.x = quad.x - offsetX;
3359 vertex.mPosition.y = quad.w - offsetY;
3360 vertex.mColor = backgroundColor;
3361 mesh.mVertices.PushBack( vertex );
3364 vertex.mPosition.x = quad.z - offsetX;
3365 vertex.mPosition.y = quad.w - offsetY;
3366 vertex.mColor = backgroundColor;
3367 mesh.mVertices.PushBack( vertex );
3369 // Six indices in counter clockwise winding
3370 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3371 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3372 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3373 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3374 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3375 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3381 // Only create the background actor if there are glyphs with background color
3382 if ( mesh.mVertices.Count() > 0u )
3384 Property::Map quadVertexFormat;
3385 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3386 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3388 PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
3389 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3391 Geometry quadGeometry = Geometry::New();
3392 quadGeometry.AddVertexBuffer( quadVertices );
3393 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3395 if( !mShaderBackground )
3397 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3400 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3401 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3402 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3404 actor = Actor::New();
3405 actor.SetName( "TextBackgroundColorActor" );
3406 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
3407 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3408 actor.SetSize( textSize );
3409 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
3410 actor.AddRenderer( renderer );
3419 } // namespace Toolkit