2 * Copyright (c) 2014 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/controls/text-input/text-input-impl.h>
25 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/key-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/type-registry-helper.h>
32 #include <dali/public-api/object/property-notification.h>
33 #include <dali/integration-api/debug.h>
34 #include <dali/public-api/images/resource-image.h>
37 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
38 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
39 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
40 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
48 #if defined(DEBUG_ENABLED)
49 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
52 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
53 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
54 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
55 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
56 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
57 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
59 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
60 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
61 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
62 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
63 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
65 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
66 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
67 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
68 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
69 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
71 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
72 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
73 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
74 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
75 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
76 const float CURSOR_THICKNESS(4.0f);
77 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
78 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
80 const char* const NEWLINE = "\n";
82 const TextStyle DEFAULT_TEXT_STYLE;
84 const unsigned int SCROLL_TICK_INTERVAL = 50u;
85 const float SCROLL_THRESHOLD = 10.f;
86 const float SCROLL_SPEED = 15.f;
89 * Selection state enumeration (FSM)
93 SelectionNone, ///< Currently not encountered selected section.
94 SelectionStarted, ///< Encountered selected section
95 SelectionFinished ///< Finished selected section
98 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
100 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
104 if( ( *it ).mIsVisible )
106 return --cursorPosition;
115 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
117 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
119 if( ( *it ).mIsVisible )
121 return cursorPosition;
127 return cursorPosition;
131 * Whether the given position plus the cursor size offset is inside the given boundary.
133 * @param[in] position The given position.
134 * @param[in] cursorSize The cursor size.
135 * @param[in] controlSize The given boundary.
137 * @return whether the given position is inside the given boundary.
139 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
141 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
142 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
143 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
144 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
148 * Splits a text in two halves.
150 * If the text's number of characters is odd, firstHalf has one more character.
152 * @param[in] text The text to be split.
153 * @param[out] firstHalf The first half of the text.
154 * @param[out] secondHalf The second half of the text.
156 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
157 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
158 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
163 const std::size_t textLength = text.size();
164 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
166 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
167 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
170 } // end of namespace
181 namespace // Unnamed namespace
186 return Toolkit::TextInput::New();
189 // Setup properties, signals and actions using the type-registry.
190 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextInput, Toolkit::Control, Create )
192 DALI_PROPERTY_REGISTRATION( TextInput, "highlight-color", VECTOR4, HIGHLIGHT_COLOR )
193 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-bg-color", VECTOR4, CUT_AND_PASTE_COLOR )
194 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-pressed-color", VECTOR4, CUT_AND_PASTE_PRESSED_COLOR )
195 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-border-color", VECTOR4, CUT_AND_PASTE_BORDER_COLOR )
196 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-icon-color", VECTOR4, CUT_AND_PASTE_ICON_COLOR )
197 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-icon-pressed-color", VECTOR4, CUT_AND_PASTE_ICON_PRESSED_COLOR )
198 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-text-color", VECTOR4, CUT_AND_PASTE_TEXT_COLOR )
199 DALI_PROPERTY_REGISTRATION( TextInput, "cut-and-paste-text-pressed-color", VECTOR4, CUT_AND_PASTE_TEXT_PRESSED_COLOR )
200 DALI_PROPERTY_REGISTRATION( TextInput, "cut-button-position-priority", UNSIGNED_INTEGER, CUT_BUTTON_POSITION_PRIORITY )
201 DALI_PROPERTY_REGISTRATION( TextInput, "copy-button-position-priority", UNSIGNED_INTEGER, COPY_BUTTON_POSITION_PRIORITY )
202 DALI_PROPERTY_REGISTRATION( TextInput, "paste-button-position-priority", UNSIGNED_INTEGER, PASTE_BUTTON_POSITION_PRIORITY )
203 DALI_PROPERTY_REGISTRATION( TextInput, "select-button-position-priority", UNSIGNED_INTEGER, SELECT_BUTTON_POSITION_PRIORITY )
204 DALI_PROPERTY_REGISTRATION( TextInput, "select-all-button-position-priority", UNSIGNED_INTEGER, SELECT_ALL_BUTTON_POSITION_PRIORITY )
205 DALI_PROPERTY_REGISTRATION( TextInput, "clipboard-button-position-priority", UNSIGNED_INTEGER, CLIPBOARD_BUTTON_POSITION_PRIORITY )
206 DALI_PROPERTY_REGISTRATION( TextInput, "popup-offset-from-text", VECTOR4, POP_UP_OFFSET_FROM_TEXT )
207 DALI_PROPERTY_REGISTRATION( TextInput, "cursor-color", VECTOR4, CURSOR_COLOR )
209 DALI_SIGNAL_REGISTRATION( TextInput, "start-input", SIGNAL_START_INPUT )
210 DALI_SIGNAL_REGISTRATION( TextInput, "end-input", SIGNAL_END_INPUT )
211 DALI_SIGNAL_REGISTRATION( TextInput, "style-changed", SIGNAL_STYLE_CHANGED )
212 DALI_SIGNAL_REGISTRATION( TextInput, "max-input-characters-reached", SIGNAL_MAX_INPUT_CHARACTERS_REACHED )
213 DALI_SIGNAL_REGISTRATION( TextInput, "toolbar-displayed", SIGNAL_TOOLBAR_DISPLAYED )
214 DALI_SIGNAL_REGISTRATION( TextInput, "text-exceed-boundaries", SIGNAL_TEXT_EXCEED_BOUNDARIES )
216 DALI_TYPE_REGISTRATION_END()
220 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
222 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
224 QuadCoordinates quad(x1, y1, x2, y2);
225 mQuadList.push_back( quad );
228 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
230 for(std::size_t i = 0;i < mQuadList.size(); i++)
232 QuadCoordinates& quad = mQuadList[i];
234 quad.min.Clamp(min, max);
235 quad.max.Clamp(min, max);
239 // [TextInput] ////////////////////////////////////////////////////////////////
241 Dali::Toolkit::TextInput TextInput::New()
243 // Create the implementation
244 TextInputPtr textInput(new TextInput());
245 // Pass ownership to CustomActor via derived handle
246 Dali::Toolkit::TextInput handle(*textInput);
247 handle.SetName( "TextInput");
249 textInput->Initialize();
253 TextInput::TextInput()
254 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
259 mDisplayedTextView(),
260 mStyledPlaceHolderText(),
261 mMaxStringLength( DEFAULT_MAX_SIZE ),
262 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
263 mCursorPosition( 0 ),
264 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
265 mIsSelectionHandleOneFlipped( false ),
266 mIsSelectionHandleTwoFlipped( false ),
267 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
268 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
269 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
270 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
271 mSelectionHandleOnePosition( 0 ),
272 mSelectionHandleTwoPosition( 0 ),
274 mPreEditStartPosition( 0 ),
275 mPreEditLength ( 0 ),
276 mNumberOfSurroundingCharactersDeleted( 0 ),
277 mTouchStartTime( 0 ),
279 mCurrentCopySelecton(),
282 mScrollDisplacement(),
283 mCurrentHandlePosition(),
284 mCurrentSelectionId(),
285 mCurrentSelectionHandlePosition(),
286 mRequestedSelection( 0, 0 ),
287 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
288 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
290 mMaterialColor( LIGHTBLUE ),
291 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
292 mOverrideAutomaticAlignment( false ),
293 mCursorRTLEnabled( false ),
294 mClosestCursorPositionEOL ( false ),
295 mCursorBlinkStatus( true ),
296 mCursorVisibility( false ),
297 mGrabHandleVisibility( false ),
298 mIsCursorInScrollArea( true ),
299 mIsGrabHandleInScrollArea( true ),
300 mEditModeActive( false ),
301 mEditOnTouch( true ),
302 mTextSelection( true ),
303 mExceedEnabled( true ),
304 mGrabHandleEnabled( true ),
305 mIsSelectionHandleFlipEnabled( true ),
306 mPreEditFlag( false ),
307 mIgnoreCommitFlag( false ),
308 mIgnoreFirstCommitFlag( false ),
309 mSelectingText( false ),
310 mPreserveCursorPosition( false ),
311 mSelectTextOnCommit( false ),
312 mUnderlinedPriorToPreEdit ( false ),
313 mCommitByKeyInput( false ),
314 mPlaceHolderSet( false ),
315 mMarkUpEnabled( false )
317 // Updates the line height accordingly with the input style.
321 TextInput::~TextInput()
323 StopCursorBlinkTimer();
328 std::string TextInput::GetText() const
332 // Return text-view's text only if the text-input's text is not empty
333 // in order to not to return the placeholder text.
334 if( !mStyledText.empty() )
336 text = mDisplayedTextView.GetText();
342 std::string TextInput::GetMarkupText() const
344 std::string markupString;
345 MarkupProcessor::GetMarkupString( mStyledText, markupString );
350 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
352 mDisplayedTextView.SetText( stylePlaceHolderText );
353 mPlaceHolderSet = true;
354 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
357 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
359 // Get the placeholder styled text array from the markup string.
360 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
361 if( mStyledText.empty() )
363 ShowPlaceholderText( mStyledPlaceHolderText );
367 std::string TextInput::GetPlaceholderText()
369 // Traverses the styled placeholder array getting only the text.
370 // Note that for some languages a 'character' could be represented by more than one 'char'
372 std::string placeholderText;
373 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
375 placeholderText.append( (*it).mText.GetText() );
378 return placeholderText ;
381 void TextInput::SetInitialText(const std::string& initialText)
383 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
385 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
387 mPreEditFlag = false;
388 mIgnoreCommitFlag = true;
391 SetText( initialText );
392 PreEditReset( false ); // Reset keyboard as text changed
395 void TextInput::SetText(const std::string& initialText)
397 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
399 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
401 if( mStyledText.empty() )
403 ShowPlaceholderText( mStyledPlaceHolderText );
407 mDisplayedTextView.SetText( mStyledText );
408 mPlaceHolderSet = false;
413 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
415 ImfManager imfManager = ImfManager::Get();
418 imfManager.SetCursorPosition( mCursorPosition );
419 imfManager.SetSurroundingText( initialText );
420 imfManager.NotifyCursorPosition();
423 if( IsScrollEnabled() )
425 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
428 ShowGrabHandleAndSetVisibility( false );
437 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
439 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
441 mDisplayedTextView.SetText( styleText );
442 mPlaceHolderSet = false;
444 // If text alignment hasn't been manually set by application developer, then we
445 // automatically determine the alignment based on the content of the text i.e. what
446 // language the text begins with.
447 // TODO: This should determine different alignments for each line (broken by '\n') of text.
448 if(!mOverrideAutomaticAlignment)
450 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
451 bool leftToRight(true);
453 if( !styleText.empty() )
455 bool breakOut(false);
457 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
459 const Text& text = textIter->mText;
461 for( std::size_t i = 0; i < text.GetLength(); ++i )
463 Character character( text[i] );
464 if( character.GetCharacterDirection() != Character::Neutral )
466 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
474 // Based on this direction, either left or right align text if not manually set by application developer.
475 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
476 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
477 Toolkit::Alignment::VerticalTop ) );
478 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
484 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
486 mMaxStringLength = maxChars;
489 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
491 DALI_ASSERT_DEBUG( maxLines > 0 )
495 mNumberOflinesLimit = maxLines;
499 std::size_t TextInput::GetNumberOfLinesLimit() const
501 return mNumberOflinesLimit;
504 std::size_t TextInput::GetNumberOfCharacters() const
506 return mStyledText.size();
510 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
512 mMaterialColor = color;
513 if ( mCustomMaterial )
515 mCustomMaterial.SetDiffuseColor( mMaterialColor );
516 mMeshData.SetMaterial( mCustomMaterial );
520 const Vector4& TextInput::GetMaterialDiffuseColor() const
522 return mMaterialColor;
527 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
529 return mInputStartedSignal;
532 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
534 return mInputFinishedSignal;
537 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
539 return mCutAndPasteToolBarDisplayed;
542 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
544 return mStyleChangedSignal;
547 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
549 return mTextModifiedSignal;
552 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
554 return mMaxInputCharactersReachedSignal;
557 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
559 return mInputTextExceedBoundariesSignal;
562 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
564 Dali::BaseHandle handle( object );
566 bool connected( true );
567 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( handle );
569 if( 0 == strcmp( signalName.c_str(), SIGNAL_START_INPUT ) )
571 textInput.InputStartedSignal().Connect( tracker, functor );
573 else if( 0 == strcmp( signalName.c_str(), SIGNAL_END_INPUT ) )
575 textInput.InputFinishedSignal().Connect( tracker, functor );
577 else if( 0 == strcmp( signalName.c_str(), SIGNAL_STYLE_CHANGED ) )
579 textInput.StyleChangedSignal().Connect( tracker, functor );
581 else if( 0 == strcmp( signalName.c_str(), SIGNAL_MAX_INPUT_CHARACTERS_REACHED ) )
583 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
585 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TOOLBAR_DISPLAYED ) )
587 textInput.CutAndPasteToolBarDisplayedSignal().Connect( tracker, functor );
589 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TEXT_EXCEED_BOUNDARIES ) )
591 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
595 // signalName does not match any signal
602 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
606 // update line height before calculate the actual position.
611 if( setCursorOnTouchPoint )
613 // Sets the cursor position for the given touch point.
614 ReturnClosestIndex( touchPoint, mCursorPosition );
616 // Creates the grab handle.
617 if( IsGrabHandleEnabled() )
619 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
623 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
624 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
625 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
626 ShowGrabHandleAndSetVisibility( true );
628 // Scrolls the text-view if needed.
629 if( IsScrollEnabled() )
631 ScrollTextViewToMakeCursorVisible( cursorPosition );
637 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
649 bool TextInput::IsEditable() const
651 return mEditModeActive;
654 void TextInput::SetEditOnTouch( bool editOnTouch )
656 mEditOnTouch = editOnTouch;
659 bool TextInput::IsEditOnTouch() const
664 void TextInput::SetTextSelectable( bool textSelectable )
666 mTextSelection = textSelectable;
669 bool TextInput::IsTextSelectable() const
671 return mTextSelection;
674 bool TextInput::IsTextSelected() const
676 return mHighlightMeshActor;
679 void TextInput::DeSelectText()
686 void TextInput::SetGrabHandleImage(Dali::Image image )
690 CreateGrabHandle(image);
694 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
696 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
700 mCursor.SetImage( image );
701 mCursor.SetNinePatchBorder( border );
705 Vector3 TextInput::GetSelectionHandleSize()
707 return DEFAULT_SELECTION_HANDLE_SIZE;
710 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
712 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
716 mCursorRTL.SetImage( image);
717 mCursorRTL.SetNinePatchBorder( border );
721 void TextInput::EnableGrabHandle(bool toggle)
723 // enables grab handle with will in turn de-activate magnifier
724 mGrabHandleEnabled = toggle;
727 bool TextInput::IsGrabHandleEnabled()
729 // if false then magnifier will be shown instead.
730 return mGrabHandleEnabled;
733 void TextInput::EnableSelectionHandleFlip( bool toggle )
735 // Deprecated function. To be removed.
736 mIsSelectionHandleFlipEnabled = toggle;
739 bool TextInput::IsSelectionHandleFlipEnabled()
741 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
745 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
747 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
748 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
749 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
751 mSelectionHandleFlipMargin = margin;
754 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
756 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
757 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
759 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
760 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
762 const Vector4 boundary( originX,
764 originX + boundingRectangle.width,
765 originY + boundingRectangle.height );
767 mBoundingRectangleWorldCoordinates = boundary;
770 const Rect<float> TextInput::GetBoundingRectangle() const
772 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
774 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
775 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
777 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
782 const Vector4& TextInput::GetSelectionHandleFlipMargin()
784 return mSelectionHandleFlipMargin;
787 void TextInput::SetTextColor( const Vector4& color )
789 mDisplayedTextView.SetColor( color );
792 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
794 if( style != mInputStyle )
797 bool emitSignal = false;
799 // mask: modify style according to mask, if different emit signal.
800 const TextStyle oldInputStyle( mInputStyle );
802 // Copy the new style.
803 mInputStyle.Copy( style, mask );
805 // if style has changed, emit signal.
806 if( oldInputStyle != mInputStyle )
811 // Updates the line height accordingly with the input style.
814 // Changing font point size will require the cursor to be re-sized
819 EmitStyleChangedSignal();
824 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
826 if ( IsTextSelected() )
828 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
829 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
831 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
833 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
836 // Keeps the old style to be compared with the new one.
837 const TextStyle oldInputStyle( mInputStyle );
839 // Copy only those parameters from the style which are set in the mask.
840 mInputStyle.Copy( style, mask );
842 if( mInputStyle != oldInputStyle )
844 // Updates the line height accordingly with the input style.
847 EmitStyleChangedSignal();
852 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
854 if( !mStyledText.empty() )
856 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
860 TextStyle TextInput::GetStyleAtCursor() const
864 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
866 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
867 style = mStyledText.at( mCursorPosition-1 ).mStyle;
873 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
875 Dali::Font defaultFont = Dali::Font::New();
876 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
883 TextStyle TextInput::GetStyleAt( std::size_t position ) const
885 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
887 if( position >= mStyledText.size() )
889 position = mStyledText.size() - 1;
892 return mStyledText.at( position ).mStyle;
895 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
897 mDisplayedTextView.SetTextAlignment( align );
898 mOverrideAutomaticAlignment = true;
901 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
903 mDisplayedTextView.SetLineJustification( justification );
904 mOverrideAutomaticAlignment = true;
907 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
909 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
912 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
914 return mDisplayedTextView.GetFadeBoundary();
917 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
919 return mDisplayedTextView.GetTextAlignment();
922 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
924 mDisplayedTextView.SetMultilinePolicy( policy );
927 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
929 return mDisplayedTextView.GetMultilinePolicy();
932 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
934 mDisplayedTextView.SetWidthExceedPolicy( policy );
937 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
939 return mDisplayedTextView.GetWidthExceedPolicy();
942 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
944 mDisplayedTextView.SetHeightExceedPolicy( policy );
947 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
949 return mDisplayedTextView.GetHeightExceedPolicy();
952 void TextInput::SetExceedEnabled( bool enable )
954 mExceedEnabled = enable;
957 bool TextInput::GetExceedEnabled() const
959 return mExceedEnabled;
962 void TextInput::SetBackground(Dali::Image image )
964 // TODO Should add this function and add public api to match.
967 bool TextInput::OnTouchEvent(const TouchEvent& event)
972 bool TextInput::OnKeyEvent(const KeyEvent& event)
974 switch( event.state )
978 return OnKeyDownEvent(event);
984 return OnKeyUpEvent(event);
996 void TextInput::OnKeyInputFocusGained()
998 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1000 mEditModeActive = true;
1002 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1004 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1006 // Updates the line height accordingly with the input style.
1009 // Connect the signals to use in text input.
1010 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1011 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1013 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1016 GetTextLayoutInfo();
1019 SetCursorVisibility( true );
1020 StartCursorBlinkTimer();
1022 Toolkit::TextInput handle( GetOwner() );
1023 mInputStartedSignal.Emit( handle );
1025 ImfManager imfManager = ImfManager::Get();
1029 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1031 // Notify that the text editing start.
1032 imfManager.Activate();
1034 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1035 imfManager.SetRestoreAfterFocusLost( true );
1037 imfManager.SetCursorPosition( mCursorPosition );
1038 imfManager.NotifyCursorPosition();
1041 mClipboard = Clipboard::Get(); // Store handle to clipboard
1043 // Now in edit mode we can accept string to paste from clipboard
1044 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1047 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1051 void TextInput::OnKeyInputFocusLost()
1053 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1057 // If key input focus is lost, it removes the
1058 // underline from the last pre-edit text.
1059 RemovePreEditStyle();
1060 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1061 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1065 ImfManager imfManager = ImfManager::Get();
1068 // The text editing is finished. Therefore the imf manager don't have restore activation.
1069 imfManager.SetRestoreAfterFocusLost( false );
1071 // Notify that the text editing finish.
1072 imfManager.Deactivate();
1074 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1076 // Disconnect signal used the text input.
1077 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1079 Toolkit::TextInput handle( GetOwner() );
1080 mInputFinishedSignal.Emit( handle );
1081 mEditModeActive = false;
1082 mPreEditFlag = false;
1084 SetCursorVisibility( false );
1085 StopCursorBlinkTimer();
1087 ShowGrabHandleAndSetVisibility( false );
1090 // No longer in edit mode so do not want to receive string from clipboard
1091 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1094 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1097 Clipboard clipboard = Clipboard::Get();
1100 clipboard.HideClipboard();
1104 void TextInput::OnControlStageConnection()
1106 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1108 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1110 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1114 void TextInput::CreateActiveLayer()
1116 Actor self = Self();
1117 mActiveLayer = Layer::New();
1118 mActiveLayer.SetName ( "ActiveLayerActor" );
1120 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1121 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1122 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1124 self.Add( mActiveLayer );
1125 mActiveLayer.RaiseToTop();
1128 void TextInput::OnInitialize()
1130 CreateTextViewActor();
1134 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1135 // different positions depending on language)
1136 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1137 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1139 Actor self = Self();
1140 self.Add( mCursor );
1141 self.Add( mCursorRTL );
1143 mCursorVisibility = false;
1145 CreateActiveLayer(); // todo move this so layer only created when needed.
1147 // Assign names to image actors
1148 mCursor.SetName("mainCursor");
1149 mCursorRTL.SetName("rtlCursor");
1152 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1154 mDisplayedTextView.SetSize( targetSize );
1155 GetTextLayoutInfo();
1156 mActiveLayer.SetSize(targetSize);
1159 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1161 Relayout( mDisplayedTextView, size, container );
1162 Relayout( mPopupPanel.GetRootActor(), size, container );
1164 GetTextLayoutInfo();
1169 Vector3 TextInput::GetNaturalSize()
1171 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1173 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1175 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1176 naturalSize.height = mLineHeight;
1182 float TextInput::GetHeightForWidth( float width )
1184 float height = mDisplayedTextView.GetHeightForWidth( width );
1186 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1188 // If the height is zero, it means there is no text. Let's return the cursor height.
1189 height = mLineHeight;
1195 /*end of Virtual methods from parent*/
1197 // Private Internal methods
1199 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1201 switch (gesture.state)
1203 case Gesture::Started:
1204 // fall through so code not duplicated
1205 case Gesture::Continuing:
1207 if (actor == mGrabArea)
1209 SetCursorVisibility( true );
1210 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1211 MoveGrabHandle( gesture.displacement );
1212 HidePopup(); // Do not show popup whilst handle is moving
1214 else if (actor == mHandleOneGrabArea)
1216 // the displacement in PanGesture is affected by the actor's rotation.
1217 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1218 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1220 MoveSelectionHandle( HandleOne, gesture.displacement );
1222 mState = StateDraggingHandle;
1225 else if (actor == mHandleTwoGrabArea)
1227 // the displacement in PanGesture is affected by the actor's rotation.
1228 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1229 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1231 MoveSelectionHandle( HandleTwo, gesture.displacement );
1233 mState = StateDraggingHandle;
1239 case Gesture::Finished:
1241 // Revert back to non-pressed selection handle images
1242 if (actor == mGrabArea)
1244 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1245 SetCursorVisibility( true );
1246 SetUpPopupSelection();
1249 if (actor == mHandleOneGrabArea)
1251 // the displacement in PanGesture is affected by the actor's rotation.
1252 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1253 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1255 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1257 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1259 ShowPopupCutCopyPaste();
1261 if (actor == mHandleTwoGrabArea)
1263 // the displacement in PanGesture is affected by the actor's rotation.
1264 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1265 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1267 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1269 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1271 ShowPopupCutCopyPaste();
1280 // Stop the flashing animation so easy to see when moved.
1281 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1283 if (touch.GetPoint(0).state == TouchPoint::Down)
1285 SetCursorVisibility( true );
1286 StopCursorBlinkTimer();
1288 else if (touch.GetPoint(0).state == TouchPoint::Up)
1290 SetCursorVisibility( true );
1291 StartCursorBlinkTimer();
1296 // selection handle one
1297 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1299 if (touch.GetPoint(0).state == TouchPoint::Down)
1301 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1303 else if (touch.GetPoint(0).state == TouchPoint::Up)
1305 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1310 // selection handle two
1311 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1313 if (touch.GetPoint(0).state == TouchPoint::Down)
1315 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1317 else if (touch.GetPoint(0).state == TouchPoint::Up)
1319 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1324 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1326 // If text exists then select nearest word.
1327 if ( !mStyledText.empty())
1331 ShowGrabHandleAndSetVisibility( false );
1336 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1337 // converts the pre-edit word being displayed to a committed word.
1338 if ( !mUnderlinedPriorToPreEdit )
1341 style.SetUnderline( false );
1342 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1344 mPreEditFlag = false;
1345 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1346 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1347 PreEditReset( false );
1349 mCursorPosition = 0;
1351 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1352 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1354 std::size_t start = 0;
1355 std::size_t end = 0;
1356 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1358 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1360 ImfManager imfManager = ImfManager::Get();
1363 imfManager.SetCursorPosition ( mCursorPosition );
1364 imfManager.NotifyCursorPosition();
1367 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1369 SelectText( start, end );
1370 ShowPopupCutCopyPaste();
1374 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1375 HidePopup( false ); // Hide popup with setting to do auto show.
1376 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1380 else if ( mClipboard && mClipboard.NumberOfItems() )
1382 ShowPopupCutCopyPaste();
1385 // If no text and clipboard empty then do nothing
1388 // TODO: Change the function name to be more general.
1389 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1391 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1392 , (mEditOnTouch)?"true":"false"
1393 , (mEditModeActive)?"true":"false");
1395 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1400 if( mGrabArea == actor )
1402 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1404 SetUpPopupSelection();
1414 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1416 // Initially don't create the grab handle.
1417 bool createGrabHandle = false;
1419 if ( !mEditModeActive )
1421 // update line height before calculate the actual position.
1424 // Only start edit mode if TextInput configured to edit on touch
1427 // Set the initial cursor position in the tap point.
1428 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1434 // Show the keyboard if it was hidden.
1435 if (!VirtualKeyboard::IsVisible())
1437 VirtualKeyboard::Show();
1440 // Reset keyboard as tap event has occurred.
1441 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1442 PreEditReset( true );
1444 GetTextLayoutInfo();
1446 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1448 // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1450 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1452 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1454 // Notify keyboard so it can 're-capture' word for predictive text.
1455 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1456 ImfManager imfManager = ImfManager::Get();
1459 imfManager.SetCursorPosition ( mCursorPosition );
1460 imfManager.NotifyCursorPosition();
1462 const TextStyle oldInputStyle( mInputStyle );
1464 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1468 // Create the grab handle.
1469 // Grab handle is created later.
1470 createGrabHandle = true;
1472 if( oldInputStyle != mInputStyle )
1474 // Updates the line height accordingly with the input style.
1477 EmitStyleChangedSignal();
1482 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1483 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1484 // otherwise the Grab handle will be shown when selecting.
1485 if ( createGrabHandle && IsGrabHandleEnabled() )
1487 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1488 bool altPositionValid; // Alternate cursor validity flag.
1489 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1490 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1492 if( altPositionValid )
1494 // Check which of the positions is the closest.
1495 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1497 cursorPosition = altPosition;
1503 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1504 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1505 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1506 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1511 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1513 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1515 // Ignore longpress if in selection mode already
1516 if( mHighlightMeshActor )
1521 if(longPress.state == Dali::Gesture::Started)
1523 // Start edit mode on long press
1524 if ( !mEditModeActive )
1529 // If text exists then select nearest word.
1530 if ( !mStyledText.empty())
1534 ShowGrabHandleAndSetVisibility( false );
1539 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1540 // converts the pre-edit word being displayed to a committed word.
1541 if ( !mUnderlinedPriorToPreEdit )
1544 style.SetUnderline( false );
1545 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1547 mPreEditFlag = false;
1548 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1549 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1550 PreEditReset( false );
1552 mCursorPosition = 0;
1554 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1555 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1557 std::size_t start = 0;
1558 std::size_t end = 0;
1559 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1561 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1563 ImfManager imfManager = ImfManager::Get();
1566 imfManager.SetCursorPosition ( mCursorPosition );
1567 imfManager.NotifyCursorPosition();
1570 SelectText( start, end );
1573 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1574 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1576 ShowPopupCutCopyPaste();
1581 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1583 const Text clipboardText( notifier.GetContent() );
1584 PasteText( clipboardText );
1586 SetCursorVisibility( true );
1587 StartCursorBlinkTimer();
1589 ShowGrabHandleAndSetVisibility( false );
1595 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1597 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1599 const std::string& name = button.GetName();
1601 if(name == TextInputPopup::OPTION_SELECT_WORD)
1603 std::size_t start = 0;
1604 std::size_t end = 0;
1605 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1607 SelectText( start, end );
1609 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1611 SetCursorVisibility(false);
1612 StopCursorBlinkTimer();
1614 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1615 std::size_t start = 0;
1617 SelectText( start, end );
1619 else if(name == TextInputPopup::OPTION_CUT)
1621 bool ret = CopySelectedTextToClipboard();
1625 DeleteHighlightedText( true );
1629 SetCursorVisibility( true );
1630 StartCursorBlinkTimer();
1634 else if(name == TextInputPopup::OPTION_COPY)
1636 CopySelectedTextToClipboard();
1640 SetCursorVisibility( true );
1641 StartCursorBlinkTimer();
1645 else if(name == TextInputPopup::OPTION_PASTE)
1647 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1649 PasteText(retrievedString);
1651 SetCursorVisibility( true );
1652 StartCursorBlinkTimer();
1654 ShowGrabHandleAndSetVisibility( false );
1658 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1660 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1661 // Hence pass the false parameter for signalFinished.
1662 HidePopup( true, false );
1663 mClipboard.ShowClipboard();
1669 bool TextInput::OnCursorBlinkTimerTick()
1672 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1673 if ( mCursorRTLEnabled )
1675 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1677 mCursorBlinkStatus = !mCursorBlinkStatus;
1682 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1684 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1686 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1687 if(mHighlightMeshActor && mState == StateEdit)
1689 ShowPopupCutCopyPaste();
1693 //FIXME this routine needs to be re-written as it contains too many branches.
1694 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1696 std::string keyName = event.keyPressedName;
1697 std::string keyString = event.keyPressed;
1699 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1701 // Do not consume "Tab" and "Escape" keys.
1702 if(keyName == "Tab" || keyName == "Escape")
1704 // Escape key to end the edit mode
1710 HidePopup(); // If Pop-up shown then hides it as editing text.
1712 // Update Flag, indicates whether to update the text-input contents or not.
1713 // Any key stroke that results in a visual change of the text-input should
1714 // set this flag to true.
1717 // Whether to scroll text to cursor position.
1718 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1719 bool scroll = false;
1721 if (keyName == "Return")
1723 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1725 bool preEditFlagPreviouslySet( mPreEditFlag );
1727 // replaces highlighted text with new line
1728 DeleteHighlightedText( false );
1730 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1732 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1733 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1736 mCommitByKeyInput = true;
1739 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1740 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1742 mPreEditFlag = true;
1743 mIgnoreCommitFlag = false;
1753 else if ( keyName == "space" )
1755 if ( mHighlightMeshActor )
1757 // Some text is selected so erase it before adding space.
1758 DeleteHighlightedText( true );
1761 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1763 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1764 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1767 mCommitByKeyInput = true;
1772 else if (keyName == "BackSpace")
1774 if ( mHighlightMeshActor )
1776 // Some text is selected so erase it
1777 DeleteHighlightedText( true );
1782 if ( mCursorPosition > 0 )
1784 DeleteCharacter( mCursorPosition );
1790 else if (keyName == "Right")
1795 else if (keyName == "Left")
1797 AdvanceCursor(true);
1800 else // event is a character
1802 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1803 if ( !keyString.empty() )
1805 // replaces highlighted text with new character
1806 DeleteHighlightedText( false );
1808 // Received key String
1809 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1815 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1816 // as this is a costly operation.
1822 if(update || scroll)
1824 if( IsScrollEnabled() )
1826 // Calculates the new cursor position (in actor coordinates)
1827 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1829 ScrollTextViewToMakeCursorVisible( cursorPosition );
1836 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1838 std::string keyName = event.keyPressedName;
1839 std::string keyString = event.keyPressed;
1841 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1843 // The selected text become deselected when the key code is DALI_KEY_BACK.
1844 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1853 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1854 const Vector3& cursorPositionTwo,
1855 bool altPositionValidOne,
1856 bool altPositionValidTwo,
1857 const Vector3& altPositionOne,
1858 const Vector3& altPositionTwo )
1860 // TODO VCC Valid for one line.
1861 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1862 if( cursorPositionOne != cursorPositionTwo )
1864 if( cursorPositionOne.x < cursorPositionTwo.x )
1866 mSelectionHandleOneActualPosition = cursorPositionOne;
1867 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1871 mSelectionHandleOneActualPosition = cursorPositionTwo;
1872 mSelectionHandleTwoActualPosition = cursorPositionOne;
1877 mSelectionHandleOneActualPosition = cursorPositionOne;
1878 if( altPositionValidOne )
1880 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1882 mSelectionHandleOneActualPosition = altPositionOne;
1885 if( altPositionValidTwo )
1887 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1889 mSelectionHandleOneActualPosition = altPositionTwo;
1893 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1894 if( altPositionValidTwo )
1896 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1898 mSelectionHandleTwoActualPosition = altPositionTwo;
1901 if( altPositionValidOne )
1903 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1905 mSelectionHandleTwoActualPosition = altPositionOne;
1911 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1913 // Updates the stored scroll position.
1914 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1916 const Vector3& controlSize = GetControlSize();
1917 Size cursorSize( CURSOR_THICKNESS, 0.f );
1919 // Updates the cursor and grab handle position and visibility.
1920 if( mGrabHandle || mCursor )
1922 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1924 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1925 bool altPositionValid; // Alternate cursor validity flag.
1926 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1927 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1929 if( altPositionValid )
1931 // Check which of the positions is the closest.
1932 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1934 cursorPosition = altPosition;
1938 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1940 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1944 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1945 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1950 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1951 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1955 // Updates the selection handles and highlighted text position and visibility.
1956 if( mSelectionHandleOne && mSelectionHandleTwo )
1958 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1959 bool altPositionValidOne; // Alternate cursor validity flag.
1960 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1961 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1963 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1964 bool altPositionValidTwo; // Alternate cursor validity flag.
1965 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1966 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1968 // VCC TODO: This method is a hack for one line.
1969 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1971 altPositionValidOne,
1972 altPositionValidTwo,
1976 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1977 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1978 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1979 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1981 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1982 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1983 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1984 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1986 if( mHighlightMeshActor )
1988 mHighlightMeshActor.SetVisible( true );
1994 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1996 // Scroll the text to make the cursor visible.
1997 const Size cursorSize( CURSOR_THICKNESS,
1998 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2000 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2002 const Vector3& controlSize = GetControlSize();
2004 // Calculates the new scroll position.
2005 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2006 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2008 scrollOffset.x += cursorPosition.x;
2011 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2013 scrollOffset.y += cursorPosition.y;
2016 // Sets the new scroll position.
2017 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2018 SetScrollPosition( scrollOffset );
2021 void TextInput::StartScrollTimer()
2025 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2026 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2029 if( !mScrollTimer.IsRunning() )
2031 mScrollTimer.Start();
2035 void TextInput::StopScrollTimer()
2039 mScrollTimer.Stop();
2043 bool TextInput::OnScrollTimerTick()
2045 // TODO: need to set the new style accordingly the new handle position.
2047 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2049 // nothing to do if all handles are invisible or doesn't exist.
2055 // Choose between the grab handle or the selection handles.
2056 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2057 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2058 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2060 std::size_t newCursorPosition = 0;
2061 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2063 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2064 // the new selection handle's position needs to be different of the other one.
2065 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2066 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2067 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2069 if( differentSelectionHandles )
2071 handlePosition = newCursorPosition;
2073 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2075 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2077 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2078 scrollPosition += scrollDelta;
2079 SetScrollPosition( scrollPosition );
2081 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2086 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2089 actualHandlePosition.x += mScrollDisplacement.x;
2090 actualHandlePosition.y += mScrollDisplacement.y;
2095 // Public Internal Methods (public for testing purpose)
2097 void TextInput::SetUpTouchEvents()
2099 if ( !mTapDetector )
2101 mTapDetector = TapGestureDetector::New();
2102 // Attach the actors and connect the signal
2103 mTapDetector.Attach(Self());
2105 // As contains children which may register for tap the default control detector is not used.
2106 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2109 if ( !mDoubleTapDetector )
2111 mDoubleTapDetector = TapGestureDetector::New( 2 );
2112 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2114 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2115 // so that we do not, unnecessarily, have a double tap request all the time
2118 if ( !mPanGestureDetector )
2120 mPanGestureDetector = PanGestureDetector::New();
2121 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2124 if ( !mLongPressDetector )
2126 mLongPressDetector = LongPressGestureDetector::New();
2127 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2128 mLongPressDetector.Attach(Self());
2132 void TextInput::CreateTextViewActor()
2134 mDisplayedTextView = Toolkit::TextView::New();
2135 mDisplayedTextView.SetName( "DisplayedTextView ");
2136 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2137 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2138 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2139 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2140 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2141 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2142 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2143 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2144 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2145 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2147 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2149 Self().Add( mDisplayedTextView );
2152 // Start a timer to initiate, used by the cursor to blink.
2153 void TextInput::StartCursorBlinkTimer()
2155 if ( !mCursorBlinkTimer )
2157 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2158 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2161 if ( !mCursorBlinkTimer.IsRunning() )
2163 mCursorBlinkTimer.Start();
2167 // Start a timer to initiate, used by the cursor to blink.
2168 void TextInput::StopCursorBlinkTimer()
2170 if ( mCursorBlinkTimer )
2172 mCursorBlinkTimer.Stop();
2176 void TextInput::StartEditMode()
2178 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2180 if(!mEditModeActive)
2185 if ( mDoubleTapDetector )
2187 mDoubleTapDetector.Attach( Self() );
2191 void TextInput::EndEditMode()
2193 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2195 ClearKeyInputFocus();
2197 if ( mDoubleTapDetector )
2199 mDoubleTapDetector.Detach( Self() );
2203 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2205 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2207 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2209 style.SetUnderline( true );
2210 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2214 void TextInput::RemovePreEditStyle()
2216 if ( !mUnderlinedPriorToPreEdit )
2219 style.SetUnderline( false );
2220 SetActiveStyle( style, TextStyle::UNDERLINE );
2224 // IMF related methods
2227 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2229 bool update( false );
2230 bool preeditResetRequired ( false );
2232 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2234 HidePopup(); // If Pop-up shown then hides it as editing text.
2237 switch ( imfEvent.eventName )
2239 case ImfManager::PREEDIT:
2241 mIgnoreFirstCommitFlag = false;
2243 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2244 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2246 // replaces highlighted text with new character
2247 DeleteHighlightedText( false );
2250 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2252 if( IsScrollEnabled() )
2254 // Calculates the new cursor position (in actor coordinates)
2255 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2256 ScrollTextViewToMakeCursorVisible( cursorPosition );
2263 case ImfManager::COMMIT:
2265 if( mIgnoreFirstCommitFlag )
2267 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2268 mIgnoreFirstCommitFlag = false;
2272 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2274 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2275 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2277 // replaces highlighted text with new character
2278 DeleteHighlightedText( false );
2281 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2282 // not needed, one such scenario is when the pre-edit word is too long to fit.
2283 if ( !mIgnoreCommitFlag )
2285 update = CommitReceived( imfEvent.predictiveString );
2289 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2295 if( IsScrollEnabled() )
2297 // Calculates the new cursor position (in actor coordinates)
2298 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2300 ScrollTextViewToMakeCursorVisible( cursorPosition );
2305 case ImfManager::DELETESURROUNDING:
2307 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2308 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2310 mPreEditFlag = false;
2312 std::size_t toDelete = 0;
2313 std::size_t numberOfCharacters = 0;
2315 if( mHighlightMeshActor )
2317 // delete highlighted text.
2318 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2319 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2323 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2325 toDelete = mCursorPosition + imfEvent.cursorOffset;
2327 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2329 numberOfCharacters = mStyledText.size() - toDelete;
2333 numberOfCharacters = imfEvent.numberOfChars;
2336 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2337 DeleteRange( toDelete, numberOfCharacters );
2339 mCursorPosition = toDelete;
2340 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2344 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2347 case ImfManager::GETSURROUNDING:
2349 // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2350 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2351 if (! ( mHighlightMeshActor || mSelectingText ) )
2353 std::string text( GetText() );
2354 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2356 imfManager.SetCursorPosition( mCursorPosition );
2357 imfManager.SetSurroundingText( text );
2360 if( 0 != mNumberOfSurroundingCharactersDeleted )
2362 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2363 mNumberOfSurroundingCharactersDeleted = 0;
2365 if( mStyledText.empty() )
2367 ShowPlaceholderText( mStyledPlaceHolderText );
2372 case ImfManager::VOID:
2374 DALI_ASSERT_DEBUG( false );
2378 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2380 return callbackData;
2383 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2385 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2387 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2388 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2390 bool preeditResetRequest ( false );
2392 if( mPreEditFlag ) // Already in pre-edit state.
2394 if( mStyledText.size() >= mMaxStringLength )
2396 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2397 // Cannot fit these characters into field, clear pre-edit.
2398 if ( !mUnderlinedPriorToPreEdit )
2401 style.SetUnderline( false );
2402 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2404 mIgnoreCommitFlag = true;
2405 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2406 mPreEditFlag = false;
2407 EmitMaxInputCharactersReachedSignal();
2411 // delete existing pre-edit string
2412 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2414 // Store new pre-edit string
2415 mPreEditString.SetText( keyString );
2417 if ( keyString.empty() )
2419 mPreEditFlag = false;
2420 mCursorPosition = mPreEditStartPosition;
2422 if( mStyledText.empty() )
2424 ShowPlaceholderText( mStyledPlaceHolderText );
2428 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2431 GetTextLayoutInfo();
2436 // Insert new pre-edit string. InsertAt updates the size and position table.
2437 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2438 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2439 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2440 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2441 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2444 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2448 else // mPreEditFlag not set
2450 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2452 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2453 // new pre-edit so move into pre-edit state by setting flag
2454 mPreEditFlag = true;
2455 mPreEditString.SetText( keyString ); // store new pre-edit string
2456 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2457 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2458 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2459 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2460 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2461 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2462 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2468 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2472 return preeditResetRequest;
2475 bool TextInput::CommitReceived(const std::string& keyString )
2477 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2478 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2480 bool update( false );
2482 RemovePreEditStyle();
2484 const std::size_t styledTextSize( mStyledText.size() );
2485 if( styledTextSize >= mMaxStringLength )
2487 // Cannot fit these characters into field, clear pre-edit.
2490 mIgnoreCommitFlag = true;
2491 mPreEditFlag = false;
2493 EmitMaxInputCharactersReachedSignal();
2499 // delete existing pre-edit string
2500 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2501 mPreEditFlag = false;
2503 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2504 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2506 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2508 // No need to update cursor position as Cursor location given by touch.
2509 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2510 mPreserveCursorPosition = false;
2514 // Cursor not set by touch so needs to be re-positioned to input more text
2515 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2517 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2518 if ( mCommitByKeyInput )
2520 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2521 mCommitByKeyInput = false;
2527 if ( mSelectTextOnCommit )
2529 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2534 else // mPreEditFlag not set
2536 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2538 if( mStyledText.empty() && mPlaceHolderSet )
2540 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2541 mDisplayedTextView.SetText( "" );
2542 mNumberOfSurroundingCharactersDeleted = 0;
2543 mPlaceHolderSet = false;
2545 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2547 mNumberOfSurroundingCharactersDeleted = 0;
2552 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2557 mSelectTextOnCommit = false;
2559 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2560 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2565 // End of IMF related methods
2567 std::size_t TextInput::DeletePreEdit()
2569 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2571 DALI_ASSERT_DEBUG( mPreEditFlag );
2573 const std::size_t preEditStringLength = mPreEditString.GetLength();
2574 const std::size_t styledTextSize = mStyledText.size();
2576 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2578 // Prevents erase items outside mStyledText bounds.
2579 if( mPreEditStartPosition > styledTextSize )
2581 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2582 mPreEditStartPosition = styledTextSize;
2585 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2587 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2588 endPosition = styledTextSize;
2591 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2593 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2594 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2596 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2598 return preEditStringLength;
2601 void TextInput::PreEditReset( bool preserveCursorPosition )
2603 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2604 preserveCursorPosition, mCursorPosition);
2606 // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2607 mPreserveCursorPosition = preserveCursorPosition;
2609 // Reset incase we are in a pre-edit state.
2610 ImfManager imfManager = ImfManager::Get();
2613 imfManager.Reset(); // Will trigger a commit message
2617 void TextInput::CursorUpdate()
2621 ImfManager imfManager = ImfManager::Get();
2624 std::string text( GetText() );
2625 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2626 imfManager.SetCursorPosition ( mCursorPosition );
2627 imfManager.NotifyCursorPosition();
2631 /* Delete highlighted characters redisplay*/
2632 void TextInput::DeleteHighlightedText( bool inheritStyle )
2634 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2636 if( mHighlightMeshActor )
2638 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2640 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2641 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2643 // Get the styled text of the characters to be deleted as it may be needed if
2644 // the "exceed the text-input's boundaries" option is disabled.
2645 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2647 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2649 mStyledText.erase( start, end ); // erase range of characters
2651 // Remove text from TextView and update place holder text if required
2653 // Set the placeholder text only if the styled text is empty.
2654 if( mStyledText.empty() )
2656 ShowPlaceholderText( mStyledPlaceHolderText );
2660 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2662 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2664 // It may happen than after removing a white space or a new line character,
2665 // two words merge, this new word could be big enough to not fit in its
2666 // current line, so moved to the next one, and make some part of the text to
2667 // exceed the text-input's boundary.
2668 if( !mExceedEnabled )
2670 // Get the new text layout after removing some characters.
2671 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2673 // Get text-input's size.
2674 const Vector3& size = GetControlSize();
2676 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2677 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2679 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2681 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2682 styledCharactersToDelete.begin(),
2683 styledCharactersToDelete.end() );
2687 GetTextLayoutInfo();
2695 const TextStyle oldInputStyle( mInputStyle );
2697 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2699 if( oldInputStyle != mInputStyle )
2701 // Updates the line height accordingly with the input style.
2704 EmitStyleChangedSignal();
2710 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2712 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2713 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2715 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2718 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2720 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2721 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2723 mStyledText.erase(itStart, itEnd);
2725 // update the selection handles if they are visible.
2726 if( mHighlightMeshActor )
2728 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2729 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2731 if( minHandle >= start + ncharacters )
2733 minHandle -= ncharacters;
2735 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2740 if( maxHandle >= start + ncharacters )
2742 maxHandle -= ncharacters;
2744 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2750 // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2753 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2755 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2756 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2757 // Mean we do not re-draw the text more than we have too.
2760 /* Delete character at current cursor position and redisplay*/
2761 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2763 // Ensure positionToDelete is not out of bounds.
2764 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2765 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2766 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2768 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2771 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2773 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2775 // Get the styled text of the character to be deleted as it may be needed if
2776 // the "exceed the text-input's boundaries" option is disabled.
2777 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2779 mStyledText.erase(it); // erase the character left of positionToDelete
2781 if( mStyledText.empty() )
2783 ShowPlaceholderText( mStyledPlaceHolderText );
2787 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2789 const Character characterToDelete = styledCharacterToDelete.mText[0];
2791 // It may happen than after removing a white space or a new line character,
2792 // two words merge, this new word could be big enough to not fit in its
2793 // current line, so moved to the next one, and make some part of the text to
2794 // exceed the text-input's boundary.
2795 if( !mExceedEnabled )
2797 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2799 // Get the new text layout after removing one character.
2800 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2802 // Get text-input's size.
2803 const Vector3& size = GetControlSize();
2805 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2806 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2808 MarkupProcessor::StyledTextArray array;
2809 array.push_back( styledCharacterToDelete );
2810 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2812 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2817 GetTextLayoutInfo();
2819 ShowGrabHandleAndSetVisibility( false );
2821 mCursorPosition = positionToDelete -1;
2823 const TextStyle oldInputStyle( mInputStyle );
2825 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2827 if( oldInputStyle != mInputStyle )
2829 // Updates the line height accordingly with the input style.
2832 EmitStyleChangedSignal();
2837 /*Insert new character into the string and (optionally) redisplay text-input*/
2838 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2840 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2842 // Ensure insertionPosition is not out of bounds.
2843 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2845 bool textExceedsMaximunNumberOfCharacters = false;
2846 bool textExceedsBoundary = false;
2847 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2849 ShowGrabHandleAndSetVisibility( false );
2851 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2855 mIgnoreCommitFlag = true;
2856 mPreEditFlag = false;
2857 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2858 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2861 if( textExceedsMaximunNumberOfCharacters )
2863 EmitMaxInputCharactersReachedSignal();
2866 if( textExceedsBoundary )
2868 EmitInputTextExceedsBoundariesSignal();
2869 PreEditReset( false );
2873 return insertedStringLength;
2876 ImageActor TextInput::CreateCursor( const Vector4& color)
2879 cursor = CreateSolidColorActor(color);
2880 cursor.SetName( "Cursor" );
2882 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2883 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2884 cursor.SetVisible(false);
2889 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2891 // As cursor is not moving due to grab handle, handle should be hidden.
2892 ShowGrabHandleAndSetVisibility( false );
2894 bool cursorPositionChanged = false;
2897 if ( mCursorPosition >= places )
2899 mCursorPosition = mCursorPosition - places;
2900 cursorPositionChanged = true;
2905 if ((mCursorPosition + places) <= mStyledText.size())
2907 mCursorPosition = mCursorPosition + places;
2908 cursorPositionChanged = true;
2912 if( cursorPositionChanged )
2914 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2916 const TextStyle oldInputStyle( mInputStyle );
2917 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2921 if( oldInputStyle != mInputStyle )
2923 // Updates the line height accordingly with the input style.
2926 EmitStyleChangedSignal();
2929 ImfManager imfManager = ImfManager::Get();
2932 imfManager.SetCursorPosition ( mCursorPosition );
2933 imfManager.NotifyCursorPosition();
2938 void TextInput::DrawCursor()
2940 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2942 // Get height of cursor and set its size
2943 Size size( CURSOR_THICKNESS, 0.0f );
2944 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2946 size.height = rowRect.height;
2950 // Measure Font so know how big text will be if no initial text to measure.
2951 size.height = mLineHeight;
2954 mCursor.SetSize(size);
2956 // If the character is italic then the cursor also tilts.
2957 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2959 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2961 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2963 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2964 bool altPositionValid; // Alternate cursor validity flag.
2965 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2966 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2968 SetAltCursorEnabled( altPositionValid );
2970 if( !altPositionValid )
2972 mCursor.SetPosition( position + UI_OFFSET );
2976 size.height *= 0.5f;
2977 mCursor.SetSize(size);
2978 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2980 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2981 size.height = rowRect.height * 0.5f;
2982 mCursorRTL.SetSize(size);
2983 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2986 if( IsScrollEnabled() )
2988 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2989 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2994 void TextInput::SetAltCursorEnabled( bool enabled )
2996 mCursorRTLEnabled = enabled;
2997 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3000 void TextInput::SetCursorVisibility( bool visible )
3002 mCursorVisibility = visible;
3003 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3004 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3007 void TextInput::CreateGrabHandle( Dali::Image image )
3013 mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3017 mGrabHandleImage = image;
3020 mGrabHandle = ImageActor::New(mGrabHandleImage);
3021 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3022 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3024 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3026 ShowGrabHandleAndSetVisibility( false );
3028 CreateGrabArea( mGrabHandle );
3030 mActiveLayer.Add(mGrabHandle);
3034 void TextInput::CreateGrabArea( Actor& parent )
3036 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3037 mGrabArea.SetName( "GrabArea" );
3038 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3039 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3040 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
3041 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3042 mTapDetector.Attach( mGrabArea );
3043 mPanGestureDetector.Attach( mGrabArea );
3044 mLongPressDetector.Attach( mGrabArea );
3046 parent.Add(mGrabArea);
3049 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3051 Vector3 actualHandlePosition;
3055 mActualGrabHandlePosition.x += displacement.x;
3056 mActualGrabHandlePosition.y += displacement.y;
3058 // Grab handle should jump to the nearest character and take cursor with it
3059 std::size_t newCursorPosition = 0;
3060 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3062 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3063 bool altPositionValid; // Alternate cursor validity flag.
3064 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3065 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3067 if( altPositionValid )
3069 // Check which of the positions is the closest.
3070 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3072 actualHandlePosition = altPosition;
3076 bool handleVisible = true;
3078 if( IsScrollEnabled() )
3080 const Vector3 controlSize = GetControlSize();
3081 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3082 // Scrolls the text if the handle is not in a visible position
3083 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3090 mCurrentHandlePosition = actualHandlePosition;
3091 mScrollDisplacement = Vector2::ZERO;
3095 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3097 mScrollDisplacement.x = -SCROLL_SPEED;
3099 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3101 mScrollDisplacement.x = SCROLL_SPEED;
3103 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3105 mScrollDisplacement.y = -SCROLL_SPEED;
3107 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3109 mScrollDisplacement.y = SCROLL_SPEED;
3115 if( handleVisible && // Only redraw cursor and do updates if position changed
3116 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3118 mCursorPosition = newCursorPosition;
3120 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3122 const TextStyle oldInputStyle( mInputStyle );
3124 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3126 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3128 if( oldInputStyle != mInputStyle )
3130 // Updates the line height accordingly with the input style.
3133 EmitStyleChangedSignal();
3138 return actualHandlePosition;
3141 void TextInput::ShowGrabHandle( bool visible )
3143 if ( IsGrabHandleEnabled() )
3147 mGrabHandle.SetVisible( mGrabHandleVisibility );
3149 StartMonitoringStageForTouch();
3153 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3155 mGrabHandleVisibility = visible;
3156 ShowGrabHandle( visible );
3159 // Callbacks connected to be Property notifications for Boundary checking.
3161 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3163 mIsSelectionHandleOneFlipped = true;
3164 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3165 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3168 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3170 mIsSelectionHandleOneFlipped = false;
3171 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3172 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3175 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3177 mIsSelectionHandleTwoFlipped = true;
3178 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3179 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3182 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3184 mIsSelectionHandleTwoFlipped = false;
3185 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3186 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3189 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3190 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3192 mSelectionHandleOne.SetOpacity(0.0f);
3195 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3197 mSelectionHandleOne.SetOpacity(1.0f);
3200 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3202 mSelectionHandleTwo.SetOpacity(0.0f);
3205 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3207 mSelectionHandleTwo.SetOpacity(1.0f);
3210 // End of Callbacks connected to be Property notifications for Boundary checking.
3212 void TextInput::SetUpHandlePropertyNotifications()
3214 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3216 Vector3 handlesize = GetSelectionHandleSize();
3218 // Exceeding horizontal boundary
3219 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3220 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3222 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3223 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3225 // Within horizontal boundary
3226 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3227 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3229 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3230 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3232 // Exceeding vertical boundary
3233 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3234 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3235 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3236 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3238 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3239 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3240 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3241 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3243 // Within vertical boundary
3244 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3245 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3246 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3247 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3249 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3250 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3251 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3252 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3255 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3257 mSelectionHandleOnePosition = start;
3258 mSelectionHandleTwoPosition = end;
3260 if ( !mSelectionHandleOne )
3262 // create normal and pressed images
3263 mSelectionHandleOneImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3264 mSelectionHandleOneImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3266 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3267 mSelectionHandleOne.SetName("SelectionHandleOne");
3268 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3269 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3270 mIsSelectionHandleOneFlipped = false;
3271 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3273 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3274 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3276 mHandleOneGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3277 mHandleOneGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3278 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3280 mTapDetector.Attach( mHandleOneGrabArea );
3281 mPanGestureDetector.Attach( mHandleOneGrabArea );
3283 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3285 mSelectionHandleOne.Add( mHandleOneGrabArea );
3286 mActiveLayer.Add( mSelectionHandleOne );
3289 if ( !mSelectionHandleTwo )
3291 // create normal and pressed images
3292 mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3293 mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3295 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3296 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3297 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3298 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3299 mIsSelectionHandleTwoFlipped = false;
3300 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3302 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3303 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3304 mHandleTwoGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3305 mHandleTwoGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3306 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3308 mTapDetector.Attach( mHandleTwoGrabArea );
3309 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3311 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3313 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3315 mActiveLayer.Add( mSelectionHandleTwo );
3318 SetUpHandlePropertyNotifications();
3320 // update table as text may have changed.
3321 GetTextLayoutInfo();
3323 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3324 bool altPositionValidOne; // Alternate cursor validity flag.
3325 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3326 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3328 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3329 bool altPositionValidTwo; // Alternate cursor validity flag.
3330 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3331 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3333 // VCC TODO: This method is a hack for one line.
3334 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3336 altPositionValidOne,
3337 altPositionValidTwo,
3341 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3342 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3344 // Calculates and set the visibility if the scroll mode is enabled.
3345 bool isSelectionHandleOneVisible = true;
3346 bool isSelectionHandleTwoVisible = true;
3347 if( IsScrollEnabled() )
3349 const Vector3& controlSize( GetControlSize() );
3350 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3351 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3352 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3353 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3356 CreateHighlight(); // function will only create highlight if not already created.
3359 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3361 Vector3 actualHandlePosition;
3363 if ( mSelectionHandleOne && mSelectionHandleTwo )
3365 const Vector3& controlSize = GetControlSize();
3367 Size cursorSize( CURSOR_THICKNESS, 0.f );
3369 // Get a reference of the wanted selection handle (handle one or two).
3370 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3372 // Get a reference for the current position of the handle and a copy of its pair
3373 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3374 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3376 // Get a handle of the selection handle actor
3377 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3379 // Selection handles should jump to the nearest character
3380 std::size_t newHandlePosition = 0;
3381 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3383 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3384 bool altPositionValid; // Alternate cursor validity flag.
3385 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3386 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3387 if( altPositionValid )
3389 // Check which of the positions is the closest.
3390 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3392 actualHandlePosition = altPosition;
3396 bool handleVisible = true;
3398 if( IsScrollEnabled() )
3400 mCurrentSelectionId = handleId;
3402 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3403 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3404 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3411 mCurrentSelectionHandlePosition = actualHandlePosition;
3412 mScrollDisplacement = Vector2::ZERO;
3416 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3418 mScrollDisplacement.x = -SCROLL_SPEED;
3420 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3422 mScrollDisplacement.x = SCROLL_SPEED;
3424 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3426 mScrollDisplacement.y = -SCROLL_SPEED;
3428 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3430 mScrollDisplacement.y = SCROLL_SPEED;
3436 if ( handleVisible && // Ensure the handle is visible.
3437 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3438 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3440 currentSelectionHandlePosition = newHandlePosition;
3442 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3443 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3447 if ( handleId == HandleOne )
3449 const TextStyle oldInputStyle( mInputStyle );
3451 // Set Active Style to that of first character in selection
3452 if( mSelectionHandleOnePosition < mStyledText.size() )
3454 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3457 if( oldInputStyle != mInputStyle )
3459 // Updates the line height accordingly with the input style.
3462 EmitStyleChangedSignal();
3468 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3471 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3473 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3474 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3476 if ( selectionHandleActor )
3478 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3479 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3480 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3482 if( IsScrollEnabled() )
3484 const Size cursorSize( CURSOR_THICKNESS,
3485 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3486 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3488 GetControlSize() ) );
3493 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3495 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3497 // VCC Set true/false in logical order. TODO : It needs to be checked.
3499 if( startSelection > endSelection )
3501 std::swap( startSelection, endSelection );
3503 std::size_t index = 0u;
3504 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3506 if( ( index < startSelection ) || ( endSelection <= index ) )
3517 // Calculate the dimensions of the quads they will make the highlight mesh
3518 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3520 // At the moment there is no public API to modify the block alignment option.
3522 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3524 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3526 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3527 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3529 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3530 std::vector<bool> selectedVisualText;
3531 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3532 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3533 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3535 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3536 float rowLeft = 0.0f;
3537 float rowRight = 0.0f;
3538 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3539 float maxRowLeft = std::numeric_limits<float>::max();
3540 float maxRowRight = 0.0f;
3542 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3544 // Scan through entire text.
3547 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3549 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3550 bool charSelected = false;
3551 if( selectedIt != selectedEndIt )
3553 charSelected = *selectedIt++;
3556 if( selectionState == SelectionNone )
3560 selectionState = SelectionStarted;
3561 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3562 rowRight = rowLeft + charInfo.mSize.width;
3565 else if( selectionState == SelectionStarted )
3567 // break selection on:
3568 // 1. new line causing selection break. (\n or wordwrap)
3569 // 2. character not selected.
3570 if( !charSelected ||
3571 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3573 // finished selection.
3574 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3575 // that it resides on. That way this enumeration is not necessary.
3577 if(lastIt->mIsNewParagraphChar)
3579 // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line.
3580 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3582 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3583 maxRowLeft = std::min(maxRowLeft, min.x);
3584 maxRowRight = std::max(maxRowRight, max.x);
3585 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3586 float rowTop = rowBottom - rowSize.height;
3588 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3591 rowRight = std::numeric_limits<float>::max();
3593 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3595 selectionState = SelectionNone;
3597 // Still selected? start a new selection
3600 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3602 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3603 selectionState = SelectionStarted;
3608 // build up highlight(s) with this selection data.
3609 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3610 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3617 // If reached end, and still on selection, then close selection.
3620 if(selectionState == SelectionStarted)
3622 // finished selection.
3624 if(lastIt->mIsNewParagraphChar)
3626 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3628 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3629 maxRowLeft = std::min(maxRowLeft, min.x);
3630 maxRowRight = std::max(maxRowRight, max.x);
3631 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3632 float rowTop = rowBottom - rowSize.height;
3633 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3637 // Get the top left and bottom right corners.
3638 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3639 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3640 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3642 // Clamp quads so they appear to clip to borders of the whole text.
3643 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3645 // For block-align align Further Clamp quads to max left and right extents
3646 // BlockAlign: Will adjust highlight to block:
3648 // H[ello] (top row right = max of all rows right)
3649 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3650 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3651 // [text] (bottom row left = min of all rows left)
3652 // (common in SMS messaging selection)
3654 // As opposed to the default which is tight text highlighting.
3659 // (common in regular text editors/web browser selection)
3660 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3662 // Finally clamp quads again so they don't exceed the boundry of the control.
3663 const Vector3& controlSize = GetControlSize();
3664 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3667 return mNewHighlightInfo;
3670 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3671 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3672 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3674 // At the moment there is no public API to modify the block alignment option.
3676 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3678 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3680 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3681 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3683 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3684 std::vector<bool> selectedVisualText;
3685 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3686 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3687 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3689 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3690 float rowLeft = 0.0f;
3691 float rowRight = 0.0f;
3693 // VCC TODO this is valid for one line.
3695 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3697 // Scan through entire text.
3700 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3702 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3703 bool charSelected = false;
3704 if( selectedIt != selectedEndIt )
3706 charSelected = *selectedIt++;
3711 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3712 rowRight = rowLeft + charInfo.mSize.width;
3714 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3715 float rowTop = rowBottom - rowSize.height;
3716 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3722 // Finally clamp quads again so they don't exceed the boundry of the control.
3723 const Vector3& controlSize = GetControlSize();
3724 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3727 return mNewHighlightInfo;
3730 void TextInput::UpdateHighlight()
3732 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3734 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3736 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3737 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3738 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3739 // [BOTTOM] [ MIDDLE ]
3742 // Each quad is created as 2 triangles.
3743 // Middle is just 1 quad regardless of its size.
3757 if ( mHighlightMeshActor )
3759 // vertex and triangle buffers should always be present if MeshActor is alive.
3760 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3761 MeshData::VertexContainer vertices;
3762 Dali::MeshData::FaceIndices faceIndices;
3764 if( !newHighlightInfo.mQuadList.empty() )
3766 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3767 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3769 // vertex position defaults to (0 0 0)
3770 MeshData::Vertex vertex;
3771 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3774 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3776 // Add each quad geometry (a sub-selection) to the mesh data.
3786 QuadCoordinates& quad = *iter;
3788 vertex.x = quad.min.x;
3789 vertex.y = quad.min.y;
3790 vertices.push_back( vertex );
3793 vertex.x = quad.max.x;
3794 vertex.y = quad.min.y;
3795 vertices.push_back( vertex );
3797 // bottom-left (v+2)
3798 vertex.x = quad.min.x;
3799 vertex.y = quad.max.y;
3800 vertices.push_back( vertex );
3802 // bottom-right (v+3)
3803 vertex.x = quad.max.x;
3804 vertex.y = quad.max.y;
3805 vertices.push_back( vertex );
3807 // triangle A (3, 1, 0)
3808 faceIndices.push_back( v + 3 );
3809 faceIndices.push_back( v + 1 );
3810 faceIndices.push_back( v );
3812 // triangle B (0, 2, 3)
3813 faceIndices.push_back( v );
3814 faceIndices.push_back( v + 2 );
3815 faceIndices.push_back( v + 3 );
3817 mMeshData.SetFaceIndices( faceIndices );
3820 BoneContainer bones(0); // passed empty as bones not required
3821 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3822 mHighlightMesh.UpdateMeshData(mMeshData);
3827 void TextInput::ClearPopup()
3829 mPopupPanel.Clear();
3832 void TextInput::AddPopupOptions()
3834 mPopupPanel.AddPopupOptions();
3837 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3839 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3841 Vector3 clampedPosition ( position );
3842 Vector3 tailOffsetPosition ( position );
3844 float xOffSet( 0.0f );
3846 Actor self = Self();
3847 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3849 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3850 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3852 // Clamp to left or right or of boundary
3853 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3855 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3857 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3859 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3862 clampedPosition.x = position.x + xOffSet;
3863 tailOffsetPosition.x = -xOffSet;
3865 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3866 bool flipTail( false );
3868 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3870 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3874 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3875 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3878 void TextInput::HidePopup(bool animate, bool signalFinished )
3880 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3882 mPopupPanel.Hide( animate );
3884 if( animate && signalFinished )
3886 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3891 void TextInput::ShowPopup( bool animate )
3894 Vector2 alternativePopupPosition;
3896 if(mHighlightMeshActor && mState == StateEdit)
3899 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3901 // When text is selected, show popup above top handle (and text), or below bottom handle.
3902 // topHandle: referring to the top most point of the handle or the top line of selection.
3903 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3905 topHandle = mSelectionHandleOneActualPosition;
3906 bottomHandle = mSelectionHandleTwoActualPosition;
3907 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3911 topHandle = mSelectionHandleTwoActualPosition;
3912 bottomHandle = mSelectionHandleOneActualPosition;
3913 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3915 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3916 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3918 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3920 position.x = xPosition;
3922 // Alternative position if no upper space
3923 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3924 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3928 // When no text is selected, show popup at world position of grab handle or cursor
3929 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3930 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3931 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3932 // if can't be positioned above, then position below row.
3933 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3936 // If grab handle enabled then position pop-up below the grab handle.
3937 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3941 SetPopupPosition( position, alternativePopupPosition );
3944 mPopupPanel.Show( Self(), animate );
3945 StartMonitoringStageForTouch();
3947 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3950 void TextInput::ShowPopupCutCopyPaste()
3954 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3955 // Check the selected text is whole text or not.
3956 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3958 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3961 if ( !mStyledText.empty() && IsTextSelected() )
3963 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3964 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3967 if( mClipboard && mClipboard.NumberOfItems() )
3969 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3970 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3975 mPopupPanel.Hide(false);
3979 void TextInput::SetUpPopupSelection( bool showCutButton )
3982 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3983 // If no text exists then don't offer to select
3984 if ( !mStyledText.empty() )
3986 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3987 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3988 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
3990 // if clipboard has valid contents then offer paste option
3991 if( mClipboard && mClipboard.NumberOfItems() )
3993 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3994 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3999 mPopupPanel.Hide(false);
4002 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4007 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4008 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4009 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4010 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4012 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4014 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4016 float closestYdifference = std::numeric_limits<float>::max();
4017 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4018 std::size_t numberOfMatchedCharacters = 0;
4020 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4021 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4023 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4025 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4026 float baselinePosition = info.mPosition.y - info.mDescender;
4028 if( info.mIsVisible )
4030 // store difference between source y point and the y position of the current character
4031 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4033 if( currentYdifference < closestYdifference )
4035 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4036 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4037 closestYdifference = currentYdifference;
4038 matchedCharacters.clear();
4039 numberOfMatchedCharacters = 0; // reset count
4042 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4043 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4045 // ignore new line character.
4046 if( !info.mIsNewParagraphChar )
4048 matchedCharacters.push_back( info );
4049 numberOfMatchedCharacters++;
4053 } // End of loop checking each character's y position in the character layout table
4055 // Check if last character is a newline, if it is
4056 // then need pretend there is an imaginary line afterwards,
4057 // and check if user is touching below previous line.
4058 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4060 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4062 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4066 // 2 Iterate through matching list of y positions and find closest matching X position.
4068 bool matched( false );
4070 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4071 std::size_t visualIndex = 0u;
4072 const std::size_t matchedCharactersSize = matchedCharacters.size();
4073 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4075 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4077 if( info.mIsVisible )
4079 // stop when on left side of character's center.
4080 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4081 if( sourceScrollOffset.x < characterMidPointPosition )
4083 if(info.mIsRightToLeftCharacter)
4085 rightToLeftChar = true;
4087 glyphIntersection = info.mPosition.x;
4092 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4096 if( visualIndex == matchedCharactersSize )
4098 rightToLeftChar = lastRightToLeftChar;
4101 closestIndex = lineOffset + visualIndex;
4103 mClosestCursorPositionEOL = false; // reset
4104 if( ( visualIndex == matchedCharactersSize ) && !matched )
4106 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4109 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4110 if( rightToLeftChar && lastRightToLeftChar )
4112 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4117 // closestIndex is the visual index, need to convert it to the logical index
4118 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4120 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4122 // Checks for situations where user is touching between LTR and RTL
4123 // characters. To identify if the user means the end of a LTR string
4124 // or the beginning of an RTL string, and vice versa.
4125 if( closestIndex > 0 )
4127 if( rightToLeftChar && !lastRightToLeftChar )
4132 // A: In this touch range, the user is indicating that they wish to place
4133 // the cursor at the end of the LTR text.
4134 // B: In this touch range, the user is indicating that they wish to place
4135 // the cursor at the end of the RTL text.
4137 // Result of touching A area:
4138 // [.....LTR]|[RTL......]+
4140 // |: primary cursor (for typing LTR chars)
4141 // +: secondary cursor (for typing RTL chars)
4143 // Result of touching B area:
4144 // [.....LTR]+[RTL......]|
4146 // |: primary cursor (for typing RTL chars)
4147 // +: secondary cursor (for typing LTR chars)
4149 if( sourceScrollOffset.x < glyphIntersection )
4154 else if( !rightToLeftChar && lastRightToLeftChar )
4156 if( sourceScrollOffset.x < glyphIntersection )
4163 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4164 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4165 // one further ahead
4166 if( rightToLeftChar && !lastRightToLeftChar )
4171 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4173 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4175 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4184 float TextInput::GetLineJustificationPosition() const
4186 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4187 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4188 float alignmentOffset = 0.f;
4190 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4191 if( alignment & Toolkit::Alignment::HorizontalLeft )
4193 alignmentOffset = 0.f;
4195 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4197 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4199 else if( alignment & Toolkit::Alignment::HorizontalRight )
4201 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4204 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4205 float justificationOffset = 0.f;
4207 switch( justification )
4209 case Toolkit::TextView::Left:
4211 justificationOffset = 0.f;
4214 case Toolkit::TextView::Center:
4216 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4219 case Toolkit::TextView::Right:
4221 justificationOffset = mTextLayoutInfo.mTextSize.width;
4224 case Toolkit::TextView::Justified:
4226 justificationOffset = 0.f;
4231 DALI_ASSERT_ALWAYS( false );
4235 return alignmentOffset + justificationOffset;
4238 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4240 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4241 A newline character is not inserted in this case */
4243 Vector3 cursorPosition;
4245 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4249 if( characterPosition > 0u )
4251 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4253 // If previous character on a different line then use current characters position
4254 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4256 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4257 if ( mClosestCursorPositionEOL )
4259 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4263 cursorPosition = Vector3( currentCharInfo.mPosition );
4272 // If the character is left to right, the position is the character's position plus its width.
4273 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4275 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4276 cursorPosition.y = currentCharInfo.mPosition.y;
4279 return cursorPosition;
4282 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4284 bool direction = false;
4285 Vector3 alternatePosition;
4286 bool alternatePositionValid = false;
4288 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4291 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4293 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4294 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4295 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4297 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4299 alternatePositionValid = false;
4300 directionRTL = false;
4302 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4304 if( characterPosition == 0u )
4306 // When the cursor position is at the beginning, it should be at the start of the current character.
4307 // If the current character is LTR, then the start is on the right side of the glyph.
4308 // If the current character is RTL, then the start is on the left side of the glyph.
4310 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4312 characterPosition = FindVisibleCharacter( Right, 0u );
4315 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4316 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4318 cursorPosition.x = info.mPosition.x + rtlOffset;
4319 cursorPosition.y = info.mPosition.y;
4320 directionRTL = info.mIsRightToLeftCharacter;
4322 else if( characterPosition > 0u )
4324 // Get the direction of the paragraph.
4325 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4326 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4328 // When cursor is not at beginning, consider possibility of
4329 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4331 // Cursor position should be the end of the last character.
4332 // If the last character is LTR, then the end is on the right side of the glyph.
4333 // If the last character is RTL, then the end is on the left side of the glyph.
4335 --characterPosition;
4337 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4339 characterPosition = FindVisibleCharacter( Left, characterPosition );
4342 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4343 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4345 // VCC TODO : check for a new paragraph character.
4347 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4348 const Vector3& size = GetControlSize();
4350 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4352 --characterPosition;
4354 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4357 if( !info.mIsNewParagraphChar )
4359 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4363 // VCC TODO : check for a new paragraph character.
4365 // When cursor points to first character on new line, position cursor at the start of this glyph.
4366 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4368 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4369 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4371 cursorPosition.x = infoNext.mPosition.x + start;
4372 cursorPosition.y = infoNext.mPosition.y;
4376 // If cursor points to the end of text, then can only position
4377 // cursor where the new line starts based on the line-justification position.
4378 cursorPosition.x = GetLineJustificationPosition();
4380 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4382 // If this is after the last character, then we can assume that the new cursor
4383 // should be exactly one row below the current row.
4385 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4386 cursorPosition.y = info.mPosition.y + rowRect.height;
4390 // If this is not after last character, then we can use this row's height.
4391 // should be exactly one row below the current row.
4393 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4394 cursorPosition.y = info.mPosition.y + rowRect.height;
4399 directionRTL = info.mIsRightToLeftCharacter;
4401 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4403 // 1. When the cursor is neither at the beginning or the end,
4404 // we can show multiple cursors under situations when the cursor is
4405 // between RTL and LTR text...
4406 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4408 std::size_t characterAltPosition = characterPosition + 1u;
4410 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4412 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4414 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4415 // Text: [...LTR...]|[...RTL...]
4417 // Alternate cursor pos: ^
4418 // In which case we need to display an alternate cursor for the RTL text.
4420 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4421 alternatePosition.y = infoAlt.mPosition.y;
4422 alternatePositionValid = true;
4424 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4426 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4427 // Text: |[...RTL...] [...LTR....]
4429 // Alternate cursor pos: ^
4430 // In which case we need to display an alternate cursor for the RTL text.
4432 alternatePosition.x = infoAlt.mPosition.x;
4433 alternatePosition.y = infoAlt.mPosition.y;
4434 alternatePositionValid = true;
4439 // 2. When the cursor is at the end of the text,
4440 // and we have multi-directional text,
4441 // we can also consider showing mulitple cursors.
4442 // The rule here is:
4443 // If first and last characters on row are different
4444 // Directions, then two cursors need to be displayed.
4446 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4448 // The last character's direction is differernt than the first one of current paragraph.
4451 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4453 if(info.mIsRightToLeftCharacter)
4455 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4456 // Text: [...LTR...]|[...RTL...]
4458 // Alternate cursor pos: ^
4459 // In which case we need to display an alternate cursor for the RTL text, this cursor
4460 // should be at the end of the given line.
4462 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4463 alternatePosition.y = infoStart.mPosition.y;
4464 alternatePositionValid = true;
4466 else if(!info.mIsRightToLeftCharacter) // starting RTL
4468 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4469 // Text: |[...RTL...] [...LTR....]
4471 // Alternate cursor pos: ^
4472 // In which case we need to display an alternate cursor for the RTL text.
4474 alternatePosition.x = infoStart.mPosition.x;
4475 alternatePosition.y = infoStart.mPosition.y;
4476 alternatePositionValid = true;
4481 } // characterPosition > 0
4485 // If the character table is void, place the cursor accordingly the text alignment.
4486 const Vector3& size = GetControlSize();
4488 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4489 float alignmentOffset = 0.f;
4491 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4492 if( alignment & Toolkit::Alignment::HorizontalLeft )
4494 alignmentOffset = 0.f;
4496 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4498 alignmentOffset = 0.5f * ( size.width );
4500 else if( alignment & Toolkit::Alignment::HorizontalRight )
4502 alignmentOffset = size.width;
4505 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4506 cursorPosition.x = alignmentOffset;
4508 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4509 if( alignment & Toolkit::Alignment::VerticalTop )
4511 cursorPosition.y = mLineHeight;
4513 else if( alignment & Toolkit::Alignment::VerticalCenter )
4515 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4517 else if( alignment & Toolkit::Alignment::VerticalBottom )
4519 cursorPosition.y = size.height;
4523 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4524 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4526 if( alternatePositionValid )
4528 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4529 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4532 return cursorPosition;
4535 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4537 // scan string from current position to beginning of current line to note direction of line
4538 while( logicalPosition )
4541 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4548 return logicalPosition;
4551 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4553 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4555 while( logicalPosition )
4558 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4565 return logicalPosition;
4568 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4572 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4575 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4577 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4578 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4580 min = Vector2::ZERO;
4581 max = Vector2(0.0f, mLineHeight);
4585 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4587 // Initializes the min and max position.
4588 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4589 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4593 // 1) Find the line where the character is laid-out.
4594 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4595 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4598 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4600 // Index within the whole text to the last character of the current line.
4601 std::size_t lastCharacterOfLine = 0u;
4603 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4604 if( lineNextIt != lineEndIt )
4606 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4610 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4613 // Check if the given chracter position is within the line.
4614 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4616 // 2) Get the row rect of all laid-out characters on the line.
4618 // Need to scan all characters of the line because they are in the logical position.
4619 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4620 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4624 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4626 min.x = std::min( min.x, characterInfo.mPosition.x );
4627 min.y = std::min( min.y, characterInfo.mPosition.y );
4628 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4629 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4636 return Size( max.x - min.x, max.y - min.y );
4639 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4641 Actor popUpPanel = mPopupPanel.GetRootActor();
4643 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4649 Dali::Actor parent( touchedActor.GetParent() );
4653 return WasTouchedCheck( parent );
4660 void TextInput::StartMonitoringStageForTouch()
4662 Stage stage = Stage::GetCurrent();
4663 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4666 void TextInput::EndMonitoringStageForTouch()
4668 Stage stage = Stage::GetCurrent();
4669 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4672 void TextInput::OnStageTouched(const TouchEvent& event)
4674 if( event.GetPointCount() > 0 )
4676 if ( TouchPoint::Down == event.GetPoint(0).state )
4678 const Actor touchedActor(event.GetPoint(0).hitActor);
4680 bool popUpShown( false );
4682 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4687 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4689 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4691 EndMonitoringStageForTouch();
4692 HidePopup( true, false );
4695 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4697 EndMonitoringStageForTouch();
4698 ShowGrabHandleAndSetVisibility( false );
4704 void TextInput::SelectText(std::size_t start, std::size_t end)
4706 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4707 IsGrabHandleEnabled()?"true":"false",
4708 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4709 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4710 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4712 StartMonitoringStageForTouch();
4714 if ( mEditModeActive ) // Only allow text selection when in edit mode
4716 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4717 mSelectingText = true;
4719 std::size_t selectionStartPosition = std::min( start, end );
4721 // Hide grab handle when selecting.
4722 ShowGrabHandleAndSetVisibility( false );
4724 if( start != end ) // something to select
4726 SetCursorVisibility( false );
4727 StopCursorBlinkTimer();
4729 CreateSelectionHandles(start, end);
4732 const TextStyle oldInputStyle( mInputStyle );
4733 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4735 if( oldInputStyle != mInputStyle )
4737 // Updates the line height accordingly with the input style.
4740 EmitStyleChangedSignal();
4746 mSelectingText = false;
4750 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4752 MarkupProcessor::StyledTextArray currentSelectedText;
4754 if ( IsTextSelected() )
4756 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4757 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4759 for(; it != end; ++it)
4761 MarkupProcessor::StyledText& styledText( *it );
4762 currentSelectedText.push_back( styledText );
4765 return currentSelectedText;
4768 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4770 const std::size_t beginIndex = std::min( begin, end );
4771 const std::size_t endIndex = std::max( begin, end );
4774 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4776 // Create a styled text array used to replace the text into the text-view.
4777 MarkupProcessor::StyledTextArray text;
4778 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4780 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4781 GetTextLayoutInfo();
4783 if( IsScrollEnabled() )
4785 // Need to set the scroll position as the text's size may have changed.
4786 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4789 ShowGrabHandleAndSetVisibility( false );
4795 // Set Handle positioning as the new style may have repositioned the characters.
4796 SetSelectionHandlePosition(HandleOne);
4797 SetSelectionHandlePosition(HandleTwo);
4800 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4802 // Just hide the grab handle when keyboard is hidden.
4803 if (!keyboardShown )
4805 ShowGrabHandleAndSetVisibility( false );
4807 // If the keyboard is not now being shown, then hide the popup panel
4808 mPopupPanel.Hide( true );
4812 // Removes highlight and resumes edit mode state
4813 void TextInput::RemoveHighlight( bool hidePopup )
4815 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4817 if ( mHighlightMeshActor )
4819 if ( mSelectionHandleOne )
4821 mActiveLayer.Remove( mSelectionHandleOne );
4822 mSelectionHandleOne.Reset();
4823 mSelectionHandleOneOffset.x = 0.0f;
4825 if ( mSelectionHandleTwo )
4827 mActiveLayer.Remove( mSelectionHandleTwo );
4828 mSelectionHandleTwo.Reset();
4829 mSelectionHandleTwoOffset.x = 0.0f;
4832 mNewHighlightInfo.mQuadList.clear();
4834 Self().Remove( mHighlightMeshActor );
4836 SetCursorVisibility( true );
4837 StartCursorBlinkTimer();
4839 mHighlightMeshActor.Reset();
4840 // NOTE: We cannot dereference mHighlightMesh, due
4841 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4849 mSelectionHandleOnePosition = 0;
4850 mSelectionHandleTwoPosition = 0;
4853 void TextInput::CreateHighlight()
4855 if ( !mHighlightMeshActor )
4857 mMeshData = MeshData( );
4858 mMeshData.SetHasNormals( true );
4860 mCustomMaterial = Material::New("CustomMaterial");
4861 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4863 mMeshData.SetMaterial( mCustomMaterial );
4865 mHighlightMesh = Mesh::New( mMeshData );
4867 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4868 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4869 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4870 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4871 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4873 Self().Add(mHighlightMeshActor);
4878 bool TextInput::CopySelectedTextToClipboard()
4880 mCurrentCopySelecton.clear();
4882 mCurrentCopySelecton = GetSelectedText();
4884 std::string stringToStore;
4886 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4887 * a marked up string.
4889 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4890 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4892 bool success = mClipboard.SetItem( stringToStore );
4896 void TextInput::PasteText( const Text& text )
4898 // Update Flag, indicates whether to update the text-input contents or not.
4899 // Any key stroke that results in a visual change of the text-input should
4900 // set this flag to true.
4901 bool update = false;
4902 if( mHighlightMeshActor )
4904 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4905 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4907 ImfManager imfManager = ImfManager::Get();
4910 imfManager.SetCursorPosition( mCursorPosition );
4911 imfManager.NotifyCursorPosition();
4913 DeleteHighlightedText( true );
4917 bool textExceedsMaximunNumberOfCharacters = false;
4918 bool textExceedsBoundary = false;
4920 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4922 mCursorPosition += insertedStringLength;
4923 ImfManager imfManager = ImfManager::Get();
4926 imfManager.SetCursorPosition ( mCursorPosition );
4927 imfManager.NotifyCursorPosition();
4930 update = update || ( insertedStringLength > 0 );
4937 if( insertedStringLength < text.GetLength() )
4939 EmitMaxInputCharactersReachedSignal();
4942 if( textExceedsBoundary )
4944 EmitInputTextExceedsBoundariesSignal();
4948 void TextInput::SetTextDirection()
4950 // Put the cursor to the right if we are empty and an RTL language is being used.
4951 if ( mStyledText.empty() )
4953 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4955 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4956 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4958 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4959 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4961 int alignment( mDisplayedTextView.GetTextAlignment() &
4962 ( Toolkit::Alignment::VerticalTop |
4963 Toolkit::Alignment::VerticalCenter |
4964 Toolkit::Alignment::VerticalBottom |
4965 Toolkit::Alignment::HorizontalCenter ) );
4966 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4968 // If our alignment is in the center, then do not change.
4969 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4971 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4974 // If our justification is in the center, then do not change.
4975 if ( justification != Toolkit::TextView::Center )
4977 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4980 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4981 mDisplayedTextView.SetLineJustification( justification );
4985 void TextInput::UpdateLineHeight()
4987 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4988 mLineHeight = font.GetLineHeight();
4990 // If the height exceed policy is shrink or exceed the boundaries of the text-input is not allowed, then modify the line height is needed.
4992 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4994 if( !mExceedEnabled || shrink )
4996 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5000 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5002 // VCC check if we need do this in the visual order ...
5003 std::size_t position = 0u;
5005 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5011 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5013 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5015 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5021 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5022 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5024 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5030 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5035 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5042 void TextInput::SetSortModifier( float depthOffset )
5044 if(mDisplayedTextView)
5046 mDisplayedTextView.SetSortModifier(depthOffset);
5050 void TextInput::SetSnapshotModeEnabled( bool enable )
5052 if(mDisplayedTextView)
5054 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5058 bool TextInput::IsSnapshotModeEnabled() const
5060 bool snapshotEnabled = false;
5062 if(mDisplayedTextView)
5064 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5067 return snapshotEnabled;
5070 void TextInput::SetMarkupProcessingEnabled( bool enable )
5072 mMarkUpEnabled = enable;
5075 bool TextInput::IsMarkupProcessingEnabled() const
5077 return mMarkUpEnabled;
5080 void TextInput::SetScrollEnabled( bool enable )
5082 if( mDisplayedTextView )
5084 mDisplayedTextView.SetScrollEnabled( enable );
5089 // Don't set cursor's and handle's visibility to false if they are outside the
5090 // boundaries of the text-input.
5091 mIsCursorInScrollArea = true;
5092 mIsGrabHandleInScrollArea = true;
5093 if( mSelectionHandleOne && mSelectionHandleTwo )
5095 mSelectionHandleOne.SetVisible( true );
5096 mSelectionHandleTwo.SetVisible( true );
5098 if( mHighlightMeshActor )
5100 mHighlightMeshActor.SetVisible( true );
5106 bool TextInput::IsScrollEnabled() const
5108 bool scrollEnabled = false;
5110 if( mDisplayedTextView )
5112 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5115 return scrollEnabled;
5118 void TextInput::SetScrollPosition( const Vector2& position )
5120 if( mDisplayedTextView )
5122 mDisplayedTextView.SetScrollPosition( position );
5126 Vector2 TextInput::GetScrollPosition() const
5128 Vector2 scrollPosition;
5130 if( mDisplayedTextView )
5132 scrollPosition = mDisplayedTextView.GetScrollPosition();
5135 return scrollPosition;
5138 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5140 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5141 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5142 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5144 // Add style to the new input text.
5145 MarkupProcessor::StyledTextArray textToInsert;
5146 for( std::size_t i = 0; i < insertedStringLength; ++i )
5148 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5149 textToInsert.push_back( newStyledCharacter );
5152 //Insert text to the TextView.
5153 const bool emptyTextView = mStyledText.empty();
5154 if( emptyTextView && mPlaceHolderSet )
5156 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5157 mDisplayedTextView.SetText( textToInsert );
5161 if( 0 == numberOfCharactersToReplace )
5163 mDisplayedTextView.InsertTextAt( position, textToInsert );
5167 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5170 mPlaceHolderSet = false;
5172 if( textToInsert.empty() )
5174 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5175 GetTextLayoutInfo();
5179 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5180 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5183 textExceedsBoundary = false;
5185 if( !mExceedEnabled )
5187 const Vector3& size = GetControlSize();
5189 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5191 // If new text does not fit within TextView
5192 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5193 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5194 GetTextLayoutInfo();
5195 textExceedsBoundary = true;
5196 insertedStringLength = 0;
5199 if( textExceedsBoundary )
5201 // Add the part of the text which fits on the text-input.
5203 // Split the text which doesn't fit in two halves.
5204 MarkupProcessor::StyledTextArray firstHalf;
5205 MarkupProcessor::StyledTextArray secondHalf;
5206 SplitText( textToInsert, firstHalf, secondHalf );
5208 // Clear text. This text will be filled with the text inserted.
5209 textToInsert.clear();
5211 // Where to insert the text.
5212 std::size_t positionToInsert = position;
5214 bool end = text.GetLength() <= 1;
5217 // Insert text and check ...
5218 const std::size_t textLength = firstHalf.size();
5219 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5220 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5222 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5224 // Inserted text doesn't fit.
5226 // Remove inserted text
5227 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5228 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5230 // The iteration finishes when only one character doesn't fit.
5231 end = textLength <= 1;
5235 // Prepare next two halves for next iteration.
5236 MarkupProcessor::StyledTextArray copyText = firstHalf;
5237 SplitText( copyText, firstHalf, secondHalf );
5244 // store text to be inserted in mStyledText.
5245 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5247 // Increase the inserted characters counter.
5248 insertedStringLength += textLength;
5250 // Prepare next two halves for next iteration.
5251 MarkupProcessor::StyledTextArray copyText = secondHalf;
5252 SplitText( copyText, firstHalf, secondHalf );
5254 // Update where next text has to be inserted
5255 positionToInsert += textLength;
5261 if( textToInsert.empty() && emptyTextView )
5263 // No character has been added and the text-view was empty.
5264 // Show the placeholder text.
5265 ShowPlaceholderText( mStyledPlaceHolderText );
5269 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5270 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5271 mPlaceHolderSet = false;
5274 return insertedStringLength;
5277 void TextInput::GetTextLayoutInfo()
5279 if( mStyledText.empty() )
5281 // The text-input has no text, clear the text-view's layout info.
5282 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5286 if( mDisplayedTextView )
5288 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5292 // There is no text-view.
5293 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5298 void TextInput::SetOffsetFromText( const Vector4& offset )
5300 mPopupOffsetFromText = offset;
5303 const Vector4& TextInput::GetOffsetFromText() const
5305 return mPopupOffsetFromText;
5308 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5310 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5314 TextInput& textInputImpl( GetImpl( textInput ) );
5316 switch ( propertyIndex )
5318 case Toolkit::TextInput::Property::HIGHLIGHT_COLOR:
5320 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5323 case Toolkit::TextInput::Property::CUT_AND_PASTE_COLOR:
5325 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5328 case Toolkit::TextInput::Property::CUT_AND_PASTE_PRESSED_COLOR:
5330 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5333 case Toolkit::TextInput::Property::CUT_AND_PASTE_BORDER_COLOR:
5335 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5338 case Toolkit::TextInput::Property::CUT_AND_PASTE_ICON_COLOR:
5340 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5343 case Toolkit::TextInput::Property::CUT_AND_PASTE_ICON_PRESSED_COLOR:
5345 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5348 case Toolkit::TextInput::Property::CUT_AND_PASTE_TEXT_COLOR:
5350 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5353 case Toolkit::TextInput::Property::CUT_AND_PASTE_TEXT_PRESSED_COLOR:
5355 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5358 case Toolkit::TextInput::Property::CUT_BUTTON_POSITION_PRIORITY:
5360 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5363 case Toolkit::TextInput::Property::COPY_BUTTON_POSITION_PRIORITY:
5365 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5368 case Toolkit::TextInput::Property::PASTE_BUTTON_POSITION_PRIORITY:
5370 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5373 case Toolkit::TextInput::Property::SELECT_BUTTON_POSITION_PRIORITY:
5375 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5378 case Toolkit::TextInput::Property::SELECT_ALL_BUTTON_POSITION_PRIORITY:
5380 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5383 case Toolkit::TextInput::Property::CLIPBOARD_BUTTON_POSITION_PRIORITY:
5385 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5388 case Toolkit::TextInput::Property::POP_UP_OFFSET_FROM_TEXT:
5390 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5393 case Toolkit::TextInput::Property::CURSOR_COLOR:
5395 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5402 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5404 Property::Value value;
5406 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5410 TextInput& textInputImpl( GetImpl( textInput ) );
5412 switch ( propertyIndex )
5414 case Toolkit::TextInput::Property::HIGHLIGHT_COLOR:
5416 value = textInputImpl.GetMaterialDiffuseColor();
5419 case Toolkit::TextInput::Property::CUT_AND_PASTE_COLOR:
5421 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5424 case Toolkit::TextInput::Property::CUT_AND_PASTE_PRESSED_COLOR:
5426 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5429 case Toolkit::TextInput::Property::CUT_AND_PASTE_BORDER_COLOR :
5431 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5434 case Toolkit::TextInput::Property::CUT_AND_PASTE_ICON_COLOR:
5436 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5439 case Toolkit::TextInput::Property::CUT_AND_PASTE_ICON_PRESSED_COLOR:
5441 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5444 case Toolkit::TextInput::Property::CUT_AND_PASTE_TEXT_COLOR:
5446 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5449 case Toolkit::TextInput::Property::CUT_AND_PASTE_TEXT_PRESSED_COLOR:
5451 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5454 case Toolkit::TextInput::Property::CUT_BUTTON_POSITION_PRIORITY:
5456 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5459 case Toolkit::TextInput::Property::COPY_BUTTON_POSITION_PRIORITY:
5461 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5464 case Toolkit::TextInput::Property::PASTE_BUTTON_POSITION_PRIORITY:
5466 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5469 case Toolkit::TextInput::Property::SELECT_BUTTON_POSITION_PRIORITY:
5471 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5474 case Toolkit::TextInput::Property::SELECT_ALL_BUTTON_POSITION_PRIORITY:
5476 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5479 case Toolkit::TextInput::Property::CLIPBOARD_BUTTON_POSITION_PRIORITY:
5481 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5484 case Toolkit::TextInput::Property::POP_UP_OFFSET_FROM_TEXT:
5486 value = textInputImpl.GetOffsetFromText();
5489 case Toolkit::TextInput::Property::CURSOR_COLOR:
5491 value = textInputImpl.mCursor.GetCurrentColor();
5498 void TextInput::EmitStyleChangedSignal()
5500 // emit signal if input style changes.
5501 Toolkit::TextInput handle( GetOwner() );
5502 mStyleChangedSignal.Emit( handle, mInputStyle );
5505 void TextInput::EmitTextModified()
5507 // emit signal when text changes.
5508 Toolkit::TextInput handle( GetOwner() );
5509 mTextModifiedSignal.Emit( handle );
5513 void TextInput::EmitMaxInputCharactersReachedSignal()
5515 // emit signal if max characters is reached during text input.
5516 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5518 Toolkit::TextInput handle( GetOwner() );
5519 mMaxInputCharactersReachedSignal.Emit( handle );
5522 void TextInput::EmitInputTextExceedsBoundariesSignal()
5524 // Emit a signal when the input text exceeds the boundaries of the text input.
5526 Toolkit::TextInput handle( GetOwner() );
5527 mInputTextExceedBoundariesSignal.Emit( handle );
5530 } // namespace Internal
5532 } // namespace Toolkit