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.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
89 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
91 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
95 if( ( *it ).mIsVisible )
97 return --cursorPosition;
106 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
108 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
110 if( ( *it ).mIsVisible )
112 return cursorPosition;
118 return cursorPosition;
122 * Whether the given position plus the cursor size offset is inside the given boundary.
124 * @param[in] position The given position.
125 * @param[in] cursorSize The cursor size.
126 * @param[in] controlSize The given boundary.
128 * @return whether the given position is inside the given boundary.
130 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
132 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
133 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
134 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
135 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
139 * Splits a text in two halves.
141 * If the text's number of characters is odd, firstHalf has one more character.
143 * @param[in] text The text to be split.
144 * @param[out] firstHalf The first half of the text.
145 * @param[out] secondHalf The second half of the text.
147 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
148 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
149 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
154 const std::size_t textLength = text.size();
155 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
157 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
158 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
161 } // end of namespace
169 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
170 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
171 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
172 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
173 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
174 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
175 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
176 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
177 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
178 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
179 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
180 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
181 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
182 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
183 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
184 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
195 return Toolkit::TextInput::New();
198 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
200 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
201 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
202 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
203 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
204 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
205 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
209 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
210 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
211 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
212 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
213 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
214 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
215 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
216 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
217 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
227 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
229 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
231 QuadCoordinates quad(x1, y1, x2, y2);
232 mQuadList.push_back( quad );
235 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
237 for(std::size_t i = 0;i < mQuadList.size(); i++)
239 QuadCoordinates& quad = mQuadList[i];
241 quad.min.Clamp(min, max);
242 quad.max.Clamp(min, max);
246 // [TextInput] ////////////////////////////////////////////////////////////////
248 Dali::Toolkit::TextInput TextInput::New()
250 // Create the implementation
251 TextInputPtr textInput(new TextInput());
252 // Pass ownership to CustomActor via derived handle
253 Dali::Toolkit::TextInput handle(*textInput);
254 handle.SetName( "TextInput");
256 textInput->Initialize();
260 TextInput::TextInput()
261 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
266 mDisplayedTextView(),
267 mStyledPlaceHolderText(),
268 mMaxStringLength( DEFAULT_MAX_SIZE ),
269 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
270 mCursorPosition( 0 ),
271 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
272 mIsSelectionHandleOneFlipped( false ),
273 mIsSelectionHandleTwoFlipped( false ),
274 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
275 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
276 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
277 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
278 mSelectionHandleOnePosition( 0 ),
279 mSelectionHandleTwoPosition( 0 ),
281 mPreEditStartPosition( 0 ),
282 mPreEditLength ( 0 ),
283 mNumberOfSurroundingCharactersDeleted( 0 ),
284 mTouchStartTime( 0 ),
286 mCurrentCopySelecton(),
289 mScrollDisplacement(),
290 mCurrentHandlePosition(),
291 mCurrentSelectionId(),
292 mCurrentSelectionHandlePosition(),
293 mRequestedSelection( 0, 0 ),
294 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
295 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
297 mMaterialColor( LIGHTBLUE ),
298 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
299 mOverrideAutomaticAlignment( false ),
300 mCursorRTLEnabled( false ),
301 mClosestCursorPositionEOL ( false ),
302 mCursorBlinkStatus( true ),
303 mCursorVisibility( false ),
304 mGrabHandleVisibility( false ),
305 mIsCursorInScrollArea( true ),
306 mIsGrabHandleInScrollArea( true ),
307 mEditModeActive( false ),
308 mEditOnTouch( true ),
309 mTextSelection( true ),
310 mExceedEnabled( true ),
311 mGrabHandleEnabled( true ),
312 mIsSelectionHandleFlipEnabled( true ),
313 mPreEditFlag( false ),
314 mIgnoreCommitFlag( false ),
315 mIgnoreFirstCommitFlag( false ),
316 mSelectingText( false ),
317 mPreserveCursorPosition( false ),
318 mSelectTextOnCommit( false ),
319 mUnderlinedPriorToPreEdit ( false ),
320 mCommitByKeyInput( false ),
321 mPlaceHolderSet( false ),
322 mMarkUpEnabled( false )
324 // Updates the line height accordingly with the input style.
328 TextInput::~TextInput()
330 StopCursorBlinkTimer();
335 std::string TextInput::GetText() const
339 // Return text-view's text only if the text-input's text is not empty
340 // in order to not to return the placeholder text.
341 if( !mStyledText.empty() )
343 text = mDisplayedTextView.GetText();
349 std::string TextInput::GetMarkupText() const
351 std::string markupString;
352 MarkupProcessor::GetMarkupString( mStyledText, markupString );
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() );
362 if( mStyledText.empty() )
364 // Set the placeholder text only if the styled text is empty.
365 mDisplayedTextView.SetText( mStyledPlaceHolderText );
366 mPlaceHolderSet = true;
370 std::string TextInput::GetPlaceholderText()
372 // Traverses the styled placeholder array getting only the text.
373 // Note that for some languages a 'character' could be represented by more than one 'char'
375 std::string placeholderText;
376 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
378 placeholderText.append( (*it).mText.GetText() );
381 return placeholderText ;
384 void TextInput::SetInitialText(const std::string& initialText)
386 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
388 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
390 mPreEditFlag = false;
391 mIgnoreCommitFlag = true;
394 SetText( initialText );
395 PreEditReset( false ); // Reset keyboard as text changed
398 void TextInput::SetText(const std::string& initialText)
400 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
402 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
404 if( mStyledText.empty() )
406 // If the initial text is empty, set the placeholder text.
407 mDisplayedTextView.SetText( mStyledPlaceHolderText );
408 mPlaceHolderSet = true;
412 mDisplayedTextView.SetText( mStyledText );
413 mPlaceHolderSet = false;
418 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
420 ImfManager imfManager = ImfManager::Get();
423 imfManager.SetCursorPosition( mCursorPosition );
424 imfManager.SetSurroundingText( initialText );
425 imfManager.NotifyCursorPosition();
428 if( IsScrollEnabled() )
430 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
433 ShowGrabHandleAndSetVisibility( false );
442 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
444 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
446 mDisplayedTextView.SetText( styleText );
447 mPlaceHolderSet = false;
449 // If text alignment hasn't been manually set by application developer, then we
450 // automatically determine the alignment based on the content of the text i.e. what
451 // language the text begins with.
452 // TODO: This should determine different alignments for each line (broken by '\n') of text.
453 if(!mOverrideAutomaticAlignment)
455 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
456 bool leftToRight(true);
458 if( !styleText.empty() )
460 bool breakOut(false);
462 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
464 const Text& text = textIter->mText;
466 for( std::size_t i = 0; i < text.GetLength(); ++i )
468 Character character( text[i] );
469 if( character.GetCharacterDirection() != Character::Neutral )
471 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
479 // Based on this direction, either left or right align text if not manually set by application developer.
480 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
481 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
482 Toolkit::Alignment::VerticalTop ) );
483 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
489 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
491 mMaxStringLength = maxChars;
494 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
496 DALI_ASSERT_DEBUG( maxLines > 0 )
500 mNumberOflinesLimit = maxLines;
504 std::size_t TextInput::GetNumberOfLinesLimit() const
506 return mNumberOflinesLimit;
509 std::size_t TextInput::GetNumberOfCharacters() const
511 return mStyledText.size();
515 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
517 mMaterialColor = color;
518 if ( mCustomMaterial )
520 mCustomMaterial.SetDiffuseColor( mMaterialColor );
521 mMeshData.SetMaterial( mCustomMaterial );
525 const Vector4& TextInput::GetMaterialDiffuseColor() const
527 return mMaterialColor;
532 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
534 return mInputStartedSignalV2;
537 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
539 return mInputFinishedSignalV2;
542 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
544 return mCutAndPasteToolBarDisplayedV2;
547 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
549 return mStyleChangedSignalV2;
552 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
554 return mTextModifiedSignal;
557 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
559 return mMaxInputCharactersReachedSignalV2;
562 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
564 return mInputTextExceedBoundariesSignalV2;
567 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
569 Dali::BaseHandle handle( object );
571 bool connected( true );
572 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
574 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
576 textInput.InputStartedSignal().Connect( tracker, functor );
578 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
580 textInput.InputFinishedSignal().Connect( tracker, functor );
582 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
584 textInput.StyleChangedSignal().Connect( tracker, functor );
586 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
588 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
590 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
592 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
596 // signalName does not match any signal
603 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
607 // update line height before calculate the actual position.
612 if( setCursorOnTouchPoint )
614 // Sets the cursor position for the given touch point.
615 ReturnClosestIndex( touchPoint, mCursorPosition );
617 // Creates the grab handle.
618 if( IsGrabHandleEnabled() )
620 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
624 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
625 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
626 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
627 ShowGrabHandleAndSetVisibility( true );
629 // Scrolls the text-view if needed.
630 if( IsScrollEnabled() )
632 ScrollTextViewToMakeCursorVisible( cursorPosition );
638 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
650 bool TextInput::IsEditable() const
652 return mEditModeActive;
655 void TextInput::SetEditOnTouch( bool editOnTouch )
657 mEditOnTouch = editOnTouch;
660 bool TextInput::IsEditOnTouch() const
665 void TextInput::SetTextSelectable( bool textSelectable )
667 mTextSelection = textSelectable;
670 bool TextInput::IsTextSelectable() const
672 return mTextSelection;
675 bool TextInput::IsTextSelected() const
677 return mHighlightMeshActor;
680 void TextInput::DeSelectText()
687 void TextInput::SetGrabHandleImage(Dali::Image image )
691 CreateGrabHandle(image);
695 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
697 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
701 mCursor.SetImage( image );
702 mCursor.SetNinePatchBorder( border );
706 Vector3 TextInput::GetSelectionHandleSize()
708 return DEFAULT_SELECTION_HANDLE_SIZE;
711 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
713 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
717 mCursorRTL.SetImage( image);
718 mCursorRTL.SetNinePatchBorder( border );
722 void TextInput::EnableGrabHandle(bool toggle)
724 // enables grab handle with will in turn de-activate magnifier
725 mGrabHandleEnabled = toggle;
728 bool TextInput::IsGrabHandleEnabled()
730 // if false then magnifier will be shown instead.
731 return mGrabHandleEnabled;
734 void TextInput::EnableSelectionHandleFlip( bool toggle )
736 // Deprecated function. To be removed.
737 mIsSelectionHandleFlipEnabled = toggle;
740 bool TextInput::IsSelectionHandleFlipEnabled()
742 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
746 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
748 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
749 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
750 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
752 mSelectionHandleFlipMargin = margin;
755 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
757 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
758 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
760 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
761 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
763 const Vector4 boundary( originX,
765 originX + boundingRectangle.width,
766 originY + boundingRectangle.height );
768 mBoundingRectangleWorldCoordinates = boundary;
771 const Rect<float> TextInput::GetBoundingRectangle() const
773 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
775 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
776 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
778 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
783 const Vector4& TextInput::GetSelectionHandleFlipMargin()
785 return mSelectionHandleFlipMargin;
788 void TextInput::SetTextColor( const Vector4& color )
790 mDisplayedTextView.SetColor( color );
793 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
795 if( style != mInputStyle )
798 bool emitSignal = false;
800 // mask: modify style according to mask, if different emit signal.
801 const TextStyle oldInputStyle( mInputStyle );
803 // Copy the new style.
804 mInputStyle.Copy( style, mask );
806 // if style has changed, emit signal.
807 if( oldInputStyle != mInputStyle )
812 // Updates the line height accordingly with the input style.
815 // Changing font point size will require the cursor to be re-sized
820 EmitStyleChangedSignal();
825 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
827 if ( IsTextSelected() )
829 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
830 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
832 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
834 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
837 // Keeps the old style to be compared with the new one.
838 const TextStyle oldInputStyle( mInputStyle );
840 // Copy only those parameters from the style which are set in the mask.
841 mInputStyle.Copy( style, mask );
843 if( mInputStyle != oldInputStyle )
845 // Updates the line height accordingly with the input style.
848 EmitStyleChangedSignal();
853 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
855 if( !mStyledText.empty() )
857 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
861 TextStyle TextInput::GetStyleAtCursor() const
865 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
867 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
868 style = mStyledText.at( mCursorPosition-1 ).mStyle;
874 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
876 Dali::Font defaultFont = Dali::Font::New();
877 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
884 TextStyle TextInput::GetStyleAt( std::size_t position ) const
886 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
888 if( position >= mStyledText.size() )
890 position = mStyledText.size() - 1;
893 return mStyledText.at( position ).mStyle;
896 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
898 mDisplayedTextView.SetTextAlignment( align );
899 mOverrideAutomaticAlignment = true;
902 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
904 mDisplayedTextView.SetLineJustification( justification );
905 mOverrideAutomaticAlignment = true;
908 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
910 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
913 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
915 return mDisplayedTextView.GetFadeBoundary();
918 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
920 return mDisplayedTextView.GetTextAlignment();
923 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
925 mDisplayedTextView.SetMultilinePolicy( policy );
928 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
930 return mDisplayedTextView.GetMultilinePolicy();
933 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
935 mDisplayedTextView.SetWidthExceedPolicy( policy );
938 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
940 return mDisplayedTextView.GetWidthExceedPolicy();
943 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
945 mDisplayedTextView.SetHeightExceedPolicy( policy );
948 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
950 return mDisplayedTextView.GetHeightExceedPolicy();
953 void TextInput::SetExceedEnabled( bool enable )
955 mExceedEnabled = enable;
958 bool TextInput::GetExceedEnabled() const
960 return mExceedEnabled;
963 void TextInput::SetBackground(Dali::Image image )
965 // TODO Should add this function and add public api to match.
968 bool TextInput::OnTouchEvent(const TouchEvent& event)
973 bool TextInput::OnKeyEvent(const KeyEvent& event)
975 switch( event.state )
979 return OnKeyDownEvent(event);
985 return OnKeyUpEvent(event);
997 void TextInput::OnKeyInputFocusGained()
999 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1001 mEditModeActive = true;
1003 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1005 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1007 // Updates the line height accordingly with the input style.
1010 // Connect the signals to use in text input.
1011 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1012 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1014 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1017 GetTextLayoutInfo();
1020 SetCursorVisibility( true );
1021 StartCursorBlinkTimer();
1023 Toolkit::TextInput handle( GetOwner() );
1024 mInputStartedSignalV2.Emit( handle );
1026 ImfManager imfManager = ImfManager::Get();
1030 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1032 // Notify that the text editing start.
1033 imfManager.Activate();
1035 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1036 imfManager.SetRestoreAferFocusLost( true );
1038 imfManager.SetCursorPosition( mCursorPosition );
1039 imfManager.NotifyCursorPosition();
1042 mClipboard = Clipboard::Get(); // Store handle to clipboard
1044 // Now in edit mode we can accept string to paste from clipboard
1045 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1048 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1052 void TextInput::OnKeyInputFocusLost()
1054 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1058 // If key input focus is lost, it removes the
1059 // underline from the last pre-edit text.
1060 RemovePreEditStyle();
1061 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1062 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1066 ImfManager imfManager = ImfManager::Get();
1069 // The text editing is finished. Therefore the imf manager don't have restore activation.
1070 imfManager.SetRestoreAferFocusLost( false );
1072 // Notify that the text editing finish.
1073 imfManager.Deactivate();
1075 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1077 // Disconnect signal used the text input.
1078 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1080 Toolkit::TextInput handle( GetOwner() );
1081 mInputFinishedSignalV2.Emit( handle );
1082 mEditModeActive = false;
1083 mPreEditFlag = false;
1085 SetCursorVisibility( false );
1086 StopCursorBlinkTimer();
1088 ShowGrabHandleAndSetVisibility( false );
1091 // No longer in edit mode so do not want to receive string from clipboard
1092 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1095 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1098 Clipboard clipboard = Clipboard::Get();
1101 clipboard.HideClipboard();
1105 void TextInput::OnControlStageConnection()
1107 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1109 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1111 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1115 void TextInput::CreateActiveLayer()
1117 Actor self = Self();
1118 mActiveLayer = Layer::New();
1119 mActiveLayer.SetName ( "ActiveLayerActor" );
1121 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1122 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1123 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1125 self.Add( mActiveLayer );
1126 mActiveLayer.RaiseToTop();
1129 void TextInput::OnInitialize()
1131 CreateTextViewActor();
1135 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1136 // different positions depending on language)
1137 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1138 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1140 Actor self = Self();
1141 self.Add( mCursor );
1142 self.Add( mCursorRTL );
1144 mCursorVisibility = false;
1146 CreateActiveLayer(); // todo move this so layer only created when needed.
1148 // Assign names to image actors
1149 mCursor.SetName("mainCursor");
1150 mCursorRTL.SetName("rtlCursor");
1153 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1155 mDisplayedTextView.SetSize( targetSize );
1156 GetTextLayoutInfo();
1157 mActiveLayer.SetSize(targetSize);
1160 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1162 Relayout( mDisplayedTextView, size, container );
1163 Relayout( mPopupPanel.GetRootActor(), size, container );
1165 GetTextLayoutInfo();
1170 Vector3 TextInput::GetNaturalSize()
1172 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1174 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1176 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1177 naturalSize.height = mLineHeight;
1183 float TextInput::GetHeightForWidth( float width )
1185 float height = mDisplayedTextView.GetHeightForWidth( width );
1187 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1189 // If the height is zero, it means there is no text. Let's return the cursor height.
1190 height = mLineHeight;
1196 /*end of Virtual methods from parent*/
1198 // Private Internal methods
1200 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1202 switch (gesture.state)
1204 case Gesture::Started:
1205 // fall through so code not duplicated
1206 case Gesture::Continuing:
1208 if (actor == mGrabArea)
1210 SetCursorVisibility( true );
1211 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1212 MoveGrabHandle( gesture.displacement );
1213 HidePopup(); // Do not show popup whilst handle is moving
1215 else if (actor == mHandleOneGrabArea)
1217 // the displacement in PanGesture is affected by the actor's rotation.
1218 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1219 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1221 MoveSelectionHandle( HandleOne, gesture.displacement );
1223 mState = StateDraggingHandle;
1226 else if (actor == mHandleTwoGrabArea)
1228 // the displacement in PanGesture is affected by the actor's rotation.
1229 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1230 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1232 MoveSelectionHandle( HandleTwo, gesture.displacement );
1234 mState = StateDraggingHandle;
1240 case Gesture::Finished:
1242 // Revert back to non-pressed selection handle images
1243 if (actor == mGrabArea)
1245 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1246 SetCursorVisibility( true );
1247 SetUpPopupSelection();
1250 if (actor == mHandleOneGrabArea)
1252 // the displacement in PanGesture is affected by the actor's rotation.
1253 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1254 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1256 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1258 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1260 ShowPopupCutCopyPaste();
1262 if (actor == mHandleTwoGrabArea)
1264 // the displacement in PanGesture is affected by the actor's rotation.
1265 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1268 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1270 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1272 ShowPopupCutCopyPaste();
1281 // Stop the flashing animation so easy to see when moved.
1282 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1284 if (touch.GetPoint(0).state == TouchPoint::Down)
1286 SetCursorVisibility( true );
1287 StopCursorBlinkTimer();
1289 else if (touch.GetPoint(0).state == TouchPoint::Up)
1291 SetCursorVisibility( true );
1292 StartCursorBlinkTimer();
1297 // selection handle one
1298 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1300 if (touch.GetPoint(0).state == TouchPoint::Down)
1302 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1304 else if (touch.GetPoint(0).state == TouchPoint::Up)
1306 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1311 // selection handle two
1312 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1314 if (touch.GetPoint(0).state == TouchPoint::Down)
1316 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1318 else if (touch.GetPoint(0).state == TouchPoint::Up)
1320 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1325 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1327 // If text exists then select nearest word.
1328 if ( !mStyledText.empty())
1332 ShowGrabHandleAndSetVisibility( false );
1337 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1338 // converts the pre-edit word being displayed to a committed word.
1339 if ( !mUnderlinedPriorToPreEdit )
1342 style.SetUnderline( false );
1343 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1345 mPreEditFlag = false;
1346 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1347 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1348 PreEditReset( false );
1350 mCursorPosition = 0;
1352 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1353 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1355 std::size_t start = 0;
1356 std::size_t end = 0;
1357 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1359 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1361 ImfManager imfManager = ImfManager::Get();
1364 imfManager.SetCursorPosition ( mCursorPosition );
1365 imfManager.NotifyCursorPosition();
1368 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1370 SelectText( start, end );
1371 ShowPopupCutCopyPaste();
1375 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1376 HidePopup( false ); // Hide popup with setting to do auto show.
1377 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1381 else if ( mClipboard && mClipboard.NumberOfItems() )
1383 ShowPopupCutCopyPaste();
1386 // If no text and clipboard empty then do nothing
1389 // TODO: Change the function name to be more general.
1390 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1392 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1393 , (mEditOnTouch)?"true":"false"
1394 , (mEditModeActive)?"true":"false");
1396 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1401 if( mGrabArea == actor )
1403 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1405 SetUpPopupSelection();
1415 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1417 // Initially don't create the grab handle.
1418 bool createGrabHandle = false;
1420 if ( !mEditModeActive )
1422 // update line height before calculate the actual position.
1425 // Only start edit mode if TextInput configured to edit on touch
1428 // Set the initial cursor position in the tap point.
1429 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1435 // Show the keyboard if it was hidden.
1436 if (!VirtualKeyboard::IsVisible())
1438 VirtualKeyboard::Show();
1441 // Reset keyboard as tap event has occurred.
1442 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1443 PreEditReset( true );
1445 GetTextLayoutInfo();
1447 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1449 // 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.
1451 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1453 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1455 // Notify keyboard so it can 're-capture' word for predictive text.
1456 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1457 ImfManager imfManager = ImfManager::Get();
1460 imfManager.SetCursorPosition ( mCursorPosition );
1461 imfManager.NotifyCursorPosition();
1463 const TextStyle oldInputStyle( mInputStyle );
1465 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1469 // Create the grab handle.
1470 // Grab handle is created later.
1471 createGrabHandle = true;
1473 if( oldInputStyle != mInputStyle )
1475 // Updates the line height accordingly with the input style.
1478 EmitStyleChangedSignal();
1483 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1484 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1485 // otherwise the Grab handle will be shown when selecting.
1486 if ( createGrabHandle && IsGrabHandleEnabled() )
1488 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1492 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1493 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1494 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1495 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1500 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1502 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1504 // Ignore longpress if in selection mode already
1505 if( mHighlightMeshActor )
1510 if(longPress.state == Dali::Gesture::Started)
1512 // Start edit mode on long press
1513 if ( !mEditModeActive )
1518 // If text exists then select nearest word.
1519 if ( !mStyledText.empty())
1523 ShowGrabHandleAndSetVisibility( false );
1528 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1529 // converts the pre-edit word being displayed to a committed word.
1530 if ( !mUnderlinedPriorToPreEdit )
1533 style.SetUnderline( false );
1534 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1536 mPreEditFlag = false;
1537 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1538 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1539 PreEditReset( false );
1541 mCursorPosition = 0;
1543 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1544 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1546 std::size_t start = 0;
1547 std::size_t end = 0;
1548 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1550 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1552 ImfManager imfManager = ImfManager::Get();
1555 imfManager.SetCursorPosition ( mCursorPosition );
1556 imfManager.NotifyCursorPosition();
1559 SelectText( start, end );
1562 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1563 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1565 ShowPopupCutCopyPaste();
1570 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1572 const Text clipboardText( notifier.GetContent() );
1573 PasteText( clipboardText );
1575 SetCursorVisibility( true );
1576 StartCursorBlinkTimer();
1578 ShowGrabHandleAndSetVisibility( false );
1584 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1586 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1588 const std::string& name = button.GetName();
1590 if(name == TextInputPopup::OPTION_SELECT_WORD)
1592 std::size_t start = 0;
1593 std::size_t end = 0;
1594 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1596 SelectText( start, end );
1598 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1600 SetCursorVisibility(false);
1601 StopCursorBlinkTimer();
1603 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1604 std::size_t start = 0;
1606 SelectText( start, end );
1608 else if(name == TextInputPopup::OPTION_CUT)
1610 bool ret = CopySelectedTextToClipboard();
1614 DeleteHighlightedText( true );
1618 SetCursorVisibility( true );
1619 StartCursorBlinkTimer();
1623 else if(name == TextInputPopup::OPTION_COPY)
1625 CopySelectedTextToClipboard();
1629 SetCursorVisibility( true );
1630 StartCursorBlinkTimer();
1634 else if(name == TextInputPopup::OPTION_PASTE)
1636 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1638 PasteText(retrievedString);
1640 SetCursorVisibility( true );
1641 StartCursorBlinkTimer();
1643 ShowGrabHandleAndSetVisibility( false );
1647 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1649 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1650 // Hence pass the false parameter for signalFinished.
1651 HidePopup( true, false );
1652 mClipboard.ShowClipboard();
1658 bool TextInput::OnCursorBlinkTimerTick()
1661 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1662 if ( mCursorRTLEnabled )
1664 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1666 mCursorBlinkStatus = !mCursorBlinkStatus;
1671 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1673 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1675 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1676 if(mHighlightMeshActor && mState == StateEdit)
1678 ShowPopupCutCopyPaste();
1682 //FIXME this routine needs to be re-written as it contains too many branches.
1683 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1685 std::string keyName = event.keyPressedName;
1686 std::string keyString = event.keyPressed;
1688 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1690 // Do not consume "Tab" and "Escape" keys.
1691 if(keyName == "Tab" || keyName == "Escape")
1693 // Escape key to end the edit mode
1699 HidePopup(); // If Pop-up shown then hides it as editing text.
1701 // Update Flag, indicates whether to update the text-input contents or not.
1702 // Any key stroke that results in a visual change of the text-input should
1703 // set this flag to true.
1706 // Whether to scroll text to cursor position.
1707 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1708 bool scroll = false;
1710 if (keyName == "Return")
1712 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1714 bool preEditFlagPreviouslySet( mPreEditFlag );
1716 // replaces highlighted text with new line
1717 DeleteHighlightedText( false );
1719 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1721 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1722 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1725 mCommitByKeyInput = true;
1728 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1729 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1731 mPreEditFlag = true;
1732 mIgnoreCommitFlag = false;
1742 else if ( keyName == "space" )
1744 if ( mHighlightMeshActor )
1746 // Some text is selected so erase it before adding space.
1747 DeleteHighlightedText( true );
1751 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1753 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1754 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1757 mCommitByKeyInput = true;
1762 else if (keyName == "BackSpace")
1764 if ( mHighlightMeshActor )
1766 // Some text is selected so erase it
1767 DeleteHighlightedText( true );
1772 if ( mCursorPosition > 0 )
1774 DeleteCharacter( mCursorPosition );
1780 else if (keyName == "Right")
1785 else if (keyName == "Left")
1787 AdvanceCursor(true);
1790 else // event is a character
1792 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1793 if ( !keyString.empty() )
1795 // replaces highlighted text with new character
1796 DeleteHighlightedText( false );
1798 // Received key String
1799 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1805 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1806 // as this is a costly operation.
1812 if(update || scroll)
1814 if( IsScrollEnabled() )
1816 // Calculates the new cursor position (in actor coordinates)
1817 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1819 ScrollTextViewToMakeCursorVisible( cursorPosition );
1826 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1828 std::string keyName = event.keyPressedName;
1829 std::string keyString = event.keyPressed;
1831 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1833 // The selected text become deselected when the key code is DALI_KEY_BACK.
1834 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1843 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1845 // Updates the stored scroll position.
1846 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1848 const Vector3& controlSize = GetControlSize();
1849 Size cursorSize( CURSOR_THICKNESS, 0.f );
1851 // Updates the cursor and grab handle position and visibility.
1852 if( mGrabHandle || mCursor )
1854 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1855 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1857 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1859 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1863 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1864 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1869 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1870 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1874 // Updates the selection handles and highlighted text position and visibility.
1875 if( mSelectionHandleOne && mSelectionHandleTwo )
1877 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1878 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1879 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1880 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1881 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1882 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1884 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1885 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1887 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1888 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1889 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1890 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1892 if( mHighlightMeshActor )
1894 mHighlightMeshActor.SetVisible( true );
1900 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1902 // Scroll the text to make the cursor visible.
1903 const Size cursorSize( CURSOR_THICKNESS,
1904 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1906 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1908 const Vector3& controlSize = GetControlSize();
1910 // Calculates the new scroll position.
1911 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1912 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1914 scrollOffset.x += cursorPosition.x;
1917 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1919 scrollOffset.y += cursorPosition.y;
1922 // Sets the new scroll position.
1923 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1924 SetScrollPosition( scrollOffset );
1927 void TextInput::StartScrollTimer()
1931 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1932 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1935 if( !mScrollTimer.IsRunning() )
1937 mScrollTimer.Start();
1941 void TextInput::StopScrollTimer()
1945 mScrollTimer.Stop();
1949 bool TextInput::OnScrollTimerTick()
1951 // TODO: need to set the new style accordingly the new handle position.
1953 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1955 // nothing to do if all handles are invisible or doesn't exist.
1961 // Choose between the grab handle or the selection handles.
1962 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1963 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1964 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1966 std::size_t newCursorPosition = 0;
1967 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1969 // Whether the handle's position is different of the previous one and in the case of the selection handle,
1970 // the new selection handle's position needs to be different of the other one.
1971 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1972 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1973 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1975 if( differentSelectionHandles )
1977 handlePosition = newCursorPosition;
1979 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1981 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1983 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1984 scrollPosition += scrollDelta;
1985 SetScrollPosition( scrollPosition );
1987 if( mDisplayedTextView.IsScrollPositionTrimmed() )
1992 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
1995 actualHandlePosition.x += mScrollDisplacement.x;
1996 actualHandlePosition.y += mScrollDisplacement.y;
2001 // Public Internal Methods (public for testing purpose)
2003 void TextInput::SetUpTouchEvents()
2005 if ( !mTapDetector )
2007 mTapDetector = TapGestureDetector::New();
2008 // Attach the actors and connect the signal
2009 mTapDetector.Attach(Self());
2011 // As contains children which may register for tap the default control detector is not used.
2012 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2015 if ( !mDoubleTapDetector )
2017 mDoubleTapDetector = TapGestureDetector::New();
2018 mDoubleTapDetector.SetTapsRequired( 2 );
2019 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2021 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2022 // so that we do not, unnecessarily, have a double tap request all the time
2025 if ( !mPanGestureDetector )
2027 mPanGestureDetector = PanGestureDetector::New();
2028 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2031 if ( !mLongPressDetector )
2033 mLongPressDetector = LongPressGestureDetector::New();
2034 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2035 mLongPressDetector.Attach(Self());
2039 void TextInput::CreateTextViewActor()
2041 mDisplayedTextView = Toolkit::TextView::New();
2042 mDisplayedTextView.SetName( "DisplayedTextView ");
2043 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2044 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2045 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2046 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2047 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2048 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2049 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2050 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2051 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2052 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2054 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2056 Self().Add( mDisplayedTextView );
2059 // Start a timer to initiate, used by the cursor to blink.
2060 void TextInput::StartCursorBlinkTimer()
2062 if ( !mCursorBlinkTimer )
2064 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2065 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2068 if ( !mCursorBlinkTimer.IsRunning() )
2070 mCursorBlinkTimer.Start();
2074 // Start a timer to initiate, used by the cursor to blink.
2075 void TextInput::StopCursorBlinkTimer()
2077 if ( mCursorBlinkTimer )
2079 mCursorBlinkTimer.Stop();
2083 void TextInput::StartEditMode()
2085 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2087 if(!mEditModeActive)
2092 if ( mDoubleTapDetector )
2094 mDoubleTapDetector.Attach( Self() );
2098 void TextInput::EndEditMode()
2100 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2102 ClearKeyInputFocus();
2104 if ( mDoubleTapDetector )
2106 mDoubleTapDetector.Detach( Self() );
2110 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2112 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2114 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2116 style.SetUnderline( true );
2117 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2121 void TextInput::RemovePreEditStyle()
2123 if ( !mUnderlinedPriorToPreEdit )
2126 style.SetUnderline( false );
2127 SetActiveStyle( style, TextStyle::UNDERLINE );
2131 // IMF related methods
2134 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2136 bool update( false );
2137 bool preeditResetRequired ( false );
2139 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2141 HidePopup(); // If Pop-up shown then hides it as editing text.
2144 switch ( imfEvent.eventName )
2146 case ImfManager::PREEDIT:
2148 mIgnoreFirstCommitFlag = false;
2150 // 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
2151 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2153 // replaces highlighted text with new character
2154 DeleteHighlightedText( false );
2157 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2159 if( IsScrollEnabled() )
2161 // Calculates the new cursor position (in actor coordinates)
2162 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2163 ScrollTextViewToMakeCursorVisible( cursorPosition );
2170 case ImfManager::COMMIT:
2172 if( mIgnoreFirstCommitFlag )
2174 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2175 mIgnoreFirstCommitFlag = false;
2179 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2181 // 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
2182 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2184 // replaces highlighted text with new character
2185 DeleteHighlightedText( false );
2188 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2189 // not needed, one such scenario is when the pre-edit word is too long to fit.
2190 if ( !mIgnoreCommitFlag )
2192 update = CommitReceived( imfEvent.predictiveString );
2196 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2202 if( IsScrollEnabled() )
2204 // Calculates the new cursor position (in actor coordinates)
2205 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2207 ScrollTextViewToMakeCursorVisible( cursorPosition );
2212 case ImfManager::DELETESURROUNDING:
2214 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2215 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2217 mPreEditFlag = false;
2219 std::size_t toDelete = 0;
2220 std::size_t numberOfCharacters = 0;
2222 if( mHighlightMeshActor )
2224 // delete highlighted text.
2225 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2226 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2230 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2232 toDelete = mCursorPosition + imfEvent.cursorOffset;
2234 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2236 numberOfCharacters = mStyledText.size() - toDelete;
2240 numberOfCharacters = imfEvent.numberOfChars;
2243 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2244 DeleteRange( toDelete, numberOfCharacters );
2246 mCursorPosition = toDelete;
2247 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2251 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2254 case ImfManager::GETSURROUNDING:
2256 // 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
2257 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2258 if (! ( mHighlightMeshActor || mSelectingText ) )
2260 std::string text( GetText() );
2261 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2263 imfManager.SetCursorPosition( mCursorPosition );
2264 imfManager.SetSurroundingText( text );
2267 if( 0 != mNumberOfSurroundingCharactersDeleted )
2269 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2270 mNumberOfSurroundingCharactersDeleted = 0;
2272 if( mStyledText.empty() )
2274 // Styled text is empty, so set the placeholder text.
2275 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2276 mPlaceHolderSet = true;
2281 case ImfManager::VOID:
2283 DALI_ASSERT_DEBUG( false );
2287 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2289 return callbackData;
2292 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2294 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2296 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2297 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2299 bool preeditResetRequest ( false );
2301 if( mPreEditFlag ) // Already in pre-edit state.
2303 if( mStyledText.size() >= mMaxStringLength )
2305 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2306 // Cannot fit these characters into field, clear pre-edit.
2307 if ( !mUnderlinedPriorToPreEdit )
2310 style.SetUnderline( false );
2311 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2313 mIgnoreCommitFlag = true;
2314 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2315 mPreEditFlag = false;
2316 EmitMaxInputCharactersReachedSignal();
2320 // delete existing pre-edit string
2321 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2323 // Store new pre-edit string
2324 mPreEditString.SetText( keyString );
2326 if ( keyString.empty() )
2328 mPreEditFlag = false;
2329 mCursorPosition = mPreEditStartPosition;
2331 if( mStyledText.empty() )
2333 // Styled text is empty, so set the placeholder text.
2334 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2335 mPlaceHolderSet = true;
2339 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2341 GetTextLayoutInfo();
2346 // Insert new pre-edit string. InsertAt updates the size and position table.
2347 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2348 // 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.
2349 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2350 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2351 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2354 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2358 else // mPreEditFlag not set
2360 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2362 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2363 // new pre-edit so move into pre-edit state by setting flag
2364 mPreEditFlag = true;
2365 mPreEditString.SetText( keyString ); // store new pre-edit string
2366 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2367 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2368 // 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.
2369 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2370 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2371 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2372 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2378 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2382 return preeditResetRequest;
2385 bool TextInput::CommitReceived(const std::string& keyString )
2387 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2388 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2390 bool update( false );
2392 RemovePreEditStyle();
2394 const std::size_t styledTextSize( mStyledText.size() );
2395 if( styledTextSize >= mMaxStringLength )
2397 // Cannot fit these characters into field, clear pre-edit.
2400 mIgnoreCommitFlag = true;
2401 mPreEditFlag = false;
2403 EmitMaxInputCharactersReachedSignal();
2409 // delete existing pre-edit string
2410 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2411 mPreEditFlag = false;
2413 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2414 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2416 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2418 // No need to update cursor position as Cursor location given by touch.
2419 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2420 mPreserveCursorPosition = false;
2424 // Cursor not set by touch so needs to be re-positioned to input more text
2425 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2427 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2428 if ( mCommitByKeyInput )
2430 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2431 mCommitByKeyInput = false;
2437 if ( mSelectTextOnCommit )
2439 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2444 else // mPreEditFlag not set
2446 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2448 if( mStyledText.empty() && mPlaceHolderSet )
2450 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2451 mDisplayedTextView.SetText( "" );
2452 mNumberOfSurroundingCharactersDeleted = 0;
2453 mPlaceHolderSet = false;
2455 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2457 mNumberOfSurroundingCharactersDeleted = 0;
2462 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2467 mSelectTextOnCommit = false;
2469 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2470 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2475 // End of IMF related methods
2477 std::size_t TextInput::DeletePreEdit()
2479 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2481 DALI_ASSERT_DEBUG( mPreEditFlag );
2483 const std::size_t preEditStringLength = mPreEditString.GetLength();
2484 const std::size_t styledTextSize = mStyledText.size();
2486 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2488 // Prevents erase items outside mStyledText bounds.
2489 if( mPreEditStartPosition > styledTextSize )
2491 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2492 mPreEditStartPosition = styledTextSize;
2495 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2497 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2498 endPosition = styledTextSize;
2501 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2503 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2504 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2506 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2508 return preEditStringLength;
2511 void TextInput::PreEditReset( bool preserveCursorPosition )
2513 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2514 preserveCursorPosition, mCursorPosition);
2516 // 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.
2517 mPreserveCursorPosition = preserveCursorPosition;
2519 // Reset incase we are in a pre-edit state.
2520 ImfManager imfManager = ImfManager::Get();
2523 imfManager.Reset(); // Will trigger a commit message
2527 void TextInput::CursorUpdate()
2531 ImfManager imfManager = ImfManager::Get();
2534 std::string text( GetText() );
2535 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2536 imfManager.SetCursorPosition ( mCursorPosition );
2537 imfManager.NotifyCursorPosition();
2541 /* Delete highlighted characters redisplay*/
2542 void TextInput::DeleteHighlightedText( bool inheritStyle )
2544 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2546 if( mHighlightMeshActor )
2548 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2550 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2551 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2553 // Get the styled text of the characters to be deleted as it may be needed if
2554 // the "exceed the text-input's boundaries" option is disabled.
2555 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2557 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2559 mStyledText.erase( start, end ); // erase range of characters
2561 // Remove text from TextView.
2563 if( mStyledText.empty() )
2565 // Styled text is empty, so set the placeholder text.
2566 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2567 mPlaceHolderSet = true;
2571 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2573 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2575 // It may happen than after removing a white space or a new line character,
2576 // two words merge, this new word could be big enough to not fit in its
2577 // current line, so moved to the next one, and make some part of the text to
2578 // exceed the text-input's boundary.
2579 if( !mExceedEnabled )
2581 // Get the new text layout after removing some characters.
2582 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2584 // Get text-input's size.
2585 const Vector3& size = GetControlSize();
2587 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2588 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2590 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2592 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2593 styledCharactersToDelete.begin(),
2594 styledCharactersToDelete.end() );
2598 GetTextLayoutInfo();
2606 const TextStyle oldInputStyle( mInputStyle );
2608 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2610 if( oldInputStyle != mInputStyle )
2612 // Updates the line height accordingly with the input style.
2615 EmitStyleChangedSignal();
2621 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2623 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2624 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2626 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2629 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2631 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2632 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2634 mStyledText.erase(itStart, itEnd);
2636 // update the selection handles if they are visible.
2637 if( mHighlightMeshActor )
2639 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2640 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2642 if( minHandle >= start + ncharacters )
2644 minHandle -= ncharacters;
2646 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2651 if( maxHandle >= start + ncharacters )
2653 maxHandle -= ncharacters;
2655 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2661 // 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.
2664 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2666 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2667 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2668 // Mean we do not re-draw the text more than we have too.
2671 /* Delete character at current cursor position and redisplay*/
2672 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2674 // Ensure positionToDelete is not out of bounds.
2675 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2676 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2677 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2679 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2682 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2684 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2686 // Get the styled text of the character to be deleted as it may be needed if
2687 // the "exceed the text-input's boundaries" option is disabled.
2688 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2690 mStyledText.erase(it); // erase the character left of positionToDelete
2692 if( mStyledText.empty() )
2694 // Styled text is empty, so set the placeholder text.
2695 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2696 mPlaceHolderSet = true;
2700 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2702 const Character characterToDelete = styledCharacterToDelete.mText[0];
2704 // It may happen than after removing a white space or a new line character,
2705 // two words merge, this new word could be big enough to not fit in its
2706 // current line, so moved to the next one, and make some part of the text to
2707 // exceed the text-input's boundary.
2708 if( !mExceedEnabled )
2710 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2712 // Get the new text layout after removing one character.
2713 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2715 // Get text-input's size.
2716 const Vector3& size = GetControlSize();
2718 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2719 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2721 MarkupProcessor::StyledTextArray array;
2722 array.push_back( styledCharacterToDelete );
2723 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2725 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2730 GetTextLayoutInfo();
2732 ShowGrabHandleAndSetVisibility( false );
2734 mCursorPosition = positionToDelete -1;
2736 const TextStyle oldInputStyle( mInputStyle );
2738 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2740 if( oldInputStyle != mInputStyle )
2742 // Updates the line height accordingly with the input style.
2745 EmitStyleChangedSignal();
2750 /*Insert new character into the string and (optionally) redisplay text-input*/
2751 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2753 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2755 // Ensure insertionPosition is not out of bounds.
2756 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2758 bool textExceedsMaximunNumberOfCharacters = false;
2759 bool textExceedsBoundary = false;
2760 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2762 ShowGrabHandleAndSetVisibility( false );
2764 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2768 mIgnoreCommitFlag = true;
2769 mPreEditFlag = false;
2770 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2771 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2774 if( textExceedsMaximunNumberOfCharacters )
2776 EmitMaxInputCharactersReachedSignal();
2779 if( textExceedsBoundary )
2781 EmitInputTextExceedsBoundariesSignal();
2782 PreEditReset( false );
2786 return insertedStringLength;
2789 ImageActor TextInput::CreateCursor( const Vector4& color)
2792 cursor = CreateSolidColorActor(color);
2793 cursor.SetName( "Cursor" );
2795 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2796 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2797 cursor.SetVisible(false);
2802 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2804 // As cursor is not moving due to grab handle, handle should be hidden.
2805 ShowGrabHandleAndSetVisibility( false );
2807 bool cursorPositionChanged = false;
2810 if ( mCursorPosition >= places )
2812 mCursorPosition = mCursorPosition - places;
2813 cursorPositionChanged = true;
2818 if ((mCursorPosition + places) <= mStyledText.size())
2820 mCursorPosition = mCursorPosition + places;
2821 cursorPositionChanged = true;
2825 if( cursorPositionChanged )
2827 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2829 const TextStyle oldInputStyle( mInputStyle );
2830 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2834 if( oldInputStyle != mInputStyle )
2836 // Updates the line height accordingly with the input style.
2839 EmitStyleChangedSignal();
2842 ImfManager imfManager = ImfManager::Get();
2845 imfManager.SetCursorPosition ( mCursorPosition );
2846 imfManager.NotifyCursorPosition();
2851 void TextInput::DrawCursor(const std::size_t nthChar)
2853 // Get height of cursor and set its size
2854 Size size( CURSOR_THICKNESS, 0.0f );
2855 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2857 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2861 // Measure Font so know how big text will be if no initial text to measure.
2862 size.height = mLineHeight;
2865 mCursor.SetSize(size);
2867 // If the character is italic then the cursor also tilts.
2868 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2870 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2872 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2874 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2875 bool altPositionValid; // Alternate cursor validity flag.
2876 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2877 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2879 SetAltCursorEnabled( altPositionValid );
2881 if(!altPositionValid)
2883 mCursor.SetPosition( position + UI_OFFSET );
2887 size.height *= 0.5f;
2888 mCursor.SetSize(size);
2889 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2891 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2892 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2893 size.height = rowSize.height * 0.5f;
2894 mCursorRTL.SetSize(size);
2895 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2898 if( IsScrollEnabled() )
2900 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2901 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2906 void TextInput::SetAltCursorEnabled( bool enabled )
2908 mCursorRTLEnabled = enabled;
2909 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2912 void TextInput::SetCursorVisibility( bool visible )
2914 mCursorVisibility = visible;
2915 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2916 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2919 void TextInput::CreateGrabHandle( Dali::Image image )
2925 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2929 mGrabHandleImage = image;
2932 mGrabHandle = ImageActor::New(mGrabHandleImage);
2933 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2934 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2936 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2938 ShowGrabHandleAndSetVisibility( false );
2940 CreateGrabArea( mGrabHandle );
2942 mActiveLayer.Add(mGrabHandle);
2946 void TextInput::CreateGrabArea( Actor& parent )
2948 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2949 mGrabArea.SetName( "GrabArea" );
2950 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2951 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2952 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2953 mTapDetector.Attach( mGrabArea );
2954 mPanGestureDetector.Attach( mGrabArea );
2955 mLongPressDetector.Attach( mGrabArea );
2957 parent.Add(mGrabArea);
2960 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2962 Vector3 actualHandlePosition;
2966 mActualGrabHandlePosition.x += displacement.x;
2967 mActualGrabHandlePosition.y += displacement.y;
2969 // Grab handle should jump to the nearest character and take cursor with it
2970 std::size_t newCursorPosition = 0;
2971 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2973 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2975 bool handleVisible = true;
2977 if( IsScrollEnabled() )
2979 const Vector3 controlSize = GetControlSize();
2980 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2981 // Scrolls the text if the handle is not in a visible position
2982 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2989 mCurrentHandlePosition = actualHandlePosition;
2990 mScrollDisplacement = Vector2::ZERO;
2994 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
2996 mScrollDisplacement.x = -SCROLL_SPEED;
2998 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3000 mScrollDisplacement.x = SCROLL_SPEED;
3002 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3004 mScrollDisplacement.y = -SCROLL_SPEED;
3006 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3008 mScrollDisplacement.y = SCROLL_SPEED;
3014 if( handleVisible && // Only redraw cursor and do updates if position changed
3015 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3017 mCursorPosition = newCursorPosition;
3019 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3021 const TextStyle oldInputStyle( mInputStyle );
3023 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3025 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3027 if( oldInputStyle != mInputStyle )
3029 // Updates the line height accordingly with the input style.
3032 EmitStyleChangedSignal();
3037 return actualHandlePosition;
3040 void TextInput::ShowGrabHandle( bool visible )
3042 if ( IsGrabHandleEnabled() )
3046 mGrabHandle.SetVisible( mGrabHandleVisibility );
3048 StartMonitoringStageForTouch();
3052 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3054 mGrabHandleVisibility = visible;
3055 ShowGrabHandle( visible );
3058 // Callbacks connected to be Property notifications for Boundary checking.
3060 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3062 mIsSelectionHandleOneFlipped = true;
3063 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3064 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3067 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3069 mIsSelectionHandleOneFlipped = false;
3070 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3071 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3074 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3076 mIsSelectionHandleTwoFlipped = true;
3077 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3078 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3081 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3083 mIsSelectionHandleTwoFlipped = false;
3084 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3085 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3088 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3089 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3091 mSelectionHandleOne.SetOpacity(0.0f);
3094 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3096 mSelectionHandleOne.SetOpacity(1.0f);
3099 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3101 mSelectionHandleTwo.SetOpacity(0.0f);
3104 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3106 mSelectionHandleTwo.SetOpacity(1.0f);
3109 // End of Callbacks connected to be Property notifications for Boundary checking.
3111 void TextInput::SetUpHandlePropertyNotifications()
3113 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3115 Vector3 handlesize = GetSelectionHandleSize();
3117 // Exceeding horizontal boundary
3118 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3119 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3121 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3122 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3124 // Within horizontal boundary
3125 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3126 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3128 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3129 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3131 // Exceeding vertical boundary
3132 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3133 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3134 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3135 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3137 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3138 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3139 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3140 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3142 // Within vertical boundary
3143 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3144 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3145 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3146 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3148 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3149 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3150 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3151 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3154 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3156 mSelectionHandleOnePosition = start;
3157 mSelectionHandleTwoPosition = end;
3159 if ( !mSelectionHandleOne )
3161 // create normal and pressed images
3162 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3163 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3165 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3166 mSelectionHandleOne.SetName("SelectionHandleOne");
3167 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3168 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3169 mIsSelectionHandleOneFlipped = false;
3170 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3172 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3173 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3175 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3176 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3178 mTapDetector.Attach( mHandleOneGrabArea );
3179 mPanGestureDetector.Attach( mHandleOneGrabArea );
3181 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3183 mSelectionHandleOne.Add( mHandleOneGrabArea );
3184 mActiveLayer.Add( mSelectionHandleOne );
3187 if ( !mSelectionHandleTwo )
3189 // create normal and pressed images
3190 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3191 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3193 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3194 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3195 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3196 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3197 mIsSelectionHandleTwoFlipped = false;
3198 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3200 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3201 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3202 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3203 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3205 mTapDetector.Attach( mHandleTwoGrabArea );
3206 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3208 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3210 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3212 mActiveLayer.Add( mSelectionHandleTwo );
3215 SetUpHandlePropertyNotifications();
3217 // update table as text may have changed.
3218 GetTextLayoutInfo();
3220 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3221 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3223 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3224 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3226 // Calculates and set the visibility if the scroll mode is enabled.
3227 bool isSelectionHandleOneVisible = true;
3228 bool isSelectionHandleTwoVisible = true;
3229 if( IsScrollEnabled() )
3231 const Vector3& controlSize( GetControlSize() );
3232 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3233 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3234 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3235 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3238 CreateHighlight(); // function will only create highlight if not already created.
3241 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3243 Vector3 actualHandlePosition;
3245 if ( mSelectionHandleOne && mSelectionHandleTwo )
3247 const Vector3& controlSize = GetControlSize();
3249 Size cursorSize( CURSOR_THICKNESS, 0.f );
3251 // Get a reference of the wanted selection handle (handle one or two).
3252 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3254 // Get a reference for the current position of the handle and a copy of its pair
3255 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3256 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3258 // Get a handle of the selection handle actor
3259 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3261 // Selection handles should jump to the nearest character
3262 std::size_t newHandlePosition = 0;
3263 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3265 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3267 bool handleVisible = true;
3269 if( IsScrollEnabled() )
3271 mCurrentSelectionId = handleId;
3273 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3274 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3275 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3282 mCurrentSelectionHandlePosition = actualHandlePosition;
3283 mScrollDisplacement = Vector2::ZERO;
3287 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3289 mScrollDisplacement.x = -SCROLL_SPEED;
3291 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3293 mScrollDisplacement.x = SCROLL_SPEED;
3295 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3297 mScrollDisplacement.y = -SCROLL_SPEED;
3299 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3301 mScrollDisplacement.y = SCROLL_SPEED;
3307 if ( handleVisible && // Ensure the handle is visible.
3308 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3309 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3311 currentSelectionHandlePosition = newHandlePosition;
3313 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3314 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3318 if ( handleId == HandleOne )
3320 const TextStyle oldInputStyle( mInputStyle );
3322 // Set Active Style to that of first character in selection
3323 if( mSelectionHandleOnePosition < mStyledText.size() )
3325 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3328 if( oldInputStyle != mInputStyle )
3330 // Updates the line height accordingly with the input style.
3333 EmitStyleChangedSignal();
3339 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3342 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3345 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3346 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3348 if ( selectionHandleActor )
3350 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3351 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3352 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3354 if( IsScrollEnabled() )
3356 const Size cursorSize( CURSOR_THICKNESS,
3357 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3358 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3360 GetControlSize() ) );
3365 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3367 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3368 // For now the visual position of end of logical string will be end of visual string.
3369 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3371 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3374 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3376 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3377 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3378 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3379 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3381 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3383 // Deselect text prior to startSelectionIt
3384 for(;it!=startSelectionIt;++it)
3386 selectedVisualText[*it] = false;
3389 // Select text from startSelectionIt -> endSelectionIt
3390 for(;it!=endSelectionIt;++it)
3392 selectedVisualText[*it] = true;
3395 // Deselect text after endSelection
3398 selectedVisualText[*it] = false;
3401 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3404 // Calculate the dimensions of the quads they will make the highlight mesh
3405 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3407 // At the moment there is no public API to modify the block alignment option.
3408 const bool blockAlignEnabled = true;
3410 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3412 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3414 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3415 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3417 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3418 std::vector<bool> selectedVisualText;
3419 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3420 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3421 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3423 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3424 float rowLeft = 0.0f;
3425 float rowRight = 0.0f;
3426 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3427 float maxRowLeft = std::numeric_limits<float>::max();
3428 float maxRowRight = 0.0f;
3430 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3432 // Scan through entire text.
3435 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3437 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3438 bool charSelected( false );
3439 if( selectedIt != selectedEndIt )
3441 charSelected = *selectedIt++;
3444 if(selectionState == SelectionNone)
3448 selectionState = SelectionStarted;
3449 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3450 rowRight = rowLeft + charInfo.mSize.width;
3453 else if(selectionState == SelectionStarted)
3455 // break selection on:
3456 // 1. new line causing selection break. (\n or wordwrap)
3457 // 2. character not selected.
3458 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3461 // finished selection.
3462 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3463 // that it resides on. That way this enumeration is not necessary.
3465 if(lastIt->mIsNewParagraphChar)
3467 // 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.
3468 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3470 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3471 maxRowLeft = std::min(maxRowLeft, min.x);
3472 maxRowRight = std::max(maxRowRight, max.x);
3473 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3474 float rowTop = rowBottom - rowSize.height;
3476 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3477 if(charSelected && blockAlignEnabled)
3479 rowRight = std::numeric_limits<float>::max();
3481 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3483 selectionState = SelectionNone;
3485 // Still selected? start a new selection
3488 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3489 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3490 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3491 selectionState = SelectionStarted;
3496 // build up highlight(s) with this selection data.
3497 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3498 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3505 // If reached end, and still on selection, then close selection.
3508 if(selectionState == SelectionStarted)
3510 // finished selection.
3512 if(lastIt->mIsNewParagraphChar)
3514 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3516 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3517 maxRowLeft = std::min(maxRowLeft, min.x);
3518 maxRowRight = std::max(maxRowRight, max.x);
3519 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3520 float rowTop = rowBottom - rowSize.height;
3521 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3525 // Get the top left and bottom right corners.
3526 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3527 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3528 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3530 // Clamp quads so they appear to clip to borders of the whole text.
3531 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3533 // For block-align align Further Clamp quads to max left and right extents
3534 if(blockAlignEnabled)
3536 // BlockAlign: Will adjust highlight to block:
3538 // H[ello] (top row right = max of all rows right)
3539 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3540 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3541 // [text] (bottom row left = min of all rows left)
3542 // (common in SMS messaging selection)
3544 // As opposed to the default which is tight text highlighting.
3549 // (common in regular text editors/web browser selection)
3551 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3554 // Finally clamp quads again so they don't exceed the boundry of the control.
3555 const Vector3& controlSize = GetControlSize();
3556 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3559 return mNewHighlightInfo;
3562 void TextInput::UpdateHighlight()
3564 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3566 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3568 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3569 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3570 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3571 // [BOTTOM] [ MIDDLE ]
3574 // Each quad is created as 2 triangles.
3575 // Middle is just 1 quad regardless of its size.
3589 if ( mHighlightMeshActor )
3591 // vertex and triangle buffers should always be present if MeshActor is alive.
3592 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3593 MeshData::VertexContainer vertices;
3594 Dali::MeshData::FaceIndices faceIndices;
3596 if( !newHighlightInfo.mQuadList.empty() )
3598 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3599 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3601 // vertex position defaults to (0 0 0)
3602 MeshData::Vertex vertex;
3603 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3606 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3608 // Add each quad geometry (a sub-selection) to the mesh data.
3618 QuadCoordinates& quad = *iter;
3620 vertex.x = quad.min.x;
3621 vertex.y = quad.min.y;
3622 vertices.push_back( vertex );
3625 vertex.x = quad.max.x;
3626 vertex.y = quad.min.y;
3627 vertices.push_back( vertex );
3629 // bottom-left (v+2)
3630 vertex.x = quad.min.x;
3631 vertex.y = quad.max.y;
3632 vertices.push_back( vertex );
3634 // bottom-right (v+3)
3635 vertex.x = quad.max.x;
3636 vertex.y = quad.max.y;
3637 vertices.push_back( vertex );
3639 // triangle A (3, 1, 0)
3640 faceIndices.push_back( v + 3 );
3641 faceIndices.push_back( v + 1 );
3642 faceIndices.push_back( v );
3644 // triangle B (0, 2, 3)
3645 faceIndices.push_back( v );
3646 faceIndices.push_back( v + 2 );
3647 faceIndices.push_back( v + 3 );
3649 mMeshData.SetFaceIndices( faceIndices );
3652 BoneContainer bones(0); // passed empty as bones not required
3653 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3654 mHighlightMesh.UpdateMeshData(mMeshData);
3659 void TextInput::ClearPopup()
3661 mPopupPanel.Clear();
3664 void TextInput::AddPopupOptions()
3666 mPopupPanel.AddPopupOptions();
3669 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3671 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3673 Vector3 clampedPosition ( position );
3674 Vector3 tailOffsetPosition ( position );
3676 float xOffSet( 0.0f );
3678 Actor self = Self();
3679 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3681 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3682 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3684 // Clamp to left or right or of boundary
3685 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3687 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3689 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3691 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3694 clampedPosition.x = position.x + xOffSet;
3695 tailOffsetPosition.x = -xOffSet;
3697 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3698 bool flipTail( false );
3700 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3702 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3706 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3707 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3710 void TextInput::HidePopup(bool animate, bool signalFinished )
3712 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3714 mPopupPanel.Hide( animate );
3716 if( animate && signalFinished )
3718 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3723 void TextInput::ShowPopup( bool animate )
3726 Vector2 alternativePopupPosition;
3728 if(mHighlightMeshActor && mState == StateEdit)
3731 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3733 // When text is selected, show popup above top handle (and text), or below bottom handle.
3734 // topHandle: referring to the top most point of the handle or the top line of selection.
3735 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3737 topHandle = mSelectionHandleOneActualPosition;
3738 bottomHandle = mSelectionHandleTwoActualPosition;
3739 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3743 topHandle = mSelectionHandleTwoActualPosition;
3744 bottomHandle = mSelectionHandleOneActualPosition;
3745 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3747 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3748 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3750 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3752 position.x = xPosition;
3754 // Alternative position if no upper space
3755 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3756 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3760 // When no text is selected, show popup at world position of grab handle or cursor
3761 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3762 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3763 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3764 // if can't be positioned above, then position below row.
3765 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3768 // If grab handle enabled then position pop-up below the grab handle.
3769 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3773 SetPopupPosition( position, alternativePopupPosition );
3776 mPopupPanel.Show( Self(), animate );
3777 StartMonitoringStageForTouch();
3779 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3782 void TextInput::ShowPopupCutCopyPaste()
3786 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3787 // Check the selected text is whole text or not.
3788 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3790 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3793 if ( !mStyledText.empty() )
3795 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3796 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3799 if( mClipboard && mClipboard.NumberOfItems() )
3801 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3802 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3807 mPopupPanel.Hide(false);
3811 void TextInput::SetUpPopupSelection( bool showCutButton )
3814 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3815 // If no text exists then don't offer to select
3816 if ( !mStyledText.empty() )
3818 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3819 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3820 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, showCutButton );
3822 // if clipboard has valid contents then offer paste option
3823 if( mClipboard && mClipboard.NumberOfItems() )
3825 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3826 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3831 mPopupPanel.Hide(false);
3834 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3839 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3840 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3841 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3842 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3844 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3846 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3848 float closestYdifference = std::numeric_limits<float>::max();
3849 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3850 std::size_t numberOfMatchedCharacters = 0;
3852 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3853 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3855 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3857 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3858 float baselinePosition = info.mPosition.y - info.mDescender;
3860 if( info.mIsVisible )
3862 // store difference between source y point and the y position of the current character
3863 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3865 if( currentYdifference < closestYdifference )
3867 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3868 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3869 closestYdifference = currentYdifference;
3870 matchedCharacters.clear();
3871 numberOfMatchedCharacters = 0; // reset count
3874 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3875 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3877 // ignore new line character.
3878 if( !info.mIsNewParagraphChar )
3880 matchedCharacters.push_back( info );
3881 numberOfMatchedCharacters++;
3885 } // End of loop checking each character's y position in the character layout table
3887 // Check if last character is a newline, if it is
3888 // then need pretend there is an imaginary line afterwards,
3889 // and check if user is touching below previous line.
3890 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3892 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3894 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3898 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3899 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3901 bool matched( false );
3903 // 2 Iterate through matching list of y positions and find closest matching X position.
3904 for( ; it != endIt; ++it )
3906 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3908 if( info.mIsVisible )
3910 // stop when on left side of character's center.
3911 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3912 if( sourceScrollOffset.x < characterMidPointPosition )
3914 if(info.mIsRightToLeftCharacter)
3916 rightToLeftChar = true;
3918 glyphIntersection = info.mPosition.x;
3923 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3929 rightToLeftChar = lastRightToLeftChar;
3932 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3933 closestIndex = lineOffset + matchCharacterIndex;
3935 mClosestCursorPositionEOL = false; // reset
3936 if ( it == endIt && !matched )
3938 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3941 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3942 if( rightToLeftChar && lastRightToLeftChar )
3944 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3949 // closestIndex is the visual index, need to convert it to the logical index
3950 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3952 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3954 // Checks for situations where user is touching between LTR and RTL
3955 // characters. To identify if the user means the end of a LTR string
3956 // or the beginning of an RTL string, and vice versa.
3957 if( closestIndex > 0 )
3959 if( rightToLeftChar && !lastRightToLeftChar )
3964 // A: In this touch range, the user is indicating that they wish to place
3965 // the cursor at the end of the LTR text.
3966 // B: In this touch range, the user is indicating that they wish to place
3967 // the cursor at the end of the RTL text.
3969 // Result of touching A area:
3970 // [.....LTR]|[RTL......]+
3972 // |: primary cursor (for typing LTR chars)
3973 // +: secondary cursor (for typing RTL chars)
3975 // Result of touching B area:
3976 // [.....LTR]+[RTL......]|
3978 // |: primary cursor (for typing RTL chars)
3979 // +: secondary cursor (for typing LTR chars)
3981 if( sourceScrollOffset.x < glyphIntersection )
3986 else if( !rightToLeftChar && lastRightToLeftChar )
3988 if( sourceScrollOffset.x < glyphIntersection )
3995 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
3996 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
3997 // one further ahead
3998 if( rightToLeftChar && !lastRightToLeftChar )
4003 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4005 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4007 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4016 float TextInput::GetLineJustificationPosition() const
4018 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4019 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4020 float alignmentOffset = 0.f;
4022 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4023 if( alignment & Toolkit::Alignment::HorizontalLeft )
4025 alignmentOffset = 0.f;
4027 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4029 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4031 else if( alignment & Toolkit::Alignment::HorizontalRight )
4033 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4036 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4037 float justificationOffset = 0.f;
4039 switch( justification )
4041 case Toolkit::TextView::Left:
4043 justificationOffset = 0.f;
4046 case Toolkit::TextView::Center:
4048 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4051 case Toolkit::TextView::Right:
4053 justificationOffset = mTextLayoutInfo.mTextSize.width;
4056 case Toolkit::TextView::Justified:
4058 justificationOffset = 0.f;
4063 DALI_ASSERT_ALWAYS( false );
4067 return alignmentOffset + justificationOffset;
4070 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4072 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4073 A newline character is not inserted in this case */
4075 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4077 Vector3 cursorPosition;
4079 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4081 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4083 // end character so use
4084 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4085 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4089 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4092 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4094 // If previous character on a different line then use current characters position
4095 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4097 if ( mClosestCursorPositionEOL )
4099 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4103 cursorPosition = Vector3(currentCharInfo.mPosition);
4108 // Previous character is on same line so use position of previous character plus it's width.
4109 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4112 return cursorPosition;
4115 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4117 bool direction(false);
4118 Vector3 alternatePosition;
4119 bool alternatePositionValid(false);
4121 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4124 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4126 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4128 alternatePositionValid = false;
4129 directionRTL = false;
4131 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4133 std::size_t visualCharacterPosition;
4135 // When cursor is not at beginning, consider possibility of
4136 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4137 if(characterPosition > 0)
4139 // Cursor position should be the end of the last character.
4140 // If the last character is LTR, then the end is on the right side of the glyph.
4141 // If the last character is RTL, then the end is on the left side of the glyph.
4142 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4144 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4146 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4149 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4150 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4152 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4153 const Vector3& size = GetControlSize();
4155 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4157 --visualCharacterPosition;
4159 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4162 if(!info.mIsNewParagraphChar)
4164 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4168 // When cursor points to first character on new line, position cursor at the start of this glyph.
4169 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4171 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4172 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4173 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4175 cursorPosition.x = infoNext.mPosition.x + start;
4176 cursorPosition.y = infoNext.mPosition.y;
4180 // If cursor points to the end of text, then can only position
4181 // cursor where the new line starts based on the line-justification position.
4182 cursorPosition.x = GetLineJustificationPosition();
4184 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4186 // If this is after the last character, then we can assume that the new cursor
4187 // should be exactly one row below the current row.
4189 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4190 cursorPosition.y = info.mPosition.y + rowRect.height;
4194 // If this is not after last character, then we can use this row's height.
4195 // should be exactly one row below the current row.
4197 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4198 cursorPosition.y = info.mPosition.y + rowRect.height;
4203 directionRTL = info.mIsRightToLeftCharacter;
4205 // 1. When the cursor is neither at the beginning or the end,
4206 // we can show multiple cursors under situations when the cursor is
4207 // between RTL and LTR text...
4208 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4210 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4212 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4213 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4215 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4217 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4218 // Text: [...LTR...]|[...RTL...]
4220 // Alternate cursor pos: ^
4221 // In which case we need to display an alternate cursor for the RTL text.
4223 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4224 alternatePosition.y = infoAlt.mPosition.y;
4225 alternatePositionValid = true;
4227 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4229 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4230 // Text: |[...RTL...] [...LTR....]
4232 // Alternate cursor pos: ^
4233 // In which case we need to display an alternate cursor for the RTL text.
4235 alternatePosition.x = infoAlt.mPosition.x;
4236 alternatePosition.y = infoAlt.mPosition.y;
4237 alternatePositionValid = true;
4242 // 2. When the cursor is at the end of the text,
4243 // and we have multi-directional text,
4244 // we can also consider showing mulitple cursors.
4245 // The rule here is:
4246 // If first and last characters on row are different
4247 // Directions, then two cursors need to be displayed.
4249 // Get first logical glyph on row
4250 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4252 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4253 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4255 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4257 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4258 // Text: [...LTR...]|[...RTL...]
4260 // Alternate cursor pos: ^
4261 // In which case we need to display an alternate cursor for the RTL text, this cursor
4262 // should be at the end of the given line.
4264 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4265 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4266 alternatePosition.y = infoAlt.mPosition.y;
4267 alternatePositionValid = true;
4269 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4271 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4272 // Text: |[...RTL...] [...LTR....]
4274 // Alternate cursor pos: ^
4275 // In which case we need to display an alternate cursor for the RTL text.
4277 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4278 alternatePosition.x = infoAlt.mPosition.x;
4279 alternatePosition.y = infoAlt.mPosition.y;
4280 alternatePositionValid = true;
4283 } // characterPosition > 0
4284 else if(characterPosition == 0)
4286 // When the cursor position is at the beginning, it should be at the start of the current character.
4287 // If the current character is LTR, then the start is on the right side of the glyph.
4288 // If the current character is RTL, then the start is on the left side of the glyph.
4289 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4291 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4293 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4296 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4297 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4299 cursorPosition.x = info.mPosition.x + start;
4300 cursorPosition.y = info.mPosition.y;
4301 directionRTL = info.mIsRightToLeftCharacter;
4306 // If the character table is void, place the cursor accordingly the text alignment.
4307 const Vector3& size = GetControlSize();
4309 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4310 float alignmentOffset = 0.f;
4312 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4313 if( alignment & Toolkit::Alignment::HorizontalLeft )
4315 alignmentOffset = 0.f;
4317 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4319 alignmentOffset = 0.5f * ( size.width );
4321 else if( alignment & Toolkit::Alignment::HorizontalRight )
4323 alignmentOffset = size.width;
4326 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4327 cursorPosition.x = alignmentOffset;
4329 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4330 if( alignment & Toolkit::Alignment::VerticalTop )
4332 cursorPosition.y = mLineHeight;
4334 else if( alignment & Toolkit::Alignment::VerticalCenter )
4336 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4338 else if( alignment & Toolkit::Alignment::VerticalBottom )
4340 cursorPosition.y = size.height;
4344 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4345 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4346 if( alternatePositionValid )
4348 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4349 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4352 return cursorPosition;
4355 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4357 // scan string from current position to beginning of current line to note direction of line
4358 while(logicalPosition)
4361 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4362 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4369 return logicalPosition;
4372 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4376 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4379 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4381 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4382 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4384 min = Vector2::ZERO;
4385 max = Vector2(0.0f, mLineHeight);
4389 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4390 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4391 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4393 // If cursor is pointing to end of line, then start from last character.
4394 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4396 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4398 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4399 if( !it->mIsVisible )
4401 characterPosition = FindVisibleCharacter( Left, characterPosition );
4402 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4405 // Scan characters left and right of cursor, stopping when end of line/string reached or
4406 // y position greater than threshold of reference line.
4408 // 1. scan left until we reach the beginning or a different line.
4409 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4410 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4411 // min-x position is the left-most char's left (x)
4412 // max-x position is the right-most char's right (x)
4413 // min-y position is the minimum of all character's top (y)
4414 // max-y position is the maximum of all character's bottom (y+height)
4415 min.y = validCharIt->mPosition.y;
4416 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4421 min.y = std::min(min.y, validCharIt->mPosition.y);
4422 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4431 if( (it->mPosition.y < referenceLine) ||
4432 (it->mIsNewParagraphChar) ||
4439 // info refers to the first character on this line.
4440 min.x = validCharIt->mPosition.x;
4442 // 2. scan right until we reach end or a different line
4443 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4444 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4448 if( (it->mPosition.y > referenceLine) ||
4449 (it->mIsNewParagraphChar) ||
4456 min.y = std::min(min.y, validCharIt->mPosition.y);
4457 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4462 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4464 if ( validCharIt != end )
4466 // info refers to the last character on this line.
4467 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4470 return Size( max.x - min.x, max.y - min.y );
4473 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4475 Actor popUpPanel = mPopupPanel.GetRootActor();
4477 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4483 Dali::Actor parent( touchedActor.GetParent() );
4487 return WasTouchedCheck( parent );
4494 void TextInput::StartMonitoringStageForTouch()
4496 Stage stage = Stage::GetCurrent();
4497 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4500 void TextInput::EndMonitoringStageForTouch()
4502 Stage stage = Stage::GetCurrent();
4503 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4506 void TextInput::OnStageTouched(const TouchEvent& event)
4508 if( event.GetPointCount() > 0 )
4510 if ( TouchPoint::Down == event.GetPoint(0).state )
4512 const Actor touchedActor(event.GetPoint(0).hitActor);
4514 bool popUpShown( false );
4516 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4521 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4523 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4525 EndMonitoringStageForTouch();
4526 HidePopup( true, false );
4529 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4531 EndMonitoringStageForTouch();
4532 ShowGrabHandleAndSetVisibility( false );
4538 void TextInput::SelectText(std::size_t start, std::size_t end)
4540 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4541 IsGrabHandleEnabled()?"true":"false",
4542 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4543 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4544 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4546 StartMonitoringStageForTouch();
4548 if ( mEditModeActive ) // Only allow text selection when in edit mode
4550 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4551 mSelectingText = true;
4553 std::size_t selectionStartPosition = std::min( start, end );
4555 // Hide grab handle when selecting.
4556 ShowGrabHandleAndSetVisibility( false );
4558 if( start != end ) // something to select
4560 SetCursorVisibility( false );
4561 StopCursorBlinkTimer();
4563 CreateSelectionHandles(start, end);
4566 const TextStyle oldInputStyle( mInputStyle );
4567 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4569 if( oldInputStyle != mInputStyle )
4571 // Updates the line height accordingly with the input style.
4574 EmitStyleChangedSignal();
4580 mSelectingText = false;
4584 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4586 MarkupProcessor::StyledTextArray currentSelectedText;
4588 if ( IsTextSelected() )
4590 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4591 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4593 for(; it != end; ++it)
4595 MarkupProcessor::StyledText& styledText( *it );
4596 currentSelectedText.push_back( styledText );
4599 return currentSelectedText;
4602 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4604 const std::size_t beginIndex = std::min( begin, end );
4605 const std::size_t endIndex = std::max( begin, end );
4608 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4610 // Create a styled text array used to replace the text into the text-view.
4611 MarkupProcessor::StyledTextArray text;
4612 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4614 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4615 GetTextLayoutInfo();
4617 if( IsScrollEnabled() )
4619 // Need to set the scroll position as the text's size may have changed.
4620 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4623 ShowGrabHandleAndSetVisibility( false );
4629 // Set Handle positioning as the new style may have repositioned the characters.
4630 SetSelectionHandlePosition(HandleOne);
4631 SetSelectionHandlePosition(HandleTwo);
4634 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4636 // Just hide the grab handle when keyboard is hidden.
4637 if (!keyboardShown )
4639 ShowGrabHandleAndSetVisibility( false );
4641 // If the keyboard is not now being shown, then hide the popup panel
4642 mPopupPanel.Hide( true );
4646 // Removes highlight and resumes edit mode state
4647 void TextInput::RemoveHighlight( bool hidePopup )
4649 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4651 if ( mHighlightMeshActor )
4653 if ( mSelectionHandleOne )
4655 mActiveLayer.Remove( mSelectionHandleOne );
4656 mSelectionHandleOne.Reset();
4657 mSelectionHandleOneOffset.x = 0.0f;
4659 if ( mSelectionHandleTwo )
4661 mActiveLayer.Remove( mSelectionHandleTwo );
4662 mSelectionHandleTwo.Reset();
4663 mSelectionHandleTwoOffset.x = 0.0f;
4666 mNewHighlightInfo.mQuadList.clear();
4668 Self().Remove( mHighlightMeshActor );
4670 SetCursorVisibility( true );
4671 StartCursorBlinkTimer();
4673 mHighlightMeshActor.Reset();
4674 // NOTE: We cannot dereference mHighlightMesh, due
4675 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4683 mSelectionHandleOnePosition = 0;
4684 mSelectionHandleTwoPosition = 0;
4687 void TextInput::CreateHighlight()
4689 if ( !mHighlightMeshActor )
4691 mMeshData = MeshData( );
4692 mMeshData.SetHasNormals( true );
4694 mCustomMaterial = Material::New("CustomMaterial");
4695 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4697 mMeshData.SetMaterial( mCustomMaterial );
4699 mHighlightMesh = Mesh::New( mMeshData );
4701 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4702 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4703 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4704 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4705 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4706 mHighlightMeshActor.SetAffectedByLighting(false);
4708 Self().Add(mHighlightMeshActor);
4713 bool TextInput::CopySelectedTextToClipboard()
4715 mCurrentCopySelecton.clear();
4717 mCurrentCopySelecton = GetSelectedText();
4719 std::string stringToStore;
4721 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4722 * a marked up string.
4724 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4725 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4726 bool success = mClipboard.SetItem( stringToStore );
4730 void TextInput::PasteText( const Text& text )
4732 // Update Flag, indicates whether to update the text-input contents or not.
4733 // Any key stroke that results in a visual change of the text-input should
4734 // set this flag to true.
4735 bool update = false;
4736 if( mHighlightMeshActor )
4738 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4739 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4741 ImfManager imfManager = ImfManager::Get();
4744 imfManager.SetCursorPosition( mCursorPosition );
4745 imfManager.NotifyCursorPosition();
4747 DeleteHighlightedText( true );
4751 bool textExceedsMaximunNumberOfCharacters = false;
4752 bool textExceedsBoundary = false;
4754 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4756 mCursorPosition += insertedStringLength;
4757 ImfManager imfManager = ImfManager::Get();
4760 imfManager.SetCursorPosition ( mCursorPosition );
4761 imfManager.NotifyCursorPosition();
4764 update = update || ( insertedStringLength > 0 );
4771 if( insertedStringLength < text.GetLength() )
4773 EmitMaxInputCharactersReachedSignal();
4776 if( textExceedsBoundary )
4778 EmitInputTextExceedsBoundariesSignal();
4782 void TextInput::SetTextDirection()
4784 // Put the cursor to the right if we are empty and an RTL language is being used.
4785 if ( mStyledText.empty() )
4787 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4789 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4790 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4792 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4793 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4795 int alignment( mDisplayedTextView.GetTextAlignment() &
4796 ( Toolkit::Alignment::VerticalTop |
4797 Toolkit::Alignment::VerticalCenter |
4798 Toolkit::Alignment::VerticalBottom |
4799 Toolkit::Alignment::HorizontalCenter ) );
4800 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4802 // If our alignment is in the center, then do not change.
4803 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4805 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4808 // If our justification is in the center, then do not change.
4809 if ( justification != Toolkit::TextView::Center )
4811 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4814 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4815 mDisplayedTextView.SetLineJustification( justification );
4819 void TextInput::UpdateLineHeight()
4821 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4822 mLineHeight = font.GetLineHeight();
4824 // 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.
4826 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4828 if( !mExceedEnabled || shrink )
4830 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4834 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4836 std::size_t position = 0;
4838 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4844 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4846 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4848 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4854 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4855 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4857 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4863 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4868 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4875 void TextInput::SetSortModifier( float depthOffset )
4877 if(mDisplayedTextView)
4879 mDisplayedTextView.SetSortModifier(depthOffset);
4883 void TextInput::SetSnapshotModeEnabled( bool enable )
4885 if(mDisplayedTextView)
4887 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4891 bool TextInput::IsSnapshotModeEnabled() const
4893 bool snapshotEnabled = false;
4895 if(mDisplayedTextView)
4897 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4900 return snapshotEnabled;
4903 void TextInput::SetMarkupProcessingEnabled( bool enable )
4905 mMarkUpEnabled = enable;
4908 bool TextInput::IsMarkupProcessingEnabled() const
4910 return mMarkUpEnabled;
4913 void TextInput::SetScrollEnabled( bool enable )
4915 if( mDisplayedTextView )
4917 mDisplayedTextView.SetScrollEnabled( enable );
4922 // Don't set cursor's and handle's visibility to false if they are outside the
4923 // boundaries of the text-input.
4924 mIsCursorInScrollArea = true;
4925 mIsGrabHandleInScrollArea = true;
4926 if( mSelectionHandleOne && mSelectionHandleTwo )
4928 mSelectionHandleOne.SetVisible( true );
4929 mSelectionHandleTwo.SetVisible( true );
4931 if( mHighlightMeshActor )
4933 mHighlightMeshActor.SetVisible( true );
4939 bool TextInput::IsScrollEnabled() const
4941 bool scrollEnabled = false;
4943 if( mDisplayedTextView )
4945 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4948 return scrollEnabled;
4951 void TextInput::SetScrollPosition( const Vector2& position )
4953 if( mDisplayedTextView )
4955 mDisplayedTextView.SetScrollPosition( position );
4959 Vector2 TextInput::GetScrollPosition() const
4961 Vector2 scrollPosition;
4963 if( mDisplayedTextView )
4965 scrollPosition = mDisplayedTextView.GetScrollPosition();
4968 return scrollPosition;
4971 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4973 // determine number of characters that we can write to style text buffer, this is the insertStringLength
4974 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4975 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4977 // Add style to the new input text.
4978 MarkupProcessor::StyledTextArray textToInsert;
4979 for( std::size_t i = 0; i < insertedStringLength; ++i )
4981 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
4982 textToInsert.push_back( newStyledCharacter );
4985 //Insert text to the TextView.
4986 const bool emptyTextView = mStyledText.empty();
4987 if( emptyTextView && mPlaceHolderSet )
4989 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
4990 mDisplayedTextView.SetText( textToInsert );
4994 if( 0 == numberOfCharactersToReplace )
4996 mDisplayedTextView.InsertTextAt( position, textToInsert );
5000 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5003 mPlaceHolderSet = false;
5005 if( textToInsert.empty() )
5007 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5008 GetTextLayoutInfo();
5012 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5013 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5016 textExceedsBoundary = false;
5018 if( !mExceedEnabled )
5020 const Vector3& size = GetControlSize();
5022 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5024 // If new text does not fit within TextView
5025 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5026 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5027 GetTextLayoutInfo();
5028 textExceedsBoundary = true;
5029 insertedStringLength = 0;
5032 if( textExceedsBoundary )
5034 // Add the part of the text which fits on the text-input.
5036 // Split the text which doesn't fit in two halves.
5037 MarkupProcessor::StyledTextArray firstHalf;
5038 MarkupProcessor::StyledTextArray secondHalf;
5039 SplitText( textToInsert, firstHalf, secondHalf );
5041 // Clear text. This text will be filled with the text inserted.
5042 textToInsert.clear();
5044 // Where to insert the text.
5045 std::size_t positionToInsert = position;
5047 bool end = text.GetLength() <= 1;
5050 // Insert text and check ...
5051 const std::size_t textLength = firstHalf.size();
5052 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5053 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5055 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5057 // Inserted text doesn't fit.
5059 // Remove inserted text
5060 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5061 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5063 // The iteration finishes when only one character doesn't fit.
5064 end = textLength <= 1;
5068 // Prepare next two halves for next iteration.
5069 MarkupProcessor::StyledTextArray copyText = firstHalf;
5070 SplitText( copyText, firstHalf, secondHalf );
5077 // store text to be inserted in mStyledText.
5078 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5080 // Increase the inserted characters counter.
5081 insertedStringLength += textLength;
5083 // Prepare next two halves for next iteration.
5084 MarkupProcessor::StyledTextArray copyText = secondHalf;
5085 SplitText( copyText, firstHalf, secondHalf );
5087 // Update where next text has to be inserted
5088 positionToInsert += textLength;
5094 if( textToInsert.empty() && emptyTextView )
5096 // No character has been added and the text-view was empty.
5097 // Set the placeholder text.
5098 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5099 mPlaceHolderSet = true;
5103 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5104 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5105 mPlaceHolderSet = false;
5108 return insertedStringLength;
5111 void TextInput::GetTextLayoutInfo()
5113 if( mStyledText.empty() )
5115 // The text-input has no text, clear the text-view's layout info.
5116 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5120 if( mDisplayedTextView )
5122 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5126 // There is no text-view.
5127 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5132 void TextInput::SetOffsetFromText( const Vector4& offset )
5134 mPopupOffsetFromText = offset;
5137 const Vector4& TextInput::GetOffsetFromText() const
5139 return mPopupOffsetFromText;
5142 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5144 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5148 TextInput& textInputImpl( GetImpl( textInput ) );
5150 switch ( propertyIndex )
5152 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5154 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5157 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5159 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5162 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5164 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5167 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5169 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5172 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5174 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5177 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5179 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5182 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5184 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5187 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5189 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5192 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5194 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5197 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5199 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5202 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5204 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5207 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5209 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5212 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5214 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5217 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5219 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5222 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5224 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5227 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5229 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5235 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5237 Property::Value value;
5239 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5243 TextInput& textInputImpl( GetImpl( textInput ) );
5245 switch ( propertyIndex )
5247 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5249 value = textInputImpl.GetMaterialDiffuseColor();
5252 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5254 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5257 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5259 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5262 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5264 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5267 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5269 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5272 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5274 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5277 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5279 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5282 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5284 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5287 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5289 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5292 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5294 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5297 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5299 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5302 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5304 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5307 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5309 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5312 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5314 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5317 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5319 value = textInputImpl.GetOffsetFromText();
5322 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5324 value = textInputImpl.mCursor.GetCurrentColor();
5331 void TextInput::EmitStyleChangedSignal()
5333 // emit signal if input style changes.
5334 Toolkit::TextInput handle( GetOwner() );
5335 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5338 void TextInput::EmitTextModified()
5340 // emit signal when text changes.
5341 Toolkit::TextInput handle( GetOwner() );
5342 mTextModifiedSignal.Emit( handle );
5346 void TextInput::EmitMaxInputCharactersReachedSignal()
5348 // emit signal if max characters is reached during text input.
5349 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5351 Toolkit::TextInput handle( GetOwner() );
5352 mMaxInputCharactersReachedSignalV2.Emit( handle );
5355 void TextInput::EmitInputTextExceedsBoundariesSignal()
5357 // Emit a signal when the input text exceeds the boundaries of the text input.
5359 Toolkit::TextInput handle( GetOwner() );
5360 mInputTextExceedBoundariesSignalV2.Emit( handle );
5363 } // namespace Internal
5365 } // namespace Toolkit