2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
29 #include <dali-toolkit/internal/text/bidirectional-support.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/color-segmentation.h>
32 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
45 * @brief Struct used to calculate the selection box.
47 struct SelectionBoxInfo
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
59 const float MAX_FLOAT = std::numeric_limits<float>::max();
60 const float MIN_FLOAT = std::numeric_limits<float>::min();
61 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
63 #define MAKE_SHADER(A)#A
65 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
66 attribute mediump vec2 aPosition;
67 attribute mediump vec4 aColor;
68 varying mediump vec4 vColor;
69 uniform highp mat4 uMvpMatrix;
73 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
74 gl_Position = uMvpMatrix * position;
79 const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
80 varying mediump vec4 vColor;
81 uniform lowp vec4 uColor;
85 gl_FragColor = vColor * uColor;
89 struct BackgroundVertex
91 Vector2 mPosition; ///< Vertex posiiton
92 Vector4 mColor; ///< Vertex color
97 Vector< BackgroundVertex > mVertices; ///< container of vertices
98 Vector< unsigned short > mIndices; ///< container of indices
101 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
102 const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
103 const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
104 const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
105 const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
118 EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
119 : mDecorator( decorator ),
120 mInputMethodContext( inputMethodContext ),
121 mPlaceholderFont( NULL ),
122 mPlaceholderTextActive(),
123 mPlaceholderTextInactive(),
124 mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
126 mInputStyleChangedQueue(),
127 mPreviousState( INACTIVE ),
129 mPrimaryCursorPosition( 0u ),
130 mLeftSelectionPosition( 0u ),
131 mRightSelectionPosition( 0u ),
132 mPreEditStartPosition( 0u ),
133 mPreEditLength( 0u ),
134 mCursorHookPositionX( 0.f ),
135 mDoubleTapAction( Controller::NoTextTap::NO_ACTION ),
136 mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ),
137 mIsShowingPlaceholderText( false ),
138 mPreEditFlag( false ),
139 mDecoratorUpdated( false ),
140 mCursorBlinkEnabled( true ),
141 mGrabHandleEnabled( true ),
142 mGrabHandlePopupEnabled( true ),
143 mSelectionEnabled( true ),
144 mUpdateCursorHookPosition( false ),
145 mUpdateCursorPosition( false ),
146 mUpdateGrabHandlePosition( false ),
147 mUpdateLeftSelectionPosition( false ),
148 mUpdateRightSelectionPosition( false ),
149 mIsLeftHandleSelected( false ),
150 mIsRightHandleSelected( false ),
151 mUpdateHighlightBox( false ),
152 mScrollAfterUpdatePosition( false ),
153 mScrollAfterDelete( false ),
154 mAllTextSelected( false ),
155 mUpdateInputStyle( false ),
156 mPasswordInput( false ),
157 mCheckScrollAmount( false ),
158 mIsPlaceholderPixelSize( false ),
159 mIsPlaceholderElideEnabled( false ),
160 mPlaceholderEllipsisFlag( false ),
161 mShiftSelectionFlag( true ),
162 mUpdateAlignment( false )
166 EventData::~EventData()
169 bool Controller::Impl::ProcessInputEvents()
171 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
172 if( NULL == mEventData )
174 // Nothing to do if there is no text input.
175 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
179 if( mEventData->mDecorator )
181 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
182 iter != mEventData->mEventQueue.end();
187 case Event::CURSOR_KEY_EVENT:
189 OnCursorKeyEvent( *iter );
192 case Event::TAP_EVENT:
197 case Event::LONG_PRESS_EVENT:
199 OnLongPressEvent( *iter );
202 case Event::PAN_EVENT:
207 case Event::GRAB_HANDLE_EVENT:
208 case Event::LEFT_SELECTION_HANDLE_EVENT:
209 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
211 OnHandleEvent( *iter );
216 OnSelectEvent( *iter );
219 case Event::SELECT_ALL:
228 if( mEventData->mUpdateCursorPosition ||
229 mEventData->mUpdateHighlightBox )
231 NotifyInputMethodContext();
234 // The cursor must also be repositioned after inserts into the model
235 if( mEventData->mUpdateCursorPosition )
237 // Updates the cursor position and scrolls the text to make it visible.
238 CursorInfo cursorInfo;
239 // Calculate the cursor position from the new cursor index.
240 GetCursorPosition( mEventData->mPrimaryCursorPosition,
243 if( mEventData->mUpdateCursorHookPosition )
245 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
246 mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
247 mEventData->mUpdateCursorHookPosition = false;
250 // Scroll first the text after delete ...
251 if( mEventData->mScrollAfterDelete )
253 ScrollTextToMatchCursor( cursorInfo );
256 // ... then, text can be scrolled to make the cursor visible.
257 if( mEventData->mScrollAfterUpdatePosition )
259 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
260 ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
262 mEventData->mScrollAfterUpdatePosition = false;
263 mEventData->mScrollAfterDelete = false;
265 UpdateCursorPosition( cursorInfo );
267 mEventData->mDecoratorUpdated = true;
268 mEventData->mUpdateCursorPosition = false;
269 mEventData->mUpdateGrabHandlePosition = false;
273 CursorInfo leftHandleInfo;
274 CursorInfo rightHandleInfo;
276 if( mEventData->mUpdateHighlightBox )
278 GetCursorPosition( mEventData->mLeftSelectionPosition,
281 GetCursorPosition( mEventData->mRightSelectionPosition,
284 if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
286 if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
288 CursorInfo& infoLeft = leftHandleInfo;
290 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
291 ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
293 CursorInfo& infoRight = rightHandleInfo;
295 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
296 ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
300 CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
302 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
303 ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
308 if( mEventData->mUpdateLeftSelectionPosition )
310 UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
314 mEventData->mDecoratorUpdated = true;
315 mEventData->mUpdateLeftSelectionPosition = false;
318 if( mEventData->mUpdateRightSelectionPosition )
320 UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
324 mEventData->mDecoratorUpdated = true;
325 mEventData->mUpdateRightSelectionPosition = false;
328 if( mEventData->mUpdateHighlightBox )
330 RepositionSelectionHandles();
332 mEventData->mUpdateLeftSelectionPosition = false;
333 mEventData->mUpdateRightSelectionPosition = false;
334 mEventData->mUpdateHighlightBox = false;
335 mEventData->mIsLeftHandleSelected = false;
336 mEventData->mIsRightHandleSelected = false;
339 mEventData->mScrollAfterUpdatePosition = false;
342 if( mEventData->mUpdateInputStyle )
344 // Keep a copy of the current input style.
345 InputStyle currentInputStyle;
346 currentInputStyle.Copy( mEventData->mInputStyle );
348 // Set the default style first.
349 RetrieveDefaultInputStyle( mEventData->mInputStyle );
351 // Get the character index from the cursor index.
352 const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
354 // Retrieve the style from the style runs stored in the logical model.
355 mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
357 // Compare if the input style has changed.
358 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
360 if( hasInputStyleChanged )
362 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
363 // Queue the input style changed signal.
364 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
367 mEventData->mUpdateInputStyle = false;
370 mEventData->mEventQueue.clear();
372 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
374 const bool decoratorUpdated = mEventData->mDecoratorUpdated;
375 mEventData->mDecoratorUpdated = false;
377 return decoratorUpdated;
380 void Controller::Impl::NotifyInputMethodContext()
382 if( mEventData && mEventData->mInputMethodContext )
384 CharacterIndex cursorPosition = GetLogicalCursorPosition();
386 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
388 // Update the cursor position by removing the initial white spaces.
389 if( cursorPosition < numberOfWhiteSpaces )
395 cursorPosition -= numberOfWhiteSpaces;
398 mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
399 mEventData->mInputMethodContext.NotifyCursorPosition();
403 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
405 if ( mEventData && mEventData->mInputMethodContext )
407 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
408 mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
412 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
414 CharacterIndex cursorPosition = 0u;
418 if( ( EventData::SELECTING == mEventData->mState ) ||
419 ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
421 cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
425 cursorPosition = mEventData->mPrimaryCursorPosition;
429 return cursorPosition;
432 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
434 Length numberOfWhiteSpaces = 0u;
436 // Get the buffer to the text.
437 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
439 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
440 for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
442 if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
448 return numberOfWhiteSpaces;
451 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
453 // Get the total number of characters.
454 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
456 // Retrieve the text.
457 if( 0u != numberOfCharacters )
459 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
463 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
465 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
466 mTextUpdateInfo.mStartGlyphIndex = 0u;
467 mTextUpdateInfo.mStartLineIndex = 0u;
468 numberOfCharacters = 0u;
470 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
471 if( 0u == numberOfParagraphs )
473 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
474 numberOfCharacters = 0u;
476 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
478 // Nothing else to do if there are no paragraphs.
482 // Find the paragraphs to be updated.
483 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
484 if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
486 // Text is being added at the end of the current text.
487 if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
489 // Text is being added in a new paragraph after the last character of the text.
490 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
491 numberOfCharacters = 0u;
492 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
494 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
495 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
497 // Nothing else to do;
501 paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
505 Length numberOfCharactersToUpdate = 0u;
506 if( mTextUpdateInfo.mFullRelayoutNeeded )
508 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
512 numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
514 mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
515 numberOfCharactersToUpdate,
516 paragraphsToBeUpdated );
519 if( 0u != paragraphsToBeUpdated.Count() )
521 const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
522 const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
523 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
525 ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
526 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
528 if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed.
529 ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph.
530 ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
531 ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
533 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
534 const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
536 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
540 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
544 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
545 mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
548 void Controller::Impl::ClearFullModelData( OperationsMask operations )
550 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
552 mModel->mLogicalModel->mLineBreakInfo.Clear();
553 mModel->mLogicalModel->mParagraphInfo.Clear();
556 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
558 mModel->mLogicalModel->mScriptRuns.Clear();
561 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
563 mModel->mLogicalModel->mFontRuns.Clear();
566 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
568 if( NO_OPERATION != ( BIDI_INFO & operations ) )
570 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
571 mModel->mLogicalModel->mCharacterDirections.Clear();
574 if( NO_OPERATION != ( REORDER & operations ) )
576 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
577 for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
578 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
582 BidirectionalLineInfoRun& bidiLineInfo = *it;
584 free( bidiLineInfo.visualToLogicalMap );
585 bidiLineInfo.visualToLogicalMap = NULL;
587 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
591 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
593 mModel->mVisualModel->mGlyphs.Clear();
594 mModel->mVisualModel->mGlyphsToCharacters.Clear();
595 mModel->mVisualModel->mCharactersToGlyph.Clear();
596 mModel->mVisualModel->mCharactersPerGlyph.Clear();
597 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
598 mModel->mVisualModel->mGlyphPositions.Clear();
601 if( NO_OPERATION != ( LAYOUT & operations ) )
603 mModel->mVisualModel->mLines.Clear();
606 if( NO_OPERATION != ( COLOR & operations ) )
608 mModel->mVisualModel->mColorIndices.Clear();
609 mModel->mVisualModel->mBackgroundColorIndices.Clear();
613 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
615 const CharacterIndex endIndexPlusOne = endIndex + 1u;
617 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
619 // Clear the line break info.
620 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
622 mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
623 lineBreakInfoBuffer + endIndexPlusOne );
625 // Clear the paragraphs.
626 ClearCharacterRuns( startIndex,
628 mModel->mLogicalModel->mParagraphInfo );
631 if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
633 // Clear the scripts.
634 ClearCharacterRuns( startIndex,
636 mModel->mLogicalModel->mScriptRuns );
639 if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
642 ClearCharacterRuns( startIndex,
644 mModel->mLogicalModel->mFontRuns );
647 if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
649 if( NO_OPERATION != ( BIDI_INFO & operations ) )
651 // Clear the bidirectional paragraph info.
652 ClearCharacterRuns( startIndex,
654 mModel->mLogicalModel->mBidirectionalParagraphInfo );
656 // Clear the character's directions.
657 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
659 mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
660 characterDirectionsBuffer + endIndexPlusOne );
663 if( NO_OPERATION != ( REORDER & operations ) )
665 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
666 uint32_t endRemoveIndex = startRemoveIndex;
667 ClearCharacterRuns( startIndex,
669 mModel->mLogicalModel->mBidirectionalLineInfo,
673 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
675 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
676 for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
677 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
681 BidirectionalLineInfoRun& bidiLineInfo = *it;
683 free( bidiLineInfo.visualToLogicalMap );
684 bidiLineInfo.visualToLogicalMap = NULL;
687 mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
688 bidirectionalLineInfoBuffer + endRemoveIndex );
693 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
695 const CharacterIndex endIndexPlusOne = endIndex + 1u;
696 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
698 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
699 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
700 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
702 const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
703 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
705 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
707 // Update the character to glyph indices.
708 for( Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
709 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
713 CharacterIndex& index = *it;
714 index -= numberOfGlyphsRemoved;
717 // Clear the character to glyph conversion table.
718 mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
719 charactersToGlyphBuffer + endIndexPlusOne );
721 // Clear the glyphs per character table.
722 mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
723 glyphsPerCharacterBuffer + endIndexPlusOne );
725 // Clear the glyphs buffer.
726 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
727 mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
728 glyphsBuffer + endGlyphIndexPlusOne );
730 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
732 // Update the glyph to character indices.
733 for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
734 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
738 CharacterIndex& index = *it;
739 index -= numberOfCharactersRemoved;
742 // Clear the glyphs to characters buffer.
743 mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
744 glyphsToCharactersBuffer + endGlyphIndexPlusOne );
746 // Clear the characters per glyph buffer.
747 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
748 mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
749 charactersPerGlyphBuffer + endGlyphIndexPlusOne );
751 // Clear the positions buffer.
752 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
753 mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
754 positionsBuffer + endGlyphIndexPlusOne );
757 if( NO_OPERATION != ( LAYOUT & operations ) )
760 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
761 uint32_t endRemoveIndex = startRemoveIndex;
762 ClearCharacterRuns( startIndex,
764 mModel->mVisualModel->mLines,
768 // Will update the glyph runs.
769 startRemoveIndex = mModel->mVisualModel->mLines.Count();
770 endRemoveIndex = startRemoveIndex;
771 ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
772 endGlyphIndexPlusOne - 1u,
773 mModel->mVisualModel->mLines,
777 // Set the line index from where to insert the new laid-out lines.
778 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
780 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
781 mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
782 linesBuffer + endRemoveIndex );
785 if( NO_OPERATION != ( COLOR & operations ) )
787 if( 0u != mModel->mVisualModel->mColorIndices.Count() )
789 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
790 mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
791 colorIndexBuffer + endGlyphIndexPlusOne );
794 if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
796 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
797 mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
798 backgroundColorIndexBuffer + endGlyphIndexPlusOne );
803 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
805 if( mTextUpdateInfo.mClearAll ||
806 ( ( 0u == startIndex ) &&
807 ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
809 ClearFullModelData( operations );
813 // Clear the model data related with characters.
814 ClearCharacterModelData( startIndex, endIndex, operations );
816 // Clear the model data related with glyphs.
817 ClearGlyphModelData( startIndex, endIndex, operations );
820 // The estimated number of lines. Used to avoid reallocations when layouting.
821 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
823 mModel->mVisualModel->ClearCaches();
826 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
828 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
830 // Calculate the operations to be done.
831 const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
833 if( NO_OPERATION == operations )
835 // Nothing to do if no operations are pending and required.
839 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
840 Vector<Character> displayCharacters;
841 bool useHiddenText = false;
842 if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
844 mHiddenInput->Substitute( srcCharacters,displayCharacters );
845 useHiddenText = true;
848 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
849 const Length numberOfCharacters = utf32Characters.Count();
851 // Index to the first character of the first paragraph to be updated.
852 CharacterIndex startIndex = 0u;
853 // Number of characters of the paragraphs to be removed.
854 Length paragraphCharacters = 0u;
856 CalculateTextUpdateIndices( paragraphCharacters );
858 // Check whether the indices for updating the text is valid
859 if ( numberOfCharacters > 0u &&
860 ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
861 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
863 std::string currentText;
864 Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
866 DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
867 DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
869 // Dump mTextUpdateInfo
870 DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
871 DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
872 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
873 DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
874 DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
875 DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
876 DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
877 DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
878 DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
879 DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
880 DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
881 DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
882 DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
887 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
889 if( mTextUpdateInfo.mClearAll ||
890 ( 0u != paragraphCharacters ) )
892 ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
895 mTextUpdateInfo.mClearAll = false;
897 // Whether the model is updated.
898 bool updated = false;
900 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
901 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
903 if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
905 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
906 // calculate the bidirectional info for each 'paragraph'.
907 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
908 // is not shaped together).
909 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
911 SetLineBreakInfo( utf32Characters,
913 requestedNumberOfCharacters,
916 // Create the paragraph info.
917 mModel->mLogicalModel->CreateParagraphInfo( startIndex,
918 requestedNumberOfCharacters );
922 const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
923 const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
925 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
926 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
928 if( getScripts || validateFonts )
930 // Validates the fonts assigned by the application or assigns default ones.
931 // It makes sure all the characters are going to be rendered by the correct font.
932 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
936 // Retrieves the scripts used in the text.
937 multilanguageSupport.SetScripts( utf32Characters,
939 requestedNumberOfCharacters,
945 // Validate the fonts set through the mark-up string.
946 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
948 // Get the default font's description.
949 TextAbstraction::FontDescription defaultFontDescription;
950 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
952 if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
954 // If the placeholder font is set specifically, only placeholder font is changed.
955 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
956 if( mEventData->mPlaceholderFont->sizeDefined )
958 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u;
961 else if( NULL != mFontDefaults )
963 // Set the normal font and the placeholder font.
964 defaultFontDescription = mFontDefaults->mFontDescription;
966 if( mTextFitEnabled )
968 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
972 defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
976 // Validates the fonts. If there is a character with no assigned font it sets a default one.
977 // After this call, fonts are validated.
978 multilanguageSupport.ValidateFonts( utf32Characters,
981 defaultFontDescription,
984 requestedNumberOfCharacters,
990 Vector<Character> mirroredUtf32Characters;
991 bool textMirrored = false;
992 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
993 if( NO_OPERATION != ( BIDI_INFO & operations ) )
995 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
996 bidirectionalInfo.Reserve( numberOfParagraphs );
998 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
999 SetBidirectionalInfo( utf32Characters,
1003 requestedNumberOfCharacters,
1005 mModel->mMatchSystemLanguageDirection,
1008 if( 0u != bidirectionalInfo.Count() )
1010 // Only set the character directions if there is right to left characters.
1011 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
1012 GetCharactersDirection( bidirectionalInfo,
1015 requestedNumberOfCharacters,
1018 // This paragraph has right to left text. Some characters may need to be mirrored.
1019 // TODO: consider if the mirrored string can be stored as well.
1021 textMirrored = GetMirroredText( utf32Characters,
1025 requestedNumberOfCharacters,
1026 mirroredUtf32Characters );
1030 // There is no right to left characters. Clear the directions vector.
1031 mModel->mLogicalModel->mCharacterDirections.Clear();
1036 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
1037 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
1038 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
1039 Vector<GlyphIndex> newParagraphGlyphs;
1040 newParagraphGlyphs.Reserve( numberOfParagraphs );
1042 const Length currentNumberOfGlyphs = glyphs.Count();
1043 if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
1045 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1047 ShapeText( textToShape,
1052 mTextUpdateInfo.mStartGlyphIndex,
1053 requestedNumberOfCharacters,
1055 glyphsToCharactersMap,
1057 newParagraphGlyphs );
1059 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1060 mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1061 mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
1065 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
1067 if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
1069 GlyphInfo* glyphsBuffer = glyphs.Begin();
1070 mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
1072 // Update the width and advance of all new paragraph characters.
1073 for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
1075 const GlyphIndex index = *it;
1076 GlyphInfo& glyph = *( glyphsBuffer + index );
1078 glyph.xBearing = 0.f;
1080 glyph.advance = 0.f;
1085 if( ( NULL != mEventData ) &&
1086 mEventData->mPreEditFlag &&
1087 ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1089 Vector< Dali::InputMethodContext::PreeditAttributeData > attrs;
1090 mEventData->mInputMethodContext.GetPreeditStyle( attrs );
1091 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
1093 // Check the type of preedit and run it.
1094 for( Vector<Dali::InputMethodContext::PreeditAttributeData>::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
1096 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
1097 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
1098 type = attrData.preeditType;
1100 // Check the number of commit characters for the start position.
1101 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
1102 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
1106 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
1108 // Add the underline for the pre-edit text.
1109 GlyphRun underlineRun;
1110 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1111 underlineRun.numberOfGlyphs = attrData.endIndex - attrData.startIndex;
1112 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1115 case Dali::InputMethodContext::PreeditStyle::REVERSE:
1117 Vector4 textColor = mModel->mVisualModel->GetTextColor();
1118 ColorRun backgroundColorRun;
1119 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1120 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1121 backgroundColorRun.color = textColor;
1122 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1124 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
1125 Vector<ColorRun> colorRuns;
1126 colorRuns.Resize( 1u );
1127 ColorRun& colorRun = *( colorRuns.Begin() );
1128 colorRun.color = backgroundColor;
1129 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1130 colorRun.characterRun.numberOfCharacters = numberOfIndices;
1132 mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
1135 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
1137 ColorRun backgroundColorRun;
1138 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1139 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1140 backgroundColorRun.color = LIGHT_BLUE;
1141 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1144 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
1146 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
1147 ColorRun backgroundColorRun;
1148 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1149 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1150 backgroundColorRun.color = BACKGROUND_SUB4;
1151 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1153 GlyphRun underlineRun;
1154 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1155 underlineRun.numberOfGlyphs = attrData.endIndex - attrData.startIndex;
1156 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1159 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1161 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1162 ColorRun backgroundColorRun;
1163 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1164 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1165 backgroundColorRun.color = BACKGROUND_SUB5;
1166 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1168 GlyphRun underlineRun;
1169 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1170 underlineRun.numberOfGlyphs = attrData.endIndex - attrData.startIndex;
1171 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1174 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1176 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1177 ColorRun backgroundColorRun;
1178 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1179 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1180 backgroundColorRun.color = BACKGROUND_SUB6;
1181 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1183 GlyphRun underlineRun;
1184 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1185 underlineRun.numberOfGlyphs = attrData.endIndex - attrData.startIndex;
1186 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1189 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1191 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1192 ColorRun backgroundColorRun;
1193 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1194 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1195 backgroundColorRun.color = BACKGROUND_SUB7;
1196 mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
1198 GlyphRun underlineRun;
1199 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1200 underlineRun.numberOfGlyphs = attrData.endIndex - attrData.startIndex;
1201 mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1204 case Dali::InputMethodContext::PreeditStyle::NONE:
1215 if( NO_OPERATION != ( COLOR & operations ) )
1217 // Set the color runs in glyphs.
1218 SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
1219 mModel->mVisualModel->mCharactersToGlyph,
1220 mModel->mVisualModel->mGlyphsPerCharacter,
1222 mTextUpdateInfo.mStartGlyphIndex,
1223 requestedNumberOfCharacters,
1224 mModel->mVisualModel->mColors,
1225 mModel->mVisualModel->mColorIndices );
1227 // Set the background color runs in glyphs.
1228 SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
1229 mModel->mVisualModel->mCharactersToGlyph,
1230 mModel->mVisualModel->mGlyphsPerCharacter,
1232 mTextUpdateInfo.mStartGlyphIndex,
1233 requestedNumberOfCharacters,
1234 mModel->mVisualModel->mBackgroundColors,
1235 mModel->mVisualModel->mBackgroundColorIndices );
1241 // The estimated number of lines. Used to avoid reallocations when layouting.
1242 mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1244 // Set the previous number of characters for the next time the text is updated.
1245 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1250 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1252 // Sets the default text's color.
1253 inputStyle.textColor = mTextColor;
1254 inputStyle.isDefaultColor = true;
1256 inputStyle.familyName.clear();
1257 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1258 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1259 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1260 inputStyle.size = 0.f;
1262 inputStyle.lineSpacing = 0.f;
1264 inputStyle.underlineProperties.clear();
1265 inputStyle.shadowProperties.clear();
1266 inputStyle.embossProperties.clear();
1267 inputStyle.outlineProperties.clear();
1269 inputStyle.isFamilyDefined = false;
1270 inputStyle.isWeightDefined = false;
1271 inputStyle.isWidthDefined = false;
1272 inputStyle.isSlantDefined = false;
1273 inputStyle.isSizeDefined = false;
1275 inputStyle.isLineSpacingDefined = false;
1277 inputStyle.isUnderlineDefined = false;
1278 inputStyle.isShadowDefined = false;
1279 inputStyle.isEmbossDefined = false;
1280 inputStyle.isOutlineDefined = false;
1282 // Sets the default font's family name, weight, width, slant and size.
1285 if( mFontDefaults->familyDefined )
1287 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1288 inputStyle.isFamilyDefined = true;
1291 if( mFontDefaults->weightDefined )
1293 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1294 inputStyle.isWeightDefined = true;
1297 if( mFontDefaults->widthDefined )
1299 inputStyle.width = mFontDefaults->mFontDescription.width;
1300 inputStyle.isWidthDefined = true;
1303 if( mFontDefaults->slantDefined )
1305 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1306 inputStyle.isSlantDefined = true;
1309 if( mFontDefaults->sizeDefined )
1311 inputStyle.size = mFontDefaults->mDefaultPointSize;
1312 inputStyle.isSizeDefined = true;
1317 float Controller::Impl::GetDefaultFontLineHeight()
1319 FontId defaultFontId = 0u;
1320 if( NULL == mFontDefaults )
1322 TextAbstraction::FontDescription fontDescription;
1323 defaultFontId = mFontClient.GetFontId( fontDescription );
1327 defaultFontId = mFontDefaults->GetFontId( mFontClient );
1330 Text::FontMetrics fontMetrics;
1331 mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1333 return( fontMetrics.ascender - fontMetrics.descender );
1336 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1338 if( NULL == mEventData || !IsShowingRealText() )
1340 // Nothing to do if there is no text input.
1344 int keyCode = event.p1.mInt;
1345 bool isShiftModifier = event.p2.mBool;
1347 CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
1349 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1351 if( mEventData->mPrimaryCursorPosition > 0u )
1353 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1355 mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1359 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1363 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1365 if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1367 if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
1369 mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1373 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1377 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
1379 // Ignore Shift-Up for text selection for now.
1381 // Get first the line index of the current cursor position index.
1382 CharacterIndex characterIndex = 0u;
1384 if( mEventData->mPrimaryCursorPosition > 0u )
1386 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1389 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1390 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1392 // Retrieve the cursor position info.
1393 CursorInfo cursorInfo;
1394 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1397 // Get the line above.
1398 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1400 // Get the next hit 'y' point.
1401 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1403 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1404 bool matchedCharacter = false;
1405 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1406 mModel->mLogicalModel,
1408 mEventData->mCursorHookPositionX,
1410 CharacterHitTest::TAP,
1413 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
1415 // Ignore Shift-Down for text selection for now.
1417 // Get first the line index of the current cursor position index.
1418 CharacterIndex characterIndex = 0u;
1420 if( mEventData->mPrimaryCursorPosition > 0u )
1422 characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1425 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1427 if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1429 // Retrieve the cursor position info.
1430 CursorInfo cursorInfo;
1431 GetCursorPosition( mEventData->mPrimaryCursorPosition,
1434 // Get the line below.
1435 const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1437 // Get the next hit 'y' point.
1438 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1440 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1441 bool matchedCharacter = false;
1442 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1443 mModel->mLogicalModel,
1445 mEventData->mCursorHookPositionX,
1447 CharacterHitTest::TAP,
1452 if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
1454 // Update selection position after moving the cursor
1455 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1456 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1459 if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
1461 // Handle text selection
1462 bool selecting = false;
1464 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1466 // Shift-Left/Right to select the text
1467 int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
1468 if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
1470 mEventData->mRightSelectionPosition += cursorPositionDelta;
1474 else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
1476 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
1482 // Notify the cursor position to the InputMethodContext.
1483 if( mEventData->mInputMethodContext )
1485 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1486 mEventData->mInputMethodContext.NotifyCursorPosition();
1489 ChangeState( EventData::SELECTING );
1491 mEventData->mUpdateLeftSelectionPosition = true;
1492 mEventData->mUpdateRightSelectionPosition = true;
1493 mEventData->mUpdateGrabHandlePosition = true;
1494 mEventData->mUpdateHighlightBox = true;
1496 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
1497 if( mEventData->mGrabHandlePopupEnabled )
1499 mEventData->mDecorator->SetPopupActive( false );
1505 // Handle normal cursor move
1506 ChangeState( EventData::EDITING );
1507 mEventData->mUpdateCursorPosition = true;
1510 mEventData->mUpdateInputStyle = true;
1511 mEventData->mScrollAfterUpdatePosition = true;
1514 void Controller::Impl::OnTapEvent( const Event& event )
1516 if( NULL != mEventData )
1518 const unsigned int tapCount = event.p1.mUint;
1520 if( 1u == tapCount )
1522 if( IsShowingRealText() )
1524 // Convert from control's coords to text's coords.
1525 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1526 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1528 // Keep the tap 'x' position. Used to move the cursor.
1529 mEventData->mCursorHookPositionX = xPosition;
1531 // Whether to touch point hits on a glyph.
1532 bool matchedCharacter = false;
1533 mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1534 mModel->mLogicalModel,
1538 CharacterHitTest::TAP,
1541 // When the cursor position is changing, delay cursor blinking
1542 mEventData->mDecorator->DelayCursorBlink();
1546 mEventData->mPrimaryCursorPosition = 0u;
1549 // Update selection position after tapping
1550 mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
1551 mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1553 mEventData->mUpdateCursorPosition = true;
1554 mEventData->mUpdateGrabHandlePosition = true;
1555 mEventData->mScrollAfterUpdatePosition = true;
1556 mEventData->mUpdateInputStyle = true;
1558 // Notify the cursor position to the InputMethodContext.
1559 if( mEventData->mInputMethodContext )
1561 mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1562 mEventData->mInputMethodContext.NotifyCursorPosition();
1565 else if( 2u == tapCount )
1567 if( mEventData->mSelectionEnabled )
1569 // Convert from control's coords to text's coords.
1570 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1571 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1573 // Calculates the logical position from the x,y coords.
1574 RepositionSelectionHandles( xPosition,
1576 mEventData->mDoubleTapAction );
1582 void Controller::Impl::OnPanEvent( const Event& event )
1584 if( NULL == mEventData )
1586 // Nothing to do if there is no text input.
1590 const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1591 const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1593 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1595 // Nothing to do if scrolling is not enabled.
1599 const int state = event.p1.mInt;
1603 case Gesture::Started:
1605 // Will remove the cursor, handles or text's popup, ...
1606 ChangeState( EventData::TEXT_PANNING );
1609 case Gesture::Continuing:
1611 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1612 const Vector2 currentScroll = mModel->mScrollPosition;
1614 if( isHorizontalScrollEnabled )
1616 const float displacementX = event.p2.mFloat;
1617 mModel->mScrollPosition.x += displacementX;
1619 ClampHorizontalScroll( layoutSize );
1622 if( isVerticalScrollEnabled )
1624 const float displacementY = event.p3.mFloat;
1625 mModel->mScrollPosition.y += displacementY;
1627 ClampVerticalScroll( layoutSize );
1630 mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1633 case Gesture::Finished:
1634 case Gesture::Cancelled: // FALLTHROUGH
1636 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1637 ChangeState( mEventData->mPreviousState );
1645 void Controller::Impl::OnLongPressEvent( const Event& event )
1647 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1649 if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
1651 ChangeState( EventData::EDITING_WITH_POPUP );
1652 mEventData->mDecoratorUpdated = true;
1653 mEventData->mUpdateInputStyle = true;
1657 if( mEventData->mSelectionEnabled )
1659 // Convert from control's coords to text's coords.
1660 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1661 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1663 // Calculates the logical position from the x,y coords.
1664 RepositionSelectionHandles( xPosition,
1666 mEventData->mLongPressAction );
1671 void Controller::Impl::OnHandleEvent( const Event& event )
1673 if( NULL == mEventData )
1675 // Nothing to do if there is no text input.
1679 const unsigned int state = event.p1.mUint;
1680 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1681 const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1683 if( HANDLE_PRESSED == state )
1685 // Convert from decorator's coords to text's coords.
1686 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1687 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1689 // Need to calculate the handle's new position.
1690 bool matchedCharacter = false;
1691 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1692 mModel->mLogicalModel,
1696 CharacterHitTest::SCROLL,
1699 if( Event::GRAB_HANDLE_EVENT == event.type )
1701 ChangeState ( EventData::GRAB_HANDLE_PANNING );
1703 if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1705 // Updates the cursor position if the handle's new position is different than the current one.
1706 mEventData->mUpdateCursorPosition = true;
1707 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1708 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1709 mEventData->mPrimaryCursorPosition = handleNewPosition;
1712 // 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.
1713 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1715 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1717 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1719 if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1720 ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1722 // Updates the highlight box if the handle's new position is different than the current one.
1723 mEventData->mUpdateHighlightBox = true;
1724 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1725 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1726 mEventData->mLeftSelectionPosition = handleNewPosition;
1729 // 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.
1730 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1732 // Will define the order to scroll the text to match the handle position.
1733 mEventData->mIsLeftHandleSelected = true;
1734 mEventData->mIsRightHandleSelected = false;
1736 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1738 ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1740 if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1741 ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1743 // Updates the highlight box if the handle's new position is different than the current one.
1744 mEventData->mUpdateHighlightBox = true;
1745 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1746 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1747 mEventData->mRightSelectionPosition = handleNewPosition;
1750 // 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.
1751 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1753 // Will define the order to scroll the text to match the handle position.
1754 mEventData->mIsLeftHandleSelected = false;
1755 mEventData->mIsRightHandleSelected = true;
1757 } // end ( HANDLE_PRESSED == state )
1758 else if( ( HANDLE_RELEASED == state ) ||
1759 handleStopScrolling )
1761 CharacterIndex handlePosition = 0u;
1762 if( handleStopScrolling || isSmoothHandlePanEnabled )
1764 // Convert from decorator's coords to text's coords.
1765 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1766 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1768 bool matchedCharacter = false;
1769 handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1770 mModel->mLogicalModel,
1774 CharacterHitTest::SCROLL,
1778 if( Event::GRAB_HANDLE_EVENT == event.type )
1780 mEventData->mUpdateCursorPosition = true;
1781 mEventData->mUpdateGrabHandlePosition = true;
1782 mEventData->mUpdateInputStyle = true;
1784 if( !IsClipboardEmpty() )
1786 ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1789 if( handleStopScrolling || isSmoothHandlePanEnabled )
1791 mEventData->mScrollAfterUpdatePosition = true;
1792 mEventData->mPrimaryCursorPosition = handlePosition;
1795 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1797 ChangeState( EventData::SELECTING );
1799 mEventData->mUpdateHighlightBox = true;
1800 mEventData->mUpdateLeftSelectionPosition = true;
1801 mEventData->mUpdateRightSelectionPosition = true;
1803 if( handleStopScrolling || isSmoothHandlePanEnabled )
1805 mEventData->mScrollAfterUpdatePosition = true;
1807 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1808 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1810 mEventData->mLeftSelectionPosition = handlePosition;
1814 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1816 ChangeState( EventData::SELECTING );
1818 mEventData->mUpdateHighlightBox = true;
1819 mEventData->mUpdateRightSelectionPosition = true;
1820 mEventData->mUpdateLeftSelectionPosition = true;
1822 if( handleStopScrolling || isSmoothHandlePanEnabled )
1824 mEventData->mScrollAfterUpdatePosition = true;
1825 if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1826 ( handlePosition != mEventData->mLeftSelectionPosition ) )
1828 mEventData->mRightSelectionPosition = handlePosition;
1833 mEventData->mDecoratorUpdated = true;
1834 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1835 else if( HANDLE_SCROLLING == state )
1837 const float xSpeed = event.p2.mFloat;
1838 const float ySpeed = event.p3.mFloat;
1839 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1840 const Vector2 currentScrollPosition = mModel->mScrollPosition;
1842 mModel->mScrollPosition.x += xSpeed;
1843 mModel->mScrollPosition.y += ySpeed;
1845 ClampHorizontalScroll( layoutSize );
1846 ClampVerticalScroll( layoutSize );
1848 bool endOfScroll = false;
1849 if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1851 // Notify the decorator there is no more text to scroll.
1852 // The decorator won't send more scroll events.
1853 mEventData->mDecorator->NotifyEndOfScroll();
1854 // Still need to set the position of the handle.
1858 // Set the position of the handle.
1859 const bool scrollRightDirection = xSpeed > 0.f;
1860 const bool scrollBottomDirection = ySpeed > 0.f;
1861 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1862 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1864 if( Event::GRAB_HANDLE_EVENT == event.type )
1866 ChangeState( EventData::GRAB_HANDLE_PANNING );
1868 // Get the grab handle position in decorator coords.
1869 Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1871 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1873 // Position the grag handle close to either the left or right edge.
1874 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1877 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1879 position.x = mEventData->mCursorHookPositionX;
1881 // Position the grag handle close to either the top or bottom edge.
1882 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1885 // Get the new handle position.
1886 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1887 bool matchedCharacter = false;
1888 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1889 mModel->mLogicalModel,
1891 position.x - mModel->mScrollPosition.x,
1892 position.y - mModel->mScrollPosition.y,
1893 CharacterHitTest::SCROLL,
1896 if( mEventData->mPrimaryCursorPosition != handlePosition )
1898 mEventData->mUpdateCursorPosition = true;
1899 mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1900 mEventData->mScrollAfterUpdatePosition = true;
1901 mEventData->mPrimaryCursorPosition = handlePosition;
1903 mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1905 // Updates the decorator if the soft handle panning is enabled.
1906 mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1908 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1910 ChangeState( EventData::SELECTION_HANDLE_PANNING );
1912 // Get the selection handle position in decorator coords.
1913 Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1915 if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1917 // Position the selection handle close to either the left or right edge.
1918 position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1921 if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1923 position.x = mEventData->mCursorHookPositionX;
1925 // Position the grag handle close to either the top or bottom edge.
1926 position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1929 // Get the new handle position.
1930 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1931 bool matchedCharacter = false;
1932 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1933 mModel->mLogicalModel,
1935 position.x - mModel->mScrollPosition.x,
1936 position.y - mModel->mScrollPosition.y,
1937 CharacterHitTest::SCROLL,
1940 if( leftSelectionHandleEvent )
1942 const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1944 if( differentHandles || endOfScroll )
1946 mEventData->mUpdateHighlightBox = true;
1947 mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1948 mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1949 mEventData->mLeftSelectionPosition = handlePosition;
1954 const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1955 if( differentHandles || endOfScroll )
1957 mEventData->mUpdateHighlightBox = true;
1958 mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1959 mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1960 mEventData->mRightSelectionPosition = handlePosition;
1964 if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1966 RepositionSelectionHandles();
1968 mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1971 mEventData->mDecoratorUpdated = true;
1972 } // end ( HANDLE_SCROLLING == state )
1975 void Controller::Impl::OnSelectEvent( const Event& event )
1977 if( NULL == mEventData )
1979 // Nothing to do if there is no text.
1983 if( mEventData->mSelectionEnabled )
1985 // Convert from control's coords to text's coords.
1986 const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1987 const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1989 // Calculates the logical position from the x,y coords.
1990 RepositionSelectionHandles( xPosition,
1992 Controller::NoTextTap::HIGHLIGHT );
1996 void Controller::Impl::OnSelectAllEvent()
1998 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
2000 if( NULL == mEventData )
2002 // Nothing to do if there is no text.
2006 if( mEventData->mSelectionEnabled )
2008 // Calculates the logical position from the start.
2009 RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
2010 0.f - mModel->mScrollPosition.y,
2011 Controller::NoTextTap::HIGHLIGHT );
2013 mEventData->mLeftSelectionPosition = 0u;
2014 mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
2018 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
2020 if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
2022 // Nothing to select if handles are in the same place.
2023 selectedText.clear();
2027 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
2029 //Get start and end position of selection
2030 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2031 const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
2033 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
2034 const Length numberOfCharacters = utf32Characters.Count();
2036 // Validate the start and end selection points
2037 if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
2039 //Get text as a UTF8 string
2040 Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
2042 if( deleteAfterRetrieval ) // Only delete text if copied successfully
2044 // Keep a copy of the current input style.
2045 InputStyle currentInputStyle;
2046 currentInputStyle.Copy( mEventData->mInputStyle );
2048 // Set as input style the style of the first deleted character.
2049 mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
2051 // Compare if the input style has changed.
2052 const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
2054 if( hasInputStyleChanged )
2056 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
2057 // Queue the input style changed signal.
2058 mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
2061 mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
2063 // Mark the paragraphs to be updated.
2064 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2066 mTextUpdateInfo.mCharacterIndex = 0;
2067 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
2068 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
2069 mTextUpdateInfo.mClearAll = true;
2073 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
2074 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
2077 // Delete text between handles
2078 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
2079 Vector<Character>::Iterator last = first + lengthOfSelectedText;
2080 utf32Characters.Erase( first, last );
2082 // Will show the cursor at the first character of the selection.
2083 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
2087 // Will show the cursor at the last character of the selection.
2088 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2091 mEventData->mDecoratorUpdated = true;
2095 void Controller::Impl::ShowClipboard()
2099 mClipboard.ShowClipboard();
2103 void Controller::Impl::HideClipboard()
2105 if( mClipboard && mClipboardHideEnabled )
2107 mClipboard.HideClipboard();
2111 void Controller::Impl::SetClipboardHideEnable(bool enable)
2113 mClipboardHideEnabled = enable;
2116 bool Controller::Impl::CopyStringToClipboard( std::string& source )
2118 //Send string to clipboard
2119 return ( mClipboard && mClipboard.SetItem( source ) );
2122 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
2124 std::string selectedText;
2125 RetrieveSelection( selectedText, deleteAfterSending );
2126 CopyStringToClipboard( selectedText );
2127 ChangeState( EventData::EDITING );
2130 void Controller::Impl::RequestGetTextFromClipboard()
2134 mClipboard.RequestItem();
2138 void Controller::Impl::RepositionSelectionHandles()
2140 CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
2141 CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
2143 if( selectionStart == selectionEnd )
2145 // Nothing to select if handles are in the same place.
2146 // So, deactive Highlight box.
2147 mEventData->mDecorator->SetHighlightActive( false );
2151 mEventData->mDecorator->ClearHighlights();
2153 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2154 const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
2155 const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
2156 const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
2157 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2158 const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
2159 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
2161 const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
2162 const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
2163 const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
2165 // Swap the indices if the start is greater than the end.
2166 const bool indicesSwapped = selectionStart > selectionEnd;
2168 // Tell the decorator to flip the selection handles if needed.
2169 mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
2171 if( indicesSwapped )
2173 std::swap( selectionStart, selectionEnd );
2176 // Get the indices to the first and last selected glyphs.
2177 const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
2178 const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
2179 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
2180 const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
2182 // Get the lines where the glyphs are laid-out.
2183 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2185 LineIndex lineIndex = 0u;
2186 Length numberOfLines = 0u;
2187 mModel->mVisualModel->GetNumberOfLines( glyphStart,
2188 1u + glyphEnd - glyphStart,
2191 const LineIndex firstLineIndex = lineIndex;
2193 // Create the structure to store some selection box info.
2194 Vector<SelectionBoxInfo> selectionBoxLinesInfo;
2195 selectionBoxLinesInfo.Resize( numberOfLines );
2197 SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
2198 selectionBoxInfo->minX = MAX_FLOAT;
2199 selectionBoxInfo->maxX = MIN_FLOAT;
2201 // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
2202 float minHighlightX = std::numeric_limits<float>::max();
2203 float maxHighlightX = std::numeric_limits<float>::min();
2205 Vector2 highLightPosition; // The highlight position in decorator's coords.
2207 // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
2209 // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
2210 selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
2213 // Transform to decorator's (control) coords.
2214 selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
2216 lineRun += firstLineIndex;
2218 // The line height is the addition of the line ascender and the line descender.
2219 // However, the line descender has a negative value, hence the subtraction.
2220 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2222 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2224 // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2225 const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
2226 bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
2228 // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
2229 const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
2230 bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
2232 // The number of quads of the selection box.
2233 const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
2234 mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
2236 // Count the actual number of quads.
2237 unsigned int actualNumberOfQuads = 0u;
2240 // Traverse the glyphs.
2241 for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
2243 const GlyphInfo& glyph = *( glyphsBuffer + index );
2244 const Vector2& position = *( positionsBuffer + index );
2246 if( splitStartGlyph )
2248 // 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.
2250 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
2251 const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
2252 // Get the direction of the character.
2253 CharacterDirection isCurrentRightToLeft = false;
2254 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2256 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
2259 // The end point could be in the middle of the ligature.
2260 // Calculate the number of characters selected.
2261 const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
2263 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
2264 quad.y = selectionBoxInfo->lineOffset;
2265 quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
2266 quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
2268 // Store the min and max 'x' for each line.
2269 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2270 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2272 mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
2273 ++actualNumberOfQuads;
2275 splitStartGlyph = false;
2279 if( splitEndGlyph && ( index == glyphEnd ) )
2281 // 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.
2283 const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
2284 const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
2285 // Get the direction of the character.
2286 CharacterDirection isCurrentRightToLeft = false;
2287 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
2289 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
2292 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
2294 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
2295 quad.y = selectionBoxInfo->lineOffset;
2296 quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
2297 quad.w = quad.y + selectionBoxInfo->lineHeight;
2299 // Store the min and max 'x' for each line.
2300 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2301 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2303 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2305 ++actualNumberOfQuads;
2307 splitEndGlyph = false;
2311 quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
2312 quad.y = selectionBoxInfo->lineOffset;
2313 quad.z = quad.x + glyph.advance;
2314 quad.w = quad.y + selectionBoxInfo->lineHeight;
2316 // Store the min and max 'x' for each line.
2317 selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
2318 selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
2320 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2322 ++actualNumberOfQuads;
2324 // Whether to retrieve the next line.
2325 if( index == lastGlyphOfLine )
2328 if( lineIndex < firstLineIndex + numberOfLines )
2330 // Retrieve the next line.
2333 // Get the last glyph of the new line.
2334 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
2336 // Keep the offset and height of the current selection box.
2337 const float currentLineOffset = selectionBoxInfo->lineOffset;
2338 const float currentLineHeight = selectionBoxInfo->lineHeight;
2340 // Get the selection box info for the next line.
2343 selectionBoxInfo->minX = MAX_FLOAT;
2344 selectionBoxInfo->maxX = MIN_FLOAT;
2346 // Update the line's vertical offset.
2347 selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
2349 // The line height is the addition of the line ascender and the line descender.
2350 // However, the line descender has a negative value, hence the subtraction.
2351 selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
2356 // Traverses all the lines and updates the min and max 'x' positions and the total height.
2357 // The final width is calculated after 'boxifying' the selection.
2358 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2359 endIt = selectionBoxLinesInfo.End();
2363 const SelectionBoxInfo& info = *it;
2365 // Update the size of the highlighted text.
2366 highLightSize.height += info.lineHeight;
2367 minHighlightX = std::min( minHighlightX, info.minX );
2368 maxHighlightX = std::max( maxHighlightX, info.maxX );
2371 // Add extra geometry to 'boxify' the selection.
2373 if( 1u < numberOfLines )
2375 // Boxify the first line.
2376 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2377 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2379 bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2380 bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2385 quad.y = firstSelectionBoxLineInfo.lineOffset;
2386 quad.z = firstSelectionBoxLineInfo.minX;
2387 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2389 // Boxify at the beginning of the line.
2390 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2392 ++actualNumberOfQuads;
2394 // Update the size of the highlighted text.
2395 minHighlightX = 0.f;
2400 quad.x = firstSelectionBoxLineInfo.maxX;
2401 quad.y = firstSelectionBoxLineInfo.lineOffset;
2402 quad.z = mModel->mVisualModel->mControlSize.width;
2403 quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2405 // Boxify at the end of the line.
2406 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2408 ++actualNumberOfQuads;
2410 // Update the size of the highlighted text.
2411 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2414 // Boxify the central lines.
2415 if( 2u < numberOfLines )
2417 for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2418 endIt = selectionBoxLinesInfo.End() - 1u;
2422 const SelectionBoxInfo& info = *it;
2425 quad.y = info.lineOffset;
2427 quad.w = info.lineOffset + info.lineHeight;
2429 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2431 ++actualNumberOfQuads;
2434 quad.y = info.lineOffset;
2435 quad.z = mModel->mVisualModel->mControlSize.width;
2436 quad.w = info.lineOffset + info.lineHeight;
2438 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2440 ++actualNumberOfQuads;
2443 // Update the size of the highlighted text.
2444 minHighlightX = 0.f;
2445 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2448 // Boxify the last line.
2449 lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2450 const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2452 boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2453 boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2458 quad.y = lastSelectionBoxLineInfo.lineOffset;
2459 quad.z = lastSelectionBoxLineInfo.minX;
2460 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2462 // Boxify at the beginning of the line.
2463 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2465 ++actualNumberOfQuads;
2467 // Update the size of the highlighted text.
2468 minHighlightX = 0.f;
2473 quad.x = lastSelectionBoxLineInfo.maxX;
2474 quad.y = lastSelectionBoxLineInfo.lineOffset;
2475 quad.z = mModel->mVisualModel->mControlSize.width;
2476 quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2478 // Boxify at the end of the line.
2479 mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2481 ++actualNumberOfQuads;
2483 // Update the size of the highlighted text.
2484 maxHighlightX = mModel->mVisualModel->mControlSize.width;
2488 // Set the actual number of quads.
2489 mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2491 // Sets the highlight's size and position. In decorator's coords.
2492 // The highlight's height has been calculated above (before 'boxifying' the highlight).
2493 highLightSize.width = maxHighlightX - minHighlightX;
2495 highLightPosition.x = minHighlightX;
2496 const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2497 highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2499 mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
2501 if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2503 CursorInfo primaryCursorInfo;
2504 GetCursorPosition( mEventData->mLeftSelectionPosition,
2505 primaryCursorInfo );
2507 const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2509 mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2511 primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2512 primaryCursorInfo.lineHeight );
2514 CursorInfo secondaryCursorInfo;
2515 GetCursorPosition( mEventData->mRightSelectionPosition,
2516 secondaryCursorInfo );
2518 const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2520 mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2521 secondaryPosition.x,
2522 secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2523 secondaryCursorInfo.lineHeight );
2526 // Set the flag to update the decorator.
2527 mEventData->mDecoratorUpdated = true;
2530 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
2532 if( NULL == mEventData )
2534 // Nothing to do if there is no text input.
2538 if( IsShowingPlaceholderText() )
2540 // Nothing to do if there is the place-holder text.
2544 const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2545 const Length numberOfLines = mModel->mVisualModel->mLines.Count();
2546 if( ( 0 == numberOfGlyphs ) ||
2547 ( 0 == numberOfLines ) )
2549 // Nothing to do if there is no text.
2553 // Find which word was selected
2554 CharacterIndex selectionStart( 0 );
2555 CharacterIndex selectionEnd( 0 );
2556 CharacterIndex noTextHitIndex( 0 );
2557 const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
2558 mModel->mLogicalModel,
2565 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2567 if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
2569 ChangeState( EventData::SELECTING );
2571 mEventData->mLeftSelectionPosition = selectionStart;
2572 mEventData->mRightSelectionPosition = selectionEnd;
2574 mEventData->mUpdateLeftSelectionPosition = true;
2575 mEventData->mUpdateRightSelectionPosition = true;
2576 mEventData->mUpdateHighlightBox = true;
2578 // It may happen an InputMethodContext commit event arrives before the selection event
2579 // if the InputMethodContext is in pre-edit state. The commit event will set the
2580 // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2581 // to false, the highlight box won't be updated.
2582 mEventData->mUpdateCursorPosition = false;
2584 mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2586 // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2587 mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2589 else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
2591 // Nothing to select. i.e. a white space, out of bounds
2592 ChangeState( EventData::EDITING_WITH_POPUP );
2594 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2596 mEventData->mUpdateCursorPosition = true;
2597 mEventData->mUpdateGrabHandlePosition = true;
2598 mEventData->mScrollAfterUpdatePosition = true;
2599 mEventData->mUpdateInputStyle = true;
2601 else if( Controller::NoTextTap::NO_ACTION == action )
2603 // Nothing to select. i.e. a white space, out of bounds
2604 mEventData->mPrimaryCursorPosition = noTextHitIndex;
2606 mEventData->mUpdateCursorPosition = true;
2607 mEventData->mUpdateGrabHandlePosition = true;
2608 mEventData->mScrollAfterUpdatePosition = true;
2609 mEventData->mUpdateInputStyle = true;
2613 void Controller::Impl::SetPopupButtons()
2616 * Sets the Popup buttons to be shown depending on State.
2618 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2620 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2623 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2625 if( EventData::SELECTING == mEventData->mState )
2627 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2629 if( !IsClipboardEmpty() )
2631 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2632 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2635 if( !mEventData->mAllTextSelected )
2637 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2640 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2642 if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2644 buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2647 if( !IsClipboardEmpty() )
2649 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2650 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2653 else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2655 if ( !IsClipboardEmpty() )
2657 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2658 buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2662 mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2665 void Controller::Impl::ChangeState( EventData::State newState )
2667 if( NULL == mEventData )
2669 // Nothing to do if there is no text input.
2673 DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState );
2675 if( mEventData->mState != newState )
2677 mEventData->mPreviousState = mEventData->mState;
2678 mEventData->mState = newState;
2680 switch( mEventData->mState )
2682 case EventData::INACTIVE:
2684 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2685 mEventData->mDecorator->StopCursorBlink();
2686 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2687 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2688 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2689 mEventData->mDecorator->SetHighlightActive( false );
2690 mEventData->mDecorator->SetPopupActive( false );
2691 mEventData->mDecoratorUpdated = true;
2694 case EventData::INTERRUPTED:
2696 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2697 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2698 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2699 mEventData->mDecorator->SetHighlightActive( false );
2700 mEventData->mDecorator->SetPopupActive( false );
2701 mEventData->mDecoratorUpdated = true;
2704 case EventData::SELECTING:
2706 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2707 mEventData->mDecorator->StopCursorBlink();
2708 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2709 if ( mEventData->mGrabHandleEnabled )
2711 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2712 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2714 mEventData->mDecorator->SetHighlightActive( true );
2715 if( mEventData->mGrabHandlePopupEnabled )
2718 mEventData->mDecorator->SetPopupActive( true );
2720 mEventData->mDecoratorUpdated = true;
2723 case EventData::EDITING:
2725 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2726 if( mEventData->mCursorBlinkEnabled )
2728 mEventData->mDecorator->StartCursorBlink();
2730 // Grab handle is not shown until a tap is received whilst EDITING
2731 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2732 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2733 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2734 mEventData->mDecorator->SetHighlightActive( false );
2735 if( mEventData->mGrabHandlePopupEnabled )
2737 mEventData->mDecorator->SetPopupActive( false );
2739 mEventData->mDecoratorUpdated = true;
2742 case EventData::EDITING_WITH_POPUP:
2744 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2746 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2747 if( mEventData->mCursorBlinkEnabled )
2749 mEventData->mDecorator->StartCursorBlink();
2751 if( mEventData->mSelectionEnabled )
2753 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2754 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2755 mEventData->mDecorator->SetHighlightActive( false );
2757 else if ( mEventData->mGrabHandleEnabled )
2759 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2761 if( mEventData->mGrabHandlePopupEnabled )
2764 mEventData->mDecorator->SetPopupActive( true );
2766 mEventData->mDecoratorUpdated = true;
2769 case EventData::EDITING_WITH_GRAB_HANDLE:
2771 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2773 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2774 if( mEventData->mCursorBlinkEnabled )
2776 mEventData->mDecorator->StartCursorBlink();
2778 // Grab handle is not shown until a tap is received whilst EDITING
2779 if ( mEventData->mGrabHandleEnabled )
2781 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2783 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2784 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2785 mEventData->mDecorator->SetHighlightActive( false );
2786 if( mEventData->mGrabHandlePopupEnabled )
2788 mEventData->mDecorator->SetPopupActive( false );
2790 mEventData->mDecoratorUpdated = true;
2793 case EventData::SELECTION_HANDLE_PANNING:
2795 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2796 mEventData->mDecorator->StopCursorBlink();
2797 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2798 if ( mEventData->mGrabHandleEnabled )
2800 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2801 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2803 mEventData->mDecorator->SetHighlightActive( true );
2804 if( mEventData->mGrabHandlePopupEnabled )
2806 mEventData->mDecorator->SetPopupActive( false );
2808 mEventData->mDecoratorUpdated = true;
2811 case EventData::GRAB_HANDLE_PANNING:
2813 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2815 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2816 if( mEventData->mCursorBlinkEnabled )
2818 mEventData->mDecorator->StartCursorBlink();
2820 if ( mEventData->mGrabHandleEnabled )
2822 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2824 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2825 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2826 mEventData->mDecorator->SetHighlightActive( false );
2827 if( mEventData->mGrabHandlePopupEnabled )
2829 mEventData->mDecorator->SetPopupActive( false );
2831 mEventData->mDecoratorUpdated = true;
2834 case EventData::EDITING_WITH_PASTE_POPUP:
2836 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2838 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2839 if( mEventData->mCursorBlinkEnabled )
2841 mEventData->mDecorator->StartCursorBlink();
2844 if ( mEventData->mGrabHandleEnabled )
2846 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2848 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2849 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2850 mEventData->mDecorator->SetHighlightActive( false );
2852 if( mEventData->mGrabHandlePopupEnabled )
2855 mEventData->mDecorator->SetPopupActive( true );
2857 mEventData->mDecoratorUpdated = true;
2860 case EventData::TEXT_PANNING:
2862 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2863 mEventData->mDecorator->StopCursorBlink();
2864 mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2865 if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2866 mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2868 mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2869 mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2870 mEventData->mDecorator->SetHighlightActive( true );
2873 if( mEventData->mGrabHandlePopupEnabled )
2875 mEventData->mDecorator->SetPopupActive( false );
2878 mEventData->mDecoratorUpdated = true;
2885 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2886 CursorInfo& cursorInfo )
2888 if( !IsShowingRealText() )
2890 // Do not want to use the place-holder text to set the cursor position.
2892 // Use the line's height of the font's family set to set the cursor's size.
2893 // If there is no font's family set, use the default font.
2894 // Use the current alignment to place the cursor at the beginning, center or end of the box.
2896 cursorInfo.lineOffset = 0.f;
2897 cursorInfo.lineHeight = GetDefaultFontLineHeight();
2898 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2901 if( mModel->mMatchSystemLanguageDirection )
2903 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2906 switch( mModel->mHorizontalAlignment )
2908 case Text::HorizontalAlignment::BEGIN :
2912 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2916 cursorInfo.primaryPosition.x = 0.f;
2920 case Text::HorizontalAlignment::CENTER:
2922 cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2925 case Text::HorizontalAlignment::END:
2929 cursorInfo.primaryPosition.x = 0.f;
2933 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2939 // Nothing else to do.
2943 const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() );
2944 GetCursorPositionParameters parameters;
2945 parameters.visualModel = mModel->mVisualModel;
2946 parameters.logicalModel = mModel->mLogicalModel;
2947 parameters.metrics = mMetrics;
2948 parameters.logical = logical;
2949 parameters.isMultiline = isMultiLine;
2951 Text::GetCursorPosition( parameters,
2954 // Adds Outline offset.
2955 const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
2956 cursorInfo.primaryPosition.x += outlineWidth;
2957 cursorInfo.primaryPosition.y += outlineWidth;
2958 cursorInfo.secondaryPosition.x += outlineWidth;
2959 cursorInfo.secondaryPosition.y += outlineWidth;
2963 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2965 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2966 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2968 if( 0.f > cursorInfo.primaryPosition.x )
2970 cursorInfo.primaryPosition.x = 0.f;
2973 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2974 if( cursorInfo.primaryPosition.x > edgeWidth )
2976 cursorInfo.primaryPosition.x = edgeWidth;
2981 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2983 if( NULL == mEventData )
2985 // Nothing to do if there is no text input.
2989 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2991 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2992 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2994 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2995 Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2997 if( numberOfCharacters > 1u )
2999 const Script script = mModel->mLogicalModel->GetScript( index );
3000 if( HasLigatureMustBreak( script ) )
3002 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
3003 numberOfCharacters = 1u;
3008 while( 0u == numberOfCharacters )
3011 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
3015 if( index < mEventData->mPrimaryCursorPosition )
3017 cursorIndex -= numberOfCharacters;
3021 cursorIndex += numberOfCharacters;
3024 // Will update the cursor hook position.
3025 mEventData->mUpdateCursorHookPosition = true;
3030 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
3032 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
3033 if( NULL == mEventData )
3035 // Nothing to do if there is no text input.
3036 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
3040 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3042 mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset );
3044 // Sets the cursor position.
3045 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
3048 cursorInfo.primaryCursorHeight,
3049 cursorInfo.lineHeight );
3050 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
3052 if( mEventData->mUpdateGrabHandlePosition )
3054 // Sets the grab handle position.
3055 mEventData->mDecorator->SetPosition( GRAB_HANDLE,
3057 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3058 cursorInfo.lineHeight );
3061 if( cursorInfo.isSecondaryCursor )
3063 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
3064 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
3065 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
3066 cursorInfo.secondaryCursorHeight,
3067 cursorInfo.lineHeight );
3068 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
3071 // Set which cursors are active according the state.
3072 if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
3074 if( cursorInfo.isSecondaryCursor )
3076 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
3080 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
3085 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
3088 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
3091 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
3092 const CursorInfo& cursorInfo )
3094 if( ( LEFT_SELECTION_HANDLE != handleType ) &&
3095 ( RIGHT_SELECTION_HANDLE != handleType ) )
3100 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
3102 // Sets the handle's position.
3103 mEventData->mDecorator->SetPosition( handleType,
3105 cursorInfo.lineOffset + mModel->mScrollPosition.y,
3106 cursorInfo.lineHeight );
3108 // If selection handle at start of the text and other at end of the text then all text is selected.
3109 const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3110 const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
3111 mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
3114 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
3116 // Clamp between -space & -alignment offset.
3118 if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
3120 const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
3121 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
3122 mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
3124 mEventData->mDecoratorUpdated = true;
3128 mModel->mScrollPosition.x = 0.f;
3132 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
3134 if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
3136 // Nothing to do if the text is single line.
3140 // Clamp between -space & 0.
3141 if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
3143 const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
3144 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
3145 mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
3147 mEventData->mDecoratorUpdated = true;
3151 mModel->mScrollPosition.y = 0.f;
3155 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
3157 const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
3159 // position is in actor's coords.
3160 const float positionEndX = position.x + cursorWidth;
3161 const float positionEndY = position.y + lineHeight;
3163 // Transform the position to decorator coords.
3164 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
3165 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
3167 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
3168 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
3170 if( decoratorPositionBeginX < 0.f )
3172 mModel->mScrollPosition.x = -position.x;
3174 else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
3176 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
3179 if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
3181 if( decoratorPositionBeginY < 0.f )
3183 mModel->mScrollPosition.y = -position.y;
3185 else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
3187 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
3192 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
3194 // Get the current cursor position in decorator coords.
3195 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3197 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition );
3201 // Calculate the offset to match the cursor position before the character was deleted.
3202 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
3204 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
3205 if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u )
3207 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR );
3208 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
3212 ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
3213 ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
3215 // Makes the new cursor position visible if needed.
3216 ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
3219 void Controller::Impl::RequestRelayout()
3221 if( NULL != mControlInterface )
3223 mControlInterface->RequestTextRelayout();
3227 Actor Controller::Impl::CreateBackgroundActor()
3229 // NOTE: Currently we only support background color for one line left-to-right text,
3230 // so the following calculation is based on one line left-to-right text only!
3234 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
3235 if( numberOfGlyphs > 0u )
3237 Vector<GlyphInfo> glyphs;
3238 glyphs.Resize( numberOfGlyphs );
3240 Vector<Vector2> positions;
3241 positions.Resize( numberOfGlyphs );
3243 // Get the line where the glyphs are laid-out.
3244 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
3245 float alignmentOffset = lineRun->alignmentOffset;
3246 numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
3252 glyphs.Resize( numberOfGlyphs );
3253 positions.Resize( numberOfGlyphs );
3255 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
3256 const Vector2* const positionsBuffer = positions.Begin();
3258 BackgroundMesh mesh;
3259 mesh.mVertices.Reserve( 4u * glyphs.Size() );
3260 mesh.mIndices.Reserve( 6u * glyphs.Size() );
3262 const Vector2 textSize = mView.GetLayoutSize();
3264 const float offsetX = textSize.width * 0.5f;
3265 const float offsetY = textSize.height * 0.5f;
3267 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
3268 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
3269 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
3272 uint32_t numberOfQuads = 0u;
3274 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
3276 const GlyphInfo& glyph = *( glyphsBuffer + i );
3278 // Get the background color of the character.
3279 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
3280 const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
3281 const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
3283 // Only create quads for glyphs with a background color
3284 if ( backgroundColor != Color::TRANSPARENT )
3286 const Vector2 position = *( positionsBuffer + i );
3288 if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
3290 quad.x = position.x;
3292 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3293 quad.w = textSize.height;
3295 else if ( i == 0u ) // The first glyph in the whole text
3297 quad.x = position.x;
3299 quad.z = quad.x - glyph.xBearing + glyph.advance;
3300 quad.w = textSize.height;
3302 else if ( i == glyphSize - 1u ) // The last glyph in the whole text
3304 quad.x = position.x - glyph.xBearing;
3306 quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
3307 quad.w = textSize.height;
3309 else // The glyph in the middle of the text
3311 quad.x = position.x - glyph.xBearing;
3313 quad.z = quad.x + glyph.advance;
3314 quad.w = textSize.height;
3317 BackgroundVertex vertex;
3320 vertex.mPosition.x = quad.x - offsetX;
3321 vertex.mPosition.y = quad.y - offsetY;
3322 vertex.mColor = backgroundColor;
3323 mesh.mVertices.PushBack( vertex );
3326 vertex.mPosition.x = quad.z - offsetX;
3327 vertex.mPosition.y = quad.y - offsetY;
3328 vertex.mColor = backgroundColor;
3329 mesh.mVertices.PushBack( vertex );
3332 vertex.mPosition.x = quad.x - offsetX;
3333 vertex.mPosition.y = quad.w - offsetY;
3334 vertex.mColor = backgroundColor;
3335 mesh.mVertices.PushBack( vertex );
3338 vertex.mPosition.x = quad.z - offsetX;
3339 vertex.mPosition.y = quad.w - offsetY;
3340 vertex.mColor = backgroundColor;
3341 mesh.mVertices.PushBack( vertex );
3343 // Six indices in counter clockwise winding
3344 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3345 mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
3346 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3347 mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
3348 mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
3349 mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
3355 // Only create the background actor if there are glyphs with background color
3356 if ( mesh.mVertices.Count() > 0u )
3358 Property::Map quadVertexFormat;
3359 quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
3360 quadVertexFormat[ "aColor" ] = Property::VECTOR4;
3362 PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
3363 quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
3365 Geometry quadGeometry = Geometry::New();
3366 quadGeometry.AddVertexBuffer( quadVertices );
3367 quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
3369 if( !mShaderBackground )
3371 mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
3374 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
3375 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
3376 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
3378 actor = Actor::New();
3379 actor.SetName( "TextBackgroundColorActor" );
3380 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
3381 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3382 actor.SetSize( textSize );
3383 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
3384 actor.AddRenderer( renderer );
3393 } // namespace Toolkit