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 ),
163 mEditingEnabled( true )
167 EventData::~EventData()
170 bool Controller::Impl::ProcessInputEvents()
172 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
173 if( NULL == mEventData )
175 // Nothing to do if there is no text input.
176 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
180 if( mEventData->mDecorator )
182 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
183 iter != mEventData->mEventQueue.end();
188 case Event::CURSOR_KEY_EVENT:
190 OnCursorKeyEvent( *iter );
193 case Event::TAP_EVENT:
198 case Event::LONG_PRESS_EVENT:
200 OnLongPressEvent( *iter );
203 case Event::PAN_EVENT:
208 case Event::GRAB_HANDLE_EVENT:
209 case Event::LEFT_SELECTION_HANDLE_EVENT:
210 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
212 OnHandleEvent( *iter );
217 OnSelectEvent( *iter );
220 case Event::SELECT_ALL:
225 case Event::SELECT_NONE:
234 if( mEventData->mUpdateCursorPosition ||
235 mEventData->mUpdateHighlightBox )
237 NotifyInputMethodContext();
240 // The cursor must also be repositioned after inserts into the model
241 if( mEventData->mUpdateCursorPosition )
243 // Updates the cursor position and scrolls the text to make it visible.
244 CursorInfo cursorInfo;
245 // Calculate the cursor position from the new cursor index.
246 GetCursorPosition( mEventData->mPrimaryCursorPosition,
249 if( mEventData->mUpdateCursorHookPosition )
251 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
252 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
253 mEventData->mUpdateCursorHookPosition = false;
256 // Scroll first the text after delete ...
257 if( mEventData->mScrollAfterDelete )
259 ScrollTextToMatchCursor( cursorInfo );
262 // ... then, text can be scrolled to make the cursor visible.
263 if( mEventData->mScrollAfterUpdatePosition )
265 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
266 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
268 mEventData->mScrollAfterUpdatePosition = false;
269 mEventData->mScrollAfterDelete = false;
271 UpdateCursorPosition( cursorInfo );
273 mEventData->mDecoratorUpdated = true;
274 mEventData->mUpdateCursorPosition = false;
275 mEventData->mUpdateGrabHandlePosition = false;
279 CursorInfo leftHandleInfo;
280 CursorInfo rightHandleInfo;
282 if( mEventData->mUpdateHighlightBox )
284 GetCursorPosition( mEventData->mLeftSelectionPosition,
287 GetCursorPosition( mEventData->mRightSelectionPosition,
290 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
292 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
294 CursorInfo& infoLeft = leftHandleInfo;
296 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
297 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
299 CursorInfo& infoRight = rightHandleInfo;
301 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
302 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
306 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
308 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
309 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
314 if( mEventData->mUpdateLeftSelectionPosition )
316 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
320 mEventData->mDecoratorUpdated = true;
321 mEventData->mUpdateLeftSelectionPosition = false;
324 if( mEventData->mUpdateRightSelectionPosition )
326 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
330 mEventData->mDecoratorUpdated = true;
331 mEventData->mUpdateRightSelectionPosition = false;
334 if( mEventData->mUpdateHighlightBox )
336 RepositionSelectionHandles();
338 mEventData->mUpdateLeftSelectionPosition = false;
339 mEventData->mUpdateRightSelectionPosition = false;
340 mEventData->mUpdateHighlightBox = false;
341 mEventData->mIsLeftHandleSelected = false;
342 mEventData->mIsRightHandleSelected = false;
345 mEventData->mScrollAfterUpdatePosition = false;
348 if( mEventData->mUpdateInputStyle )
350 // Keep a copy of the current input style.
351 InputStyle currentInputStyle;
352 currentInputStyle.Copy( mEventData->mInputStyle );
354 // Set the default style first.
355 RetrieveDefaultInputStyle( mEventData->mInputStyle );
357 // Get the character index from the cursor index.
358 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
360 // Retrieve the style from the style runs stored in the logical model.
361 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
363 // Compare if the input style has changed.
364 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
366 if( hasInputStyleChanged )
368 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
369 // Queue the input style changed signal.
370 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
373 mEventData->mUpdateInputStyle = false;
376 mEventData->mEventQueue.clear();
378 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
380 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
381 mEventData->mDecoratorUpdated = false;
383 return decoratorUpdated;
386 void Controller::Impl::NotifyInputMethodContext()
388 if( mEventData && mEventData->mInputMethodContext )
390 CharacterIndex cursorPosition = GetLogicalCursorPosition();
392 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
394 // Update the cursor position by removing the initial white spaces.
395 if( cursorPosition < numberOfWhiteSpaces )
401 cursorPosition -= numberOfWhiteSpaces;
404 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
405 mEventData->mInputMethodContext.NotifyCursorPosition();
409 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
411 if ( mEventData && mEventData->mInputMethodContext )
413 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
414 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
418 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
420 CharacterIndex cursorPosition = 0u;
424 if( ( EventData::SELECTING == mEventData->mState ) ||
425 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
427 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
431 cursorPosition = mEventData->mPrimaryCursorPosition;
435 return cursorPosition;
438 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
440 Length numberOfWhiteSpaces = 0u;
442 // Get the buffer to the text.
443 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
445 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
446 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
448 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
454 return numberOfWhiteSpaces;
457 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
459 // Get the total number of characters.
460 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
462 // Retrieve the text.
463 if( 0u != numberOfCharacters )
465 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
469 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
471 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
472 mTextUpdateInfo.mStartGlyphIndex = 0u;
473 mTextUpdateInfo.mStartLineIndex = 0u;
474 numberOfCharacters = 0u;
476 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
477 if( 0u == numberOfParagraphs )
479 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
480 numberOfCharacters = 0u;
482 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
484 // Nothing else to do if there are no paragraphs.
488 // Find the paragraphs to be updated.
489 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
490 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
492 // Text is being added at the end of the current text.
493 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
495 // Text is being added in a new paragraph after the last character of the text.
496 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
497 numberOfCharacters = 0u;
498 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
500 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
501 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
503 // Nothing else to do;
507 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
511 Length numberOfCharactersToUpdate = 0u;
512 if( mTextUpdateInfo.mFullRelayoutNeeded )
514 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
518 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
520 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
521 numberOfCharactersToUpdate,
522 paragraphsToBeUpdated );
525 if( 0u != paragraphsToBeUpdated.Count() )
527 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
528 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
529 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
531 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
532 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
534 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
535 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
536 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
537 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
539 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
540 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
542 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
546 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
550 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
551 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
554 void Controller::Impl::ClearFullModelData( OperationsMask operations )
556 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
558 mModel->mLogicalModel->mLineBreakInfo.Clear();
559 mModel->mLogicalModel->mParagraphInfo.Clear();
562 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
564 mModel->mLogicalModel->mScriptRuns.Clear();
567 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
569 mModel->mLogicalModel->mFontRuns.Clear();
572 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
574 if( NO_OPERATION != ( BIDI_INFO & operations ) )
576 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
577 mModel->mLogicalModel->mCharacterDirections.Clear();
580 if( NO_OPERATION != ( REORDER & operations ) )
582 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
583 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
584 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
588 BidirectionalLineInfoRun& bidiLineInfo = *it;
590 free( bidiLineInfo.visualToLogicalMap );
591 bidiLineInfo.visualToLogicalMap = NULL;
593 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
597 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
599 mModel->mVisualModel->mGlyphs.Clear();
600 mModel->mVisualModel->mGlyphsToCharacters.Clear();
601 mModel->mVisualModel->mCharactersToGlyph.Clear();
602 mModel->mVisualModel->mCharactersPerGlyph.Clear();
603 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
604 mModel->mVisualModel->mGlyphPositions.Clear();
607 if( NO_OPERATION != ( LAYOUT & operations ) )
609 mModel->mVisualModel->mLines.Clear();
612 if( NO_OPERATION != ( COLOR & operations ) )
614 mModel->mVisualModel->mColorIndices.Clear();
615 mModel->mVisualModel->mBackgroundColorIndices.Clear();
619 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
621 const CharacterIndex endIndexPlusOne = endIndex + 1u;
623 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
625 // Clear the line break info.
626 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
628 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
629 lineBreakInfoBuffer + endIndexPlusOne );
631 // Clear the paragraphs.
632 ClearCharacterRuns( startIndex,
634 mModel->mLogicalModel->mParagraphInfo );
637 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
639 // Clear the scripts.
640 ClearCharacterRuns( startIndex,
642 mModel->mLogicalModel->mScriptRuns );
645 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
648 ClearCharacterRuns( startIndex,
650 mModel->mLogicalModel->mFontRuns );
653 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
655 if( NO_OPERATION != ( BIDI_INFO & operations ) )
657 // Clear the bidirectional paragraph info.
658 ClearCharacterRuns( startIndex,
660 mModel->mLogicalModel->mBidirectionalParagraphInfo );
662 // Clear the character's directions.
663 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
665 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
666 characterDirectionsBuffer + endIndexPlusOne );
669 if( NO_OPERATION != ( REORDER & operations ) )
671 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
672 uint32_t endRemoveIndex = startRemoveIndex;
673 ClearCharacterRuns( startIndex,
675 mModel->mLogicalModel->mBidirectionalLineInfo,
679 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
681 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
682 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
683 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
687 BidirectionalLineInfoRun& bidiLineInfo = *it;
689 free( bidiLineInfo.visualToLogicalMap );
690 bidiLineInfo.visualToLogicalMap = NULL;
693 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
694 bidirectionalLineInfoBuffer + endRemoveIndex );
699 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
701 const CharacterIndex endIndexPlusOne = endIndex + 1u;
702 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
704 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
705 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
706 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
708 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
709 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
711 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
713 // Update the character to glyph indices.
714 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
715 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
719 CharacterIndex& index = *it;
720 index -= numberOfGlyphsRemoved;
723 // Clear the character to glyph conversion table.
724 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
725 charactersToGlyphBuffer + endIndexPlusOne );
727 // Clear the glyphs per character table.
728 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
729 glyphsPerCharacterBuffer + endIndexPlusOne );
731 // Clear the glyphs buffer.
732 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
733 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
734 glyphsBuffer + endGlyphIndexPlusOne );
736 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
738 // Update the glyph to character indices.
739 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
740 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
744 CharacterIndex& index = *it;
745 index -= numberOfCharactersRemoved;
748 // Clear the glyphs to characters buffer.
749 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
750 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
752 // Clear the characters per glyph buffer.
753 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
754 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
755 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
757 // Clear the positions buffer.
758 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
759 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
760 positionsBuffer + endGlyphIndexPlusOne );
763 if( NO_OPERATION != ( LAYOUT & operations ) )
766 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
767 uint32_t endRemoveIndex = startRemoveIndex;
768 ClearCharacterRuns( startIndex,
770 mModel->mVisualModel->mLines,
774 // Will update the glyph runs.
775 startRemoveIndex = mModel->mVisualModel->mLines.Count();
776 endRemoveIndex = startRemoveIndex;
777 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
778 endGlyphIndexPlusOne - 1u,
779 mModel->mVisualModel->mLines,
783 // Set the line index from where to insert the new laid-out lines.
784 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
786 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
787 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
788 linesBuffer + endRemoveIndex );
791 if( NO_OPERATION != ( COLOR & operations ) )
793 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
795 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
796 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
797 colorIndexBuffer + endGlyphIndexPlusOne );
800 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
802 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
803 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
804 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
809 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
811 if( mTextUpdateInfo.mClearAll ||
812 ( ( 0u == startIndex ) &&
813 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
815 ClearFullModelData( operations );
819 // Clear the model data related with characters.
820 ClearCharacterModelData( startIndex, endIndex, operations );
822 // Clear the model data related with glyphs.
823 ClearGlyphModelData( startIndex, endIndex, operations );
826 // The estimated number of lines. Used to avoid reallocations when layouting.
827 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
829 mModel->mVisualModel->ClearCaches();
832 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
834 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
836 // Calculate the operations to be done.
837 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
839 if( NO_OPERATION == operations )
841 // Nothing to do if no operations are pending and required.
845 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
846 Vector<Character> displayCharacters;
847 bool useHiddenText = false;
848 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
850 mHiddenInput->Substitute( srcCharacters,displayCharacters );
851 useHiddenText = true;
854 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
855 const Length numberOfCharacters = utf32Characters.Count();
857 // Index to the first character of the first paragraph to be updated.
858 CharacterIndex startIndex = 0u;
859 // Number of characters of the paragraphs to be removed.
860 Length paragraphCharacters = 0u;
862 CalculateTextUpdateIndices( paragraphCharacters );
864 // Check whether the indices for updating the text is valid
865 if ( numberOfCharacters > 0u &&
866 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
867 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
869 std::string currentText;
870 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
872 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
873 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
875 // Dump mTextUpdateInfo
876 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
877 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
878 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
879 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
880 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
881 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
882 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
883 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
884 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
885 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
886 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
887 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
888 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
893 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
895 if( mTextUpdateInfo.mClearAll ||
896 ( 0u != paragraphCharacters ) )
898 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
901 mTextUpdateInfo.mClearAll = false;
903 // Whether the model is updated.
904 bool updated = false;
906 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
907 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
909 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
911 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
912 // calculate the bidirectional info for each 'paragraph'.
913 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
914 // is not shaped together).
915 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
917 SetLineBreakInfo( utf32Characters,
919 requestedNumberOfCharacters,
922 // Create the paragraph info.
923 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
924 requestedNumberOfCharacters );
928 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
929 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
931 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
932 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
934 if( getScripts || validateFonts )
936 // Validates the fonts assigned by the application or assigns default ones.
937 // It makes sure all the characters are going to be rendered by the correct font.
938 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
942 // Retrieves the scripts used in the text.
943 multilanguageSupport.SetScripts( utf32Characters,
945 requestedNumberOfCharacters,
951 // Validate the fonts set through the mark-up string.
952 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
954 // Get the default font's description.
955 TextAbstraction::FontDescription defaultFontDescription;
956 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
958 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
960 // If the placeholder font is set specifically, only placeholder font is changed.
961 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
962 if( mEventData->mPlaceholderFont->sizeDefined )
964 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
967 else if( NULL != mFontDefaults )
969 // Set the normal font and the placeholder font.
970 defaultFontDescription = mFontDefaults->mFontDescription;
972 if( mTextFitEnabled )
974 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
978 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
982 // Validates the fonts. If there is a character with no assigned font it sets a default one.
983 // After this call, fonts are validated.
984 multilanguageSupport.ValidateFonts( utf32Characters,
987 defaultFontDescription,
990 requestedNumberOfCharacters,
996 Vector<Character> mirroredUtf32Characters;
997 bool textMirrored = false;
998 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
999 if( NO_OPERATION != ( BIDI_INFO & operations ) )
1001 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
1002 bidirectionalInfo.Reserve( numberOfParagraphs );
1004 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1005 SetBidirectionalInfo( utf32Characters,
1009 requestedNumberOfCharacters,
1011 mModel->mMatchSystemLanguageDirection,
1014 if( 0u != bidirectionalInfo.Count() )
1016 // Only set the character directions if there is right to left characters.
1017 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1018 GetCharactersDirection( bidirectionalInfo,
1021 requestedNumberOfCharacters,
1024 // This paragraph has right to left text. Some characters may need to be mirrored.
1025 // TODO: consider if the mirrored string can be stored as well.
1027 textMirrored = GetMirroredText( utf32Characters,
1031 requestedNumberOfCharacters,
1032 mirroredUtf32Characters );
1036 // There is no right to left characters. Clear the directions vector.
1037 mModel->mLogicalModel->mCharacterDirections.Clear();
1042 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1043 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1044 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1045 Vector<GlyphIndex> newParagraphGlyphs;
1046 newParagraphGlyphs.Reserve( numberOfParagraphs );
1048 const Length currentNumberOfGlyphs = glyphs.Count();
1049 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1051 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1053 ShapeText( textToShape,
1058 mTextUpdateInfo.mStartGlyphIndex,
1059 requestedNumberOfCharacters,
1061 glyphsToCharactersMap,
1063 newParagraphGlyphs );
1065 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1066 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1067 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1071 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1073 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1075 GlyphInfo* glyphsBuffer = glyphs.Begin();
1076 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1078 // Update the width and advance of all new paragraph characters.
1079 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1081 const GlyphIndex index = *it;
1082 GlyphInfo& glyph = *( glyphsBuffer + index );
1084 glyph.xBearing = 0.f;
1086 glyph.advance = 0.f;
1091 if( ( NULL != mEventData ) &&
1092 mEventData->mPreEditFlag &&
1093 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1095 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
1096 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1097 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1099 // Check the type of preedit and run it.
1100 for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1102 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1103 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1104 type = attrData.preeditType;
1106 // Check the number of commit characters for the start position.
1107 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1108 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1112 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1114 // Add the underline for the pre-edit text.
1115 GlyphRun underlineRun;
1116 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1117 underlineRun.numberOfGlyphs = numberOfIndices;
1118 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1121 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1123 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1124 ColorRun backgroundColorRun;
1125 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1126 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1127 backgroundColorRun.color = textColor;
1128 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1130 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1131 Vector<ColorRun> colorRuns;
1132 colorRuns.Resize( 1u );
1133 ColorRun& colorRun = *( colorRuns.Begin() );
1134 colorRun.color = backgroundColor;
1135 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1136 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1138 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1141 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1143 ColorRun backgroundColorRun;
1144 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1145 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1146 backgroundColorRun.color = LIGHT_BLUE;
1147 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1150 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1152 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1153 ColorRun backgroundColorRun;
1154 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1155 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1156 backgroundColorRun.color = BACKGROUND_SUB4;
1157 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1159 GlyphRun underlineRun;
1160 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1161 underlineRun.numberOfGlyphs = numberOfIndices;
1162 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1165 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1167 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1168 ColorRun backgroundColorRun;
1169 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1170 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1171 backgroundColorRun.color = BACKGROUND_SUB5;
1172 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1174 GlyphRun underlineRun;
1175 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1176 underlineRun.numberOfGlyphs = numberOfIndices;
1177 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1180 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1182 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1183 ColorRun backgroundColorRun;
1184 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1185 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1186 backgroundColorRun.color = BACKGROUND_SUB6;
1187 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1189 GlyphRun underlineRun;
1190 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1191 underlineRun.numberOfGlyphs = numberOfIndices;
1192 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1195 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1197 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1198 ColorRun backgroundColorRun;
1199 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1200 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1201 backgroundColorRun.color = BACKGROUND_SUB7;
1202 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1204 GlyphRun underlineRun;
1205 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1206 underlineRun.numberOfGlyphs = numberOfIndices;
1207 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1210 case Dali::InputMethodContext::PreeditStyle::NONE:
1221 if( NO_OPERATION != ( COLOR & operations ) )
1223 // Set the color runs in glyphs.
1224 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1225 mModel->mVisualModel->mCharactersToGlyph,
1226 mModel->mVisualModel->mGlyphsPerCharacter,
1228 mTextUpdateInfo.mStartGlyphIndex,
1229 requestedNumberOfCharacters,
1230 mModel->mVisualModel->mColors,
1231 mModel->mVisualModel->mColorIndices );
1233 // Set the background color runs in glyphs.
1234 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1235 mModel->mVisualModel->mCharactersToGlyph,
1236 mModel->mVisualModel->mGlyphsPerCharacter,
1238 mTextUpdateInfo.mStartGlyphIndex,
1239 requestedNumberOfCharacters,
1240 mModel->mVisualModel->mBackgroundColors,
1241 mModel->mVisualModel->mBackgroundColorIndices );
1247 // The estimated number of lines. Used to avoid reallocations when layouting.
1248 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1250 // Set the previous number of characters for the next time the text is updated.
1251 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1256 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1258 // Sets the default text's color.
1259 inputStyle.textColor = mTextColor;
1260 inputStyle.isDefaultColor = true;
1262 inputStyle.familyName.clear();
1263 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1264 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1265 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1266 inputStyle.size = 0.f;
1268 inputStyle.lineSpacing = 0.f;
1270 inputStyle.underlineProperties.clear();
1271 inputStyle.shadowProperties.clear();
1272 inputStyle.embossProperties.clear();
1273 inputStyle.outlineProperties.clear();
1275 inputStyle.isFamilyDefined = false;
1276 inputStyle.isWeightDefined = false;
1277 inputStyle.isWidthDefined = false;
1278 inputStyle.isSlantDefined = false;
1279 inputStyle.isSizeDefined = false;
1281 inputStyle.isLineSpacingDefined = false;
1283 inputStyle.isUnderlineDefined = false;
1284 inputStyle.isShadowDefined = false;
1285 inputStyle.isEmbossDefined = false;
1286 inputStyle.isOutlineDefined = false;
1288 // Sets the default font's family name, weight, width, slant and size.
1291 if( mFontDefaults->familyDefined )
1293 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1294 inputStyle.isFamilyDefined = true;
1297 if( mFontDefaults->weightDefined )
1299 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1300 inputStyle.isWeightDefined = true;
1303 if( mFontDefaults->widthDefined )
1305 inputStyle.width = mFontDefaults->mFontDescription.width;
1306 inputStyle.isWidthDefined = true;
1309 if( mFontDefaults->slantDefined )
1311 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1312 inputStyle.isSlantDefined = true;
1315 if( mFontDefaults->sizeDefined )
1317 inputStyle.size = mFontDefaults->mDefaultPointSize;
1318 inputStyle.isSizeDefined = true;
1323 float Controller::Impl::GetDefaultFontLineHeight()
1325 FontId defaultFontId = 0u;
1326 if( NULL == mFontDefaults )
1328 TextAbstraction::FontDescription fontDescription;
1329 defaultFontId = mFontClient.GetFontId( fontDescription );
1333 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1336 Text::FontMetrics fontMetrics;
1337 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1339 return( fontMetrics.ascender - fontMetrics.descender );
1342 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1344 if( NULL == mEventData || !IsShowingRealText() )
1346 // Nothing to do if there is no text input.
1350 int keyCode = event.p1.mInt;
1351 bool isShiftModifier = event.p2.mBool;
1353 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1355 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1357 if( mEventData->mPrimaryCursorPosition > 0u )
1359 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1361 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1365 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1369 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1371 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1373 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1375 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1379 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1383 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1385 // Ignore Shift-Up for text selection for now.
1387 // Get first the line index of the current cursor position index.
1388 CharacterIndex characterIndex = 0u;
1390 if( mEventData->mPrimaryCursorPosition > 0u )
1392 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1395 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1396 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1398 // Retrieve the cursor position info.
1399 CursorInfo cursorInfo;
1400 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1403 // Get the line above.
1404 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1406 // Get the next hit 'y' point.
1407 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1409 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1410 bool matchedCharacter = false;
1411 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1412 mModel->mLogicalModel,
1414 mEventData->mCursorHookPositionX,
1416 CharacterHitTest::TAP,
1419 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1421 // Ignore Shift-Down for text selection for now.
1423 // Get first the line index of the current cursor position index.
1424 CharacterIndex characterIndex = 0u;
1426 if( mEventData->mPrimaryCursorPosition > 0u )
1428 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1431 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1433 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1435 // Retrieve the cursor position info.
1436 CursorInfo cursorInfo;
1437 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1440 // Get the line below.
1441 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1443 // Get the next hit 'y' point.
1444 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1446 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1447 bool matchedCharacter = false;
1448 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1449 mModel->mLogicalModel,
1451 mEventData->mCursorHookPositionX,
1453 CharacterHitTest::TAP,
1458 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1460 // Update selection position after moving the cursor
1461 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1462 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1465 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1467 // Handle text selection
1468 bool selecting = false;
1470 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1472 // Shift-Left/Right to select the text
1473 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1474 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1476 mEventData->mRightSelectionPosition += cursorPositionDelta;
1480 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1482 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1488 // Notify the cursor position to the InputMethodContext.
1489 if( mEventData->mInputMethodContext )
1491 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1492 mEventData->mInputMethodContext.NotifyCursorPosition();
1495 ChangeState( EventData::SELECTING );
1497 mEventData->mUpdateLeftSelectionPosition = true;
1498 mEventData->mUpdateRightSelectionPosition = true;
1499 mEventData->mUpdateGrabHandlePosition = true;
1500 mEventData->mUpdateHighlightBox = true;
1502 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1503 if( mEventData->mGrabHandlePopupEnabled )
1505 mEventData->mDecorator->SetPopupActive( false );
1511 // Handle normal cursor move
1512 ChangeState( EventData::EDITING );
1513 mEventData->mUpdateCursorPosition = true;
1516 mEventData->mUpdateInputStyle = true;
1517 mEventData->mScrollAfterUpdatePosition = true;
1520 void Controller::Impl::OnTapEvent( const Event& event )
1522 if( NULL != mEventData )
1524 const unsigned int tapCount = event.p1.mUint;
1526 if( 1u == tapCount )
1528 if( IsShowingRealText() )
1530 // Convert from control's coords to text's coords.
1531 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1532 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1534 // Keep the tap 'x' position. Used to move the cursor.
1535 mEventData->mCursorHookPositionX = xPosition;
1537 // Whether to touch point hits on a glyph.
1538 bool matchedCharacter = false;
1539 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1540 mModel->mLogicalModel,
1544 CharacterHitTest::TAP,
1547 // When the cursor position is changing, delay cursor blinking
1548 mEventData->mDecorator->DelayCursorBlink();
1552 mEventData->mPrimaryCursorPosition = 0u;
1555 // Update selection position after tapping
1556 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1557 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1559 mEventData->mUpdateCursorPosition = true;
1560 mEventData->mUpdateGrabHandlePosition = true;
1561 mEventData->mScrollAfterUpdatePosition = true;
1562 mEventData->mUpdateInputStyle = true;
1564 // Notify the cursor position to the InputMethodContext.
1565 if( mEventData->mInputMethodContext )
1567 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1568 mEventData->mInputMethodContext.NotifyCursorPosition();
1571 else if( 2u == tapCount )
1573 if( mEventData->mSelectionEnabled )
1575 // Convert from control's coords to text's coords.
1576 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1577 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1579 // Calculates the logical position from the x,y coords.
1580 RepositionSelectionHandles( xPosition,
1582 mEventData->mDoubleTapAction );
1588 void Controller::Impl::OnPanEvent( const Event& event )
1590 if( NULL == mEventData )
1592 // Nothing to do if there is no text input.
1596 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1597 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1599 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1601 // Nothing to do if scrolling is not enabled.
1605 const GestureState state = static_cast<GestureState>( event.p1.mInt );
1608 case GestureState::STARTED:
1610 // Will remove the cursor, handles or text's popup, ...
1611 ChangeState( EventData::TEXT_PANNING );
1614 case GestureState::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 GestureState::FINISHED:
1639 case GestureState::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::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
2046 if( nullptr == mEventData )
2048 // Nothing to do if there is no text.
2052 if( mEventData->mSelectionEnabled && (pStart || pEnd))
2054 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
2058 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
2062 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
2065 if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
2067 ChangeState( EventData::EDITING );
2068 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
2069 mEventData->mUpdateCursorPosition = true;
2073 ChangeState( EventData::SELECTING );
2074 mEventData->mUpdateHighlightBox = true;
2079 Uint32Pair Controller::Impl::GetTextSelectionRange() const
2085 range.first = mEventData->mLeftSelectionPosition;
2086 range.second = mEventData->mRightSelectionPosition;
2092 bool Controller::Impl::IsEditable() const
2094 return mEventData && mEventData->mEditingEnabled;
2097 void Controller::Impl::SetEditable( bool editable )
2101 mEventData->mEditingEnabled = editable;
2105 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2107 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2109 // Nothing to select if handles are in the same place.
2110 selectedText.clear();
2114 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2116 //Get start and end position of selection
2117 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2118 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2120 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2121 const Length numberOfCharacters = utf32Characters.Count();
2123 // Validate the start and end selection points
2124 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2126 //Get text as a UTF8 string
2127 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2129 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2131 // Keep a copy of the current input style.
2132 InputStyle currentInputStyle;
2133 currentInputStyle.Copy( mEventData->mInputStyle );
2135 // Set as input style the style of the first deleted character.
2136 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2138 // Compare if the input style has changed.
2139 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2141 if( hasInputStyleChanged )
2143 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2144 // Queue the input style changed signal.
2145 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2148 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2150 // Mark the paragraphs to be updated.
2151 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2153 mTextUpdateInfo.mCharacterIndex = 0;
2154 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2155 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2156 mTextUpdateInfo.mClearAll = true;
2160 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2161 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2164 // Delete text between handles
2165 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2166 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2167 utf32Characters.Erase( first, last );
2169 // Will show the cursor at the first character of the selection.
2170 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2174 // Will show the cursor at the last character of the selection.
2175 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2178 mEventData->mDecoratorUpdated = true;
2182 void Controller::Impl::ShowClipboard()
2186 mClipboard.ShowClipboard();
2190 void Controller::Impl::HideClipboard()
2192 if( mClipboard && mClipboardHideEnabled )
2194 mClipboard.HideClipboard();
2198 void Controller::Impl::SetClipboardHideEnable(bool enable)
2200 mClipboardHideEnabled = enable;
2203 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2205 //Send string to clipboard
2206 return ( mClipboard && mClipboard.SetItem( source ) );
2209 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2211 std::string selectedText;
2212 RetrieveSelection( selectedText, deleteAfterSending );
2213 CopyStringToClipboard( selectedText );
2214 ChangeState( EventData::EDITING );
2217 void Controller::Impl::RequestGetTextFromClipboard()
2221 mClipboard.RequestItem();
2225 void Controller::Impl::RepositionSelectionHandles()
2227 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2228 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2230 if( selectionStart == selectionEnd )
2232 // Nothing to select if handles are in the same place.
2233 // So, deactive Highlight box.
2234 mEventData->mDecorator->SetHighlightActive( false );
2238 mEventData->mDecorator->ClearHighlights();
2240 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2241 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2242 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2243 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2244 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2245 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2246 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2248 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2249 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2250 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2252 // Swap the indices if the start is greater than the end.
2253 const bool indicesSwapped = selectionStart > selectionEnd;
2255 // Tell the decorator to flip the selection handles if needed.
2256 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2258 if( indicesSwapped )
2260 std::swap( selectionStart, selectionEnd );
2263 // Get the indices to the first and last selected glyphs.
2264 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2265 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2266 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2267 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2269 // Get the lines where the glyphs are laid-out.
2270 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2272 LineIndex lineIndex = 0u;
2273 Length numberOfLines = 0u;
2274 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2275 1u + glyphEnd - glyphStart,
2278 const LineIndex firstLineIndex = lineIndex;
2280 // Create the structure to store some selection box info.
2281 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2282 selectionBoxLinesInfo.Resize( numberOfLines );
2284 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2285 selectionBoxInfo->minX = MAX_FLOAT;
2286 selectionBoxInfo->maxX = MIN_FLOAT;
2288 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2289 float minHighlightX = std::numeric_limits<float>::max();
2290 float maxHighlightX = std::numeric_limits<float>::min();
2292 Vector2 highLightPosition; // The highlight position in decorator's coords.
2294 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2296 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2297 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2300 // Transform to decorator's (control) coords.
2301 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2303 lineRun += firstLineIndex;
2305 // The line height is the addition of the line ascender and the line descender.
2306 // However, the line descender has a negative value, hence the subtraction.
2307 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2309 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2311 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2312 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2313 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2315 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2316 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2317 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2319 // The number of quads of the selection box.
2320 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2321 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2323 // Count the actual number of quads.
2324 unsigned int actualNumberOfQuads = 0u;
2327 // Traverse the glyphs.
2328 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2330 const GlyphInfo& glyph = *( glyphsBuffer + index );
2331 const Vector2& position = *( positionsBuffer + index );
2333 if( splitStartGlyph )
2335 // 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.
2337 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2338 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2339 // Get the direction of the character.
2340 CharacterDirection isCurrentRightToLeft = false;
2341 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2343 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2346 // The end point could be in the middle of the ligature.
2347 // Calculate the number of characters selected.
2348 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2350 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2351 quad.y = selectionBoxInfo->lineOffset;
2352 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2353 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2355 // Store the min and max 'x' for each line.
2356 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2357 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2359 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2360 ++actualNumberOfQuads;
2362 splitStartGlyph = false;
2366 if( splitEndGlyph && ( index == glyphEnd ) )
2368 // 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.
2370 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2371 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2372 // Get the direction of the character.
2373 CharacterDirection isCurrentRightToLeft = false;
2374 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2376 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2379 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2381 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2382 quad.y = selectionBoxInfo->lineOffset;
2383 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2384 quad.w = quad.y + selectionBoxInfo->lineHeight;
2386 // Store the min and max 'x' for each line.
2387 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2388 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2390 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2392 ++actualNumberOfQuads;
2394 splitEndGlyph = false;
2398 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2399 quad.y = selectionBoxInfo->lineOffset;
2400 quad.z = quad.x + glyph.advance;
2401 quad.w = quad.y + selectionBoxInfo->lineHeight;
2403 // Store the min and max 'x' for each line.
2404 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2405 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2407 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2409 ++actualNumberOfQuads;
2411 // Whether to retrieve the next line.
2412 if( index == lastGlyphOfLine )
2415 if( lineIndex < firstLineIndex + numberOfLines )
2417 // Retrieve the next line.
2420 // Get the last glyph of the new line.
2421 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2423 // Keep the offset and height of the current selection box.
2424 const float currentLineOffset = selectionBoxInfo->lineOffset;
2425 const float currentLineHeight = selectionBoxInfo->lineHeight;
2427 // Get the selection box info for the next line.
2430 selectionBoxInfo->minX = MAX_FLOAT;
2431 selectionBoxInfo->maxX = MIN_FLOAT;
2433 // Update the line's vertical offset.
2434 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2436 // The line height is the addition of the line ascender and the line descender.
2437 // However, the line descender has a negative value, hence the subtraction.
2438 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2443 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2444 // The final width is calculated after 'boxifying' the selection.
2445 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2446 endIt = selectionBoxLinesInfo.End();
2450 const SelectionBoxInfo& info = *it;
2452 // Update the size of the highlighted text.
2453 highLightSize.height += info.lineHeight;
2454 minHighlightX = std::min( minHighlightX, info.minX );
2455 maxHighlightX = std::max( maxHighlightX, info.maxX );
2458 // Add extra geometry to 'boxify' the selection.
2460 if( 1u < numberOfLines )
2462 // Boxify the first line.
2463 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2464 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2466 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2467 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2472 quad.y = firstSelectionBoxLineInfo.lineOffset;
2473 quad.z = firstSelectionBoxLineInfo.minX;
2474 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2476 // Boxify at the beginning of the line.
2477 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2479 ++actualNumberOfQuads;
2481 // Update the size of the highlighted text.
2482 minHighlightX = 0.f;
2487 quad.x = firstSelectionBoxLineInfo.maxX;
2488 quad.y = firstSelectionBoxLineInfo.lineOffset;
2489 quad.z = mModel->mVisualModel->mControlSize.width;
2490 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2492 // Boxify at the end of the line.
2493 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2495 ++actualNumberOfQuads;
2497 // Update the size of the highlighted text.
2498 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2501 // Boxify the central lines.
2502 if( 2u < numberOfLines )
2504 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2505 endIt = selectionBoxLinesInfo.End() - 1u;
2509 const SelectionBoxInfo& info = *it;
2512 quad.y = info.lineOffset;
2514 quad.w = info.lineOffset + info.lineHeight;
2516 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2518 ++actualNumberOfQuads;
2521 quad.y = info.lineOffset;
2522 quad.z = mModel->mVisualModel->mControlSize.width;
2523 quad.w = info.lineOffset + info.lineHeight;
2525 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2527 ++actualNumberOfQuads;
2530 // Update the size of the highlighted text.
2531 minHighlightX = 0.f;
2532 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2535 // Boxify the last line.
2536 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2537 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2539 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2540 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2545 quad.y = lastSelectionBoxLineInfo.lineOffset;
2546 quad.z = lastSelectionBoxLineInfo.minX;
2547 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2549 // Boxify at the beginning of the line.
2550 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2552 ++actualNumberOfQuads;
2554 // Update the size of the highlighted text.
2555 minHighlightX = 0.f;
2560 quad.x = lastSelectionBoxLineInfo.maxX;
2561 quad.y = lastSelectionBoxLineInfo.lineOffset;
2562 quad.z = mModel->mVisualModel->mControlSize.width;
2563 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2565 // Boxify at the end of the line.
2566 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2568 ++actualNumberOfQuads;
2570 // Update the size of the highlighted text.
2571 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2575 // Set the actual number of quads.
2576 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2578 // Sets the highlight's size and position. In decorator's coords.
2579 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2580 highLightSize.width = maxHighlightX - minHighlightX;
2582 highLightPosition.x = minHighlightX;
2583 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2584 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2586 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2588 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2590 CursorInfo primaryCursorInfo;
2591 GetCursorPosition( mEventData->mLeftSelectionPosition,
2592 primaryCursorInfo );
2594 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2596 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2598 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2599 primaryCursorInfo.lineHeight );
2601 CursorInfo secondaryCursorInfo;
2602 GetCursorPosition( mEventData->mRightSelectionPosition,
2603 secondaryCursorInfo );
2605 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2607 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2608 secondaryPosition.x,
2609 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2610 secondaryCursorInfo.lineHeight );
2613 // Set the flag to update the decorator.
2614 mEventData->mDecoratorUpdated = true;
2617 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2619 if( NULL == mEventData )
2621 // Nothing to do if there is no text input.
2625 if( IsShowingPlaceholderText() )
2627 // Nothing to do if there is the place-holder text.
2631 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2632 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2633 if( ( 0 == numberOfGlyphs ) ||
2634 ( 0 == numberOfLines ) )
2636 // Nothing to do if there is no text.
2640 // Find which word was selected
2641 CharacterIndex selectionStart( 0 );
2642 CharacterIndex selectionEnd( 0 );
2643 CharacterIndex noTextHitIndex( 0 );
2644 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2645 mModel->mLogicalModel,
2652 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2654 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2656 ChangeState( EventData::SELECTING );
2658 mEventData->mLeftSelectionPosition = selectionStart;
2659 mEventData->mRightSelectionPosition = selectionEnd;
2661 mEventData->mUpdateLeftSelectionPosition = true;
2662 mEventData->mUpdateRightSelectionPosition = true;
2663 mEventData->mUpdateHighlightBox = true;
2665 // It may happen an InputMethodContext commit event arrives before the selection event
2666 // if the InputMethodContext is in pre-edit state. The commit event will set the
2667 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2668 // to false, the highlight box won't be updated.
2669 mEventData->mUpdateCursorPosition = false;
2671 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2673 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2674 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2676 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2678 // Nothing to select. i.e. a white space, out of bounds
2679 ChangeState( EventData::EDITING_WITH_POPUP );
2681 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2683 mEventData->mUpdateCursorPosition = true;
2684 mEventData->mUpdateGrabHandlePosition = true;
2685 mEventData->mScrollAfterUpdatePosition = true;
2686 mEventData->mUpdateInputStyle = true;
2688 else if( Controller::NoTextTap::NO_ACTION == action )
2690 // Nothing to select. i.e. a white space, out of bounds
2691 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2693 mEventData->mUpdateCursorPosition = true;
2694 mEventData->mUpdateGrabHandlePosition = true;
2695 mEventData->mScrollAfterUpdatePosition = true;
2696 mEventData->mUpdateInputStyle = true;
2700 void Controller::Impl::SetPopupButtons()
2703 * Sets the Popup buttons to be shown depending on State.
2705 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2707 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2710 bool isEditable = IsEditable();
2711 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2713 if( EventData::SELECTING == mEventData->mState )
2715 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
2718 buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
2721 if( !IsClipboardEmpty() )
2725 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2727 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2730 if( !mEventData->mAllTextSelected )
2732 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2735 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2737 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2739 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2742 if( !IsClipboardEmpty() )
2746 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2748 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2751 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2753 if ( !IsClipboardEmpty() )
2757 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2759 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2763 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2766 void Controller::Impl::ChangeState( EventData::State newState )
2768 if( NULL == mEventData )
2770 // Nothing to do if there is no text input.
2774 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2776 if( mEventData->mState != newState )
2778 mEventData->mPreviousState = mEventData->mState;
2779 mEventData->mState = newState;
2781 switch( mEventData->mState )
2783 case EventData::INACTIVE:
2785 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2786 mEventData->mDecorator->StopCursorBlink();
2787 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2788 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2789 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2790 mEventData->mDecorator->SetHighlightActive( false );
2791 mEventData->mDecorator->SetPopupActive( false );
2792 mEventData->mDecoratorUpdated = true;
2795 case EventData::INTERRUPTED:
2797 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2798 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2799 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2800 mEventData->mDecorator->SetHighlightActive( false );
2801 mEventData->mDecorator->SetPopupActive( false );
2802 mEventData->mDecoratorUpdated = true;
2805 case EventData::SELECTING:
2807 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2808 mEventData->mDecorator->StopCursorBlink();
2809 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2810 if ( mEventData->mGrabHandleEnabled )
2812 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2813 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2815 mEventData->mDecorator->SetHighlightActive( true );
2816 if( mEventData->mGrabHandlePopupEnabled )
2819 mEventData->mDecorator->SetPopupActive( true );
2821 mEventData->mDecoratorUpdated = true;
2824 case EventData::EDITING:
2826 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2827 if( mEventData->mCursorBlinkEnabled )
2829 mEventData->mDecorator->StartCursorBlink();
2831 // Grab handle is not shown until a tap is received whilst EDITING
2832 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2833 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2834 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2835 mEventData->mDecorator->SetHighlightActive( false );
2836 if( mEventData->mGrabHandlePopupEnabled )
2838 mEventData->mDecorator->SetPopupActive( false );
2840 mEventData->mDecoratorUpdated = true;
2843 case EventData::EDITING_WITH_POPUP:
2845 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2847 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2848 if( mEventData->mCursorBlinkEnabled )
2850 mEventData->mDecorator->StartCursorBlink();
2852 if( mEventData->mSelectionEnabled )
2854 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2855 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2856 mEventData->mDecorator->SetHighlightActive( false );
2858 else if ( mEventData->mGrabHandleEnabled )
2860 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2862 if( mEventData->mGrabHandlePopupEnabled )
2865 mEventData->mDecorator->SetPopupActive( true );
2867 mEventData->mDecoratorUpdated = true;
2870 case EventData::EDITING_WITH_GRAB_HANDLE:
2872 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2874 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2875 if( mEventData->mCursorBlinkEnabled )
2877 mEventData->mDecorator->StartCursorBlink();
2879 // Grab handle is not shown until a tap is received whilst EDITING
2880 if ( mEventData->mGrabHandleEnabled )
2882 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2884 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2885 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2886 mEventData->mDecorator->SetHighlightActive( false );
2887 if( mEventData->mGrabHandlePopupEnabled )
2889 mEventData->mDecorator->SetPopupActive( false );
2891 mEventData->mDecoratorUpdated = true;
2894 case EventData::SELECTION_HANDLE_PANNING:
2896 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2897 mEventData->mDecorator->StopCursorBlink();
2898 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2899 if ( mEventData->mGrabHandleEnabled )
2901 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2902 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2904 mEventData->mDecorator->SetHighlightActive( true );
2905 if( mEventData->mGrabHandlePopupEnabled )
2907 mEventData->mDecorator->SetPopupActive( false );
2909 mEventData->mDecoratorUpdated = true;
2912 case EventData::GRAB_HANDLE_PANNING:
2914 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2916 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2917 if( mEventData->mCursorBlinkEnabled )
2919 mEventData->mDecorator->StartCursorBlink();
2921 if ( mEventData->mGrabHandleEnabled )
2923 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2925 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2926 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2927 mEventData->mDecorator->SetHighlightActive( false );
2928 if( mEventData->mGrabHandlePopupEnabled )
2930 mEventData->mDecorator->SetPopupActive( false );
2932 mEventData->mDecoratorUpdated = true;
2935 case EventData::EDITING_WITH_PASTE_POPUP:
2937 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2939 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2940 if( mEventData->mCursorBlinkEnabled )
2942 mEventData->mDecorator->StartCursorBlink();
2945 if ( mEventData->mGrabHandleEnabled )
2947 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2949 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2950 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2951 mEventData->mDecorator->SetHighlightActive( false );
2953 if( mEventData->mGrabHandlePopupEnabled )
2956 mEventData->mDecorator->SetPopupActive( true );
2958 mEventData->mDecoratorUpdated = true;
2961 case EventData::TEXT_PANNING:
2963 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2964 mEventData->mDecorator->StopCursorBlink();
2965 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2966 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2967 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2969 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2970 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2971 mEventData->mDecorator->SetHighlightActive( true );
2974 if( mEventData->mGrabHandlePopupEnabled )
2976 mEventData->mDecorator->SetPopupActive( false );
2979 mEventData->mDecoratorUpdated = true;
2986 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2987 CursorInfo& cursorInfo )
2989 if( !IsShowingRealText() )
2991 // Do not want to use the place-holder text to set the cursor position.
2993 // Use the line's height of the font's family set to set the cursor's size.
2994 // If there is no font's family set, use the default font.
2995 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2997 cursorInfo.lineOffset = 0.f;
2998 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2999 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
3002 if( mModel->mMatchSystemLanguageDirection )
3004 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
3007 switch( mModel->mHorizontalAlignment )
3009 case Text::HorizontalAlignment::BEGIN :
3013 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
3017 cursorInfo.primaryPosition.x = 0.f;
3021 case Text::HorizontalAlignment::CENTER:
3023 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
3026 case Text::HorizontalAlignment::END:
3030 cursorInfo.primaryPosition.x = 0.f;
3034 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
3040 // Nothing else to do.
3044 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
3045 GetCursorPositionParameters parameters;
3046 parameters.visualModel = mModel->mVisualModel;
3047 parameters.logicalModel = mModel->mLogicalModel;
3048 parameters.metrics = mMetrics;
3049 parameters.logical = logical;
3050 parameters.isMultiline = isMultiLine;
3052 Text::GetCursorPosition( parameters,
3055 // Adds Outline offset.
3056 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
3057 cursorInfo.primaryPosition.x += outlineWidth;
3058 cursorInfo.primaryPosition.y += outlineWidth;
3059 cursorInfo.secondaryPosition.x += outlineWidth;
3060 cursorInfo.secondaryPosition.y += outlineWidth;
3064 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
3066 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
3067 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
3069 if( 0.f > cursorInfo.primaryPosition.x )
3071 cursorInfo.primaryPosition.x = 0.f;
3074 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
3075 if( cursorInfo.primaryPosition.x > edgeWidth )
3077 cursorInfo.primaryPosition.x = edgeWidth;
3082 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
3084 if( NULL == mEventData )
3086 // Nothing to do if there is no text input.
3090 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
3092 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
3093 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
3095 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
3096 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3098 if( numberOfCharacters > 1u )
3100 const Script script = mModel->mLogicalModel->GetScript( index );
3101 if( HasLigatureMustBreak( script ) )
3103 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3104 numberOfCharacters = 1u;
3109 while( 0u == numberOfCharacters )
3112 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3116 if( index < mEventData->mPrimaryCursorPosition )
3118 cursorIndex -= numberOfCharacters;
3122 cursorIndex += numberOfCharacters;
3125 // Will update the cursor hook position.
3126 mEventData->mUpdateCursorHookPosition = true;
3131 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3133 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3134 if( NULL == mEventData )
3136 // Nothing to do if there is no text input.
3137 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3141 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3143 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3145 // Sets the cursor position.
3146 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3149 cursorInfo.primaryCursorHeight,
3150 cursorInfo.lineHeight );
3151 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3153 if( mEventData->mUpdateGrabHandlePosition )
3155 // Sets the grab handle position.
3156 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3158 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3159 cursorInfo.lineHeight );
3162 if( cursorInfo.isSecondaryCursor )
3164 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3165 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3166 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3167 cursorInfo.secondaryCursorHeight,
3168 cursorInfo.lineHeight );
3169 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3172 // Set which cursors are active according the state.
3173 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3175 if( cursorInfo.isSecondaryCursor )
3177 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3181 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3186 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3189 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3192 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3193 const CursorInfo& cursorInfo )
3195 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3196 ( RIGHT_SELECTION_HANDLE != handleType ) )
3201 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3203 // Sets the handle's position.
3204 mEventData->mDecorator->SetPosition( handleType,
3206 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3207 cursorInfo.lineHeight );
3209 // If selection handle at start of the text and other at end of the text then all text is selected.
3210 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3211 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3212 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3215 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3217 // Clamp between -space & -alignment offset.
3219 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3221 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3222 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3223 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3225 mEventData->mDecoratorUpdated = true;
3229 mModel->mScrollPosition.x = 0.f;
3233 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3235 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3237 // Nothing to do if the text is single line.
3241 // Clamp between -space & 0.
3242 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3244 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3245 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3246 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3248 mEventData->mDecoratorUpdated = true;
3252 mModel->mScrollPosition.y = 0.f;
3256 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3258 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3260 // position is in actor's coords.
3261 const float positionEndX = position.x + cursorWidth;
3262 const float positionEndY = position.y + lineHeight;
3264 // Transform the position to decorator coords.
3265 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3266 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3268 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3269 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3271 if( decoratorPositionBeginX < 0.f )
3273 mModel->mScrollPosition.x = -position.x;
3275 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3277 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3280 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3282 if( decoratorPositionBeginY < 0.f )
3284 mModel->mScrollPosition.y = -position.y;
3286 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3288 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3293 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3295 // Get the current cursor position in decorator coords.
3296 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3298 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3302 // Calculate the offset to match the cursor position before the character was deleted.
3303 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3305 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3306 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3308 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3309 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3313 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3314 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3316 // Makes the new cursor position visible if needed.
3317 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3320 void Controller::Impl::RequestRelayout()
3322 if( NULL != mControlInterface )
3324 mControlInterface->RequestTextRelayout();
3328 Actor Controller::Impl::CreateBackgroundActor()
3330 // NOTE: Currently we only support background color for one line left-to-right text,
3331 // so the following calculation is based on one line left-to-right text only!
3335 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3336 if( numberOfGlyphs > 0u )
3338 Vector<GlyphInfo> glyphs;
3339 glyphs.Resize( numberOfGlyphs );
3341 Vector<Vector2> positions;
3342 positions.Resize( numberOfGlyphs );
3344 // Get the line where the glyphs are laid-out.
3345 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3346 float alignmentOffset = lineRun->alignmentOffset;
3347 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3353 glyphs.Resize( numberOfGlyphs );
3354 positions.Resize( numberOfGlyphs );
3356 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3357 const Vector2* const positionsBuffer = positions.Begin();
3359 BackgroundMesh mesh;
3360 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3361 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3363 const Vector2 textSize = mView.GetLayoutSize();
3365 const float offsetX = textSize.width * 0.5f;
3366 const float offsetY = textSize.height * 0.5f;
3368 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3369 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3370 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3373 uint32_t numberOfQuads = 0u;
3375 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3377 const GlyphInfo& glyph = *( glyphsBuffer + i );
3379 // Get the background color of the character.
3380 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3381 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3382 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3384 // Only create quads for glyphs with a background color
3385 if ( backgroundColor != Color::TRANSPARENT )
3387 const Vector2 position = *( positionsBuffer + i );
3389 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3391 quad.x = position.x;
3393 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3394 quad.w = textSize.height;
3396 else if ( i == 0u ) // The first glyph in the whole text
3398 quad.x = position.x;
3400 quad.z = quad.x - glyph.xBearing + glyph.advance;
3401 quad.w = textSize.height;
3403 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3405 quad.x = position.x - glyph.xBearing;
3407 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3408 quad.w = textSize.height;
3410 else // The glyph in the middle of the text
3412 quad.x = position.x - glyph.xBearing;
3414 quad.z = quad.x + glyph.advance;
3415 quad.w = textSize.height;
3418 BackgroundVertex vertex;
3421 vertex.mPosition.x = quad.x - offsetX;
3422 vertex.mPosition.y = quad.y - offsetY;
3423 vertex.mColor = backgroundColor;
3424 mesh.mVertices.PushBack( vertex );
3427 vertex.mPosition.x = quad.z - offsetX;
3428 vertex.mPosition.y = quad.y - offsetY;
3429 vertex.mColor = backgroundColor;
3430 mesh.mVertices.PushBack( vertex );
3433 vertex.mPosition.x = quad.x - offsetX;
3434 vertex.mPosition.y = quad.w - offsetY;
3435 vertex.mColor = backgroundColor;
3436 mesh.mVertices.PushBack( vertex );
3439 vertex.mPosition.x = quad.z - offsetX;
3440 vertex.mPosition.y = quad.w - offsetY;
3441 vertex.mColor = backgroundColor;
3442 mesh.mVertices.PushBack( vertex );
3444 // Six indices in counter clockwise winding
3445 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3446 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3447 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3448 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3449 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3450 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3456 // Only create the background actor if there are glyphs with background color
3457 if ( mesh.mVertices.Count() > 0u )
3459 Property::Map quadVertexFormat;
3460 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3461 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3463 VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
3464 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3466 Geometry quadGeometry = Geometry::New();
3467 quadGeometry.AddVertexBuffer( quadVertices );
3468 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3470 if( !mShaderBackground )
3472 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3475 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3476 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3477 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3479 actor = Actor::New();
3480 actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
3481 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
3482 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
3483 actor.SetProperty( Actor::Property::SIZE, textSize );
3484 actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
3485 actor.AddRenderer( renderer );
3494 } // namespace Toolkit