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>
25 #include <dali/integration-api/debug.h>
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
58 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
72 const std::string NEWLINE( "\n" );
74 const TextStyle DEFAULT_TEXT_STYLE;
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
81 * Selection state enumeration (FSM)
85 SelectionNone, ///< Currently not encountered selected section.
86 SelectionStarted, ///< Encountered selected section
87 SelectionFinished ///< Finished selected section
91 * Whether the given style is the default style or not.
92 * @param[in] style The given style.
93 * @return \e true if the given style is the default. Otherwise it returns \e false.
95 bool IsDefaultStyle( const TextStyle& style )
97 return DEFAULT_TEXT_STYLE == style;
101 * Whether the given styled text is using the default style or not.
102 * @param[in] textArray The given text.
103 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
107 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
109 const TextStyle& style( (*it).mStyle );
111 if( !IsDefaultStyle( style ) )
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
122 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
126 if( ( *it ).mIsVisible )
128 return --cursorPosition;
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
139 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
141 if( ( *it ).mIsVisible )
143 return cursorPosition;
149 return cursorPosition;
153 * Whether the given position plus the cursor size offset is inside the given boundary.
155 * @param[in] position The given position.
156 * @param[in] cursorSize The cursor size.
157 * @param[in] controlSize The given boundary.
159 * @return whether the given position is inside the given boundary.
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
163 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
170 * Splits a text in two halves.
172 * If the text's number of characters is odd, firstHalf has one more character.
174 * @param[in] text The text to be split.
175 * @param[out] firstHalf The first half of the text.
176 * @param[out] secondHalf The second half of the text.
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
185 const std::size_t textLength = text.size();
186 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
188 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
192 } // end of namespace
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
205 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
207 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
208 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
209 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
210 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
211 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
212 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
213 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
215 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
255 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
257 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
259 QuadCoordinates quad(x1, y1, x2, y2);
260 mQuadList.push_back( quad );
263 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
265 for(std::size_t i = 0;i < mQuadList.size(); i++)
267 QuadCoordinates& quad = mQuadList[i];
269 quad.min.Clamp(min, max);
270 quad.max.Clamp(min, max);
274 // [TextInput] ////////////////////////////////////////////////////////////////
276 Dali::Toolkit::TextInput TextInput::New()
278 // Create the implementation
279 TextInputPtr textInput(new TextInput());
280 // Pass ownership to CustomActor via derived handle
281 Dali::Toolkit::TextInput handle(*textInput);
283 textInput->Initialize();
288 TextInput::TextInput()
289 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
294 mDisplayedTextView(),
295 mStyledPlaceHolderText(),
296 mMaxStringLength( DEFAULT_MAX_SIZE ),
297 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
298 mCursorPosition( 0 ),
299 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
300 mIsSelectionHandleOneFlipped( false ),
301 mIsSelectionHandleTwoFlipped( false ),
302 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
303 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
304 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
305 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
306 mSelectionHandleOnePosition( 0 ),
307 mSelectionHandleTwoPosition( 0 ),
309 mPreEditStartPosition( 0 ),
310 mPreEditLength ( 0 ),
311 mNumberOfSurroundingCharactersDeleted( 0 ),
312 mTouchStartTime( 0 ),
314 mCurrentCopySelecton(),
317 mScrollDisplacement(),
318 mCurrentHandlePosition(),
319 mCurrentSelectionId(),
320 mCurrentSelectionHandlePosition(),
321 mRequestedSelection( 0, 0 ),
322 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
323 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mMaterialColor( LIGHTBLUE ),
326 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
327 mOverrideAutomaticAlignment( false ),
328 mCursorRTLEnabled( false ),
329 mClosestCursorPositionEOL ( false ),
330 mCursorBlinkStatus( true ),
331 mCursorVisibility( false ),
332 mGrabHandleVisibility( false ),
333 mIsCursorInScrollArea( true ),
334 mIsGrabHandleInScrollArea( true ),
335 mEditModeActive( false ),
336 mEditOnTouch( true ),
337 mTextSelection( true ),
338 mExceedEnabled( true ),
339 mGrabHandleEnabled( true ),
340 mIsSelectionHandleFlipEnabled( true ),
341 mPreEditFlag( false ),
342 mIgnoreCommitFlag( false ),
343 mIgnoreFirstCommitFlag( false ),
344 mSelectingText( false ),
345 mPreserveCursorPosition( false ),
346 mSelectTextOnCommit( false ),
347 mUnderlinedPriorToPreEdit ( false ),
348 mCommitByKeyInput( false ),
349 mPlaceHolderSet( false ),
350 mMarkUpEnabled( false )
352 // Updates the line height accordingly with the input style.
356 TextInput::~TextInput()
358 StopCursorBlinkTimer();
363 std::string TextInput::GetText() const
367 // Return text-view's text only if the text-input's text is not empty
368 // in order to not to return the placeholder text.
369 if( !mStyledText.empty() )
371 text = mDisplayedTextView.GetText();
377 std::string TextInput::GetMarkupText() const
379 std::string markupString;
380 MarkupProcessor::GetMarkupString( mStyledText, markupString );
385 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
387 // Get the placeholder styled text array from the markup string.
388 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
390 if( mStyledText.empty() )
392 // Set the placeholder text only if the styled text is empty.
393 mDisplayedTextView.SetText( mStyledPlaceHolderText );
394 mPlaceHolderSet = true;
398 std::string TextInput::GetPlaceholderText()
400 // Traverses the styled placeholder array getting only the text.
401 // Note that for some languages a 'character' could be represented by more than one 'char'
403 std::string placeholderText;
404 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
406 placeholderText.append( (*it).mText.GetText() );
409 return placeholderText ;
412 void TextInput::SetInitialText(const std::string& initialText)
414 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
416 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
418 mPreEditFlag = false;
419 mIgnoreCommitFlag = true;
422 SetText( initialText );
423 PreEditReset( false ); // Reset keyboard as text changed
426 void TextInput::SetText(const std::string& initialText)
428 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
430 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
432 if( mStyledText.empty() )
434 // If the initial text is empty, set the placeholder text.
435 mDisplayedTextView.SetText( mStyledPlaceHolderText );
436 mPlaceHolderSet = true;
440 mDisplayedTextView.SetText( mStyledText );
441 mPlaceHolderSet = false;
446 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
448 ImfManager imfManager = ImfManager::Get();
451 imfManager.SetCursorPosition( mCursorPosition );
452 imfManager.SetSurroundingText( initialText );
453 imfManager.NotifyCursorPosition();
456 if( IsScrollEnabled() )
458 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
461 ShowGrabHandleAndSetVisibility( false );
470 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
472 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
474 mDisplayedTextView.SetText( styleText );
475 mPlaceHolderSet = false;
477 // If text alignment hasn't been manually set by application developer, then we
478 // automatically determine the alignment based on the content of the text i.e. what
479 // language the text begins with.
480 // TODO: This should determine different alignments for each line (broken by '\n') of text.
481 if(!mOverrideAutomaticAlignment)
483 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
484 bool leftToRight(true);
486 if( !styleText.empty() )
488 bool breakOut(false);
490 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
492 const Text& text = textIter->mText;
494 for( std::size_t i = 0; i < text.GetLength(); ++i )
496 Character character( text[i] );
497 if( character.GetCharacterDirection() != Character::Neutral )
499 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
507 // Based on this direction, either left or right align text if not manually set by application developer.
508 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
509 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
510 Toolkit::Alignment::VerticalTop ) );
511 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
517 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
519 mMaxStringLength = maxChars;
522 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
524 DALI_ASSERT_DEBUG( maxLines > 0 )
528 mNumberOflinesLimit = maxLines;
532 std::size_t TextInput::GetNumberOfLinesLimit() const
534 return mNumberOflinesLimit;
537 std::size_t TextInput::GetNumberOfCharacters() const
539 return mStyledText.size();
543 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
545 mMaterialColor = color;
546 if ( mCustomMaterial )
548 mCustomMaterial.SetDiffuseColor( mMaterialColor );
549 mMeshData.SetMaterial( mCustomMaterial );
553 const Vector4& TextInput::GetMaterialDiffuseColor() const
555 return mMaterialColor;
560 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
562 return mInputStartedSignalV2;
565 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
567 return mInputFinishedSignalV2;
570 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
572 return mCutAndPasteToolBarDisplayedV2;
575 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
577 return mStyleChangedSignalV2;
580 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
582 return mTextModifiedSignal;
585 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
587 return mMaxInputCharactersReachedSignalV2;
590 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
592 return mInputTextExceedBoundariesSignalV2;
595 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
597 Dali::BaseHandle handle( object );
599 bool connected( true );
600 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
602 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
604 textInput.InputStartedSignal().Connect( tracker, functor );
606 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
608 textInput.InputFinishedSignal().Connect( tracker, functor );
610 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
612 textInput.StyleChangedSignal().Connect( tracker, functor );
614 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
616 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
618 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
620 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
624 // signalName does not match any signal
631 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
635 // update line height before calculate the actual position.
640 if( setCursorOnTouchPoint )
642 // Sets the cursor position for the given touch point.
643 ReturnClosestIndex( touchPoint, mCursorPosition );
645 // Creates the grab handle.
646 if( IsGrabHandleEnabled() )
648 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
652 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
653 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
654 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
655 ShowGrabHandleAndSetVisibility( true );
657 // Scrolls the text-view if needed.
658 if( IsScrollEnabled() )
660 ScrollTextViewToMakeCursorVisible( cursorPosition );
666 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
678 bool TextInput::IsEditable() const
680 return mEditModeActive;
683 void TextInput::SetEditOnTouch( bool editOnTouch )
685 mEditOnTouch = editOnTouch;
688 bool TextInput::IsEditOnTouch() const
693 void TextInput::SetTextSelectable( bool textSelectable )
695 mTextSelection = textSelectable;
698 bool TextInput::IsTextSelectable() const
700 return mTextSelection;
703 bool TextInput::IsTextSelected() const
705 return mHighlightMeshActor;
708 void TextInput::DeSelectText()
715 void TextInput::SetGrabHandleImage(Dali::Image image )
719 CreateGrabHandle(image);
723 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
725 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
729 mCursor.SetImage( image );
730 mCursor.SetNinePatchBorder( border );
734 Vector3 TextInput::GetSelectionHandleSize()
736 return DEFAULT_SELECTION_HANDLE_SIZE;
739 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
741 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
745 mCursorRTL.SetImage( image);
746 mCursorRTL.SetNinePatchBorder( border );
750 void TextInput::EnableGrabHandle(bool toggle)
752 // enables grab handle with will in turn de-activate magnifier
753 mGrabHandleEnabled = toggle;
756 bool TextInput::IsGrabHandleEnabled()
758 // if false then magnifier will be shown instead.
759 return mGrabHandleEnabled;
762 void TextInput::EnableSelectionHandleFlip( bool toggle )
764 // Deprecated function. To be removed.
765 mIsSelectionHandleFlipEnabled = toggle;
768 bool TextInput::IsSelectionHandleFlipEnabled()
770 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
774 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
776 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
777 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
778 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
780 mSelectionHandleFlipMargin = margin;
783 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
785 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
786 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
788 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
789 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
791 const Vector4 boundary( originX,
793 originX + boundingRectangle.width,
794 originY + boundingRectangle.height );
796 mBoundingRectangleWorldCoordinates = boundary;
798 // Set Boundary for Popup so it keeps the Pop-up within the area also.
799 mPopupPanel.SetPopupBoundary( boundingRectangle );
802 const Rect<float> TextInput::GetBoundingRectangle() const
804 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
806 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
807 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
809 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
814 const Vector4& TextInput::GetSelectionHandleFlipMargin()
816 return mSelectionHandleFlipMargin;
819 void TextInput::SetTextColor( const Vector4& color )
821 mDisplayedTextView.SetColor( color );
824 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
826 if( style != mInputStyle )
829 bool emitSignal = false;
831 // mask: modify style according to mask, if different emit signal.
832 const TextStyle oldInputStyle( mInputStyle );
834 // Copy the new style.
835 mInputStyle.Copy( style, mask );
837 // if style has changed, emit signal.
838 if( oldInputStyle != mInputStyle )
843 // Updates the line height accordingly with the input style.
846 // Changing font point size will require the cursor to be re-sized
851 EmitStyleChangedSignal();
856 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
858 if ( IsTextSelected() )
860 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
861 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
863 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
865 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
868 // Keeps the old style to be compared with the new one.
869 const TextStyle oldInputStyle( mInputStyle );
871 // Copy only those parameters from the style which are set in the mask.
872 mInputStyle.Copy( style, mask );
874 if( mInputStyle != oldInputStyle )
876 // Updates the line height accordingly with the input style.
879 EmitStyleChangedSignal();
884 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
886 if( !mStyledText.empty() )
888 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
892 TextStyle TextInput::GetStyleAtCursor() const
896 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
898 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
899 style = mStyledText.at( mCursorPosition-1 ).mStyle;
905 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
907 Dali::Font defaultFont = Dali::Font::New();
908 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
915 TextStyle TextInput::GetStyleAt( std::size_t position ) const
917 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
919 if( position >= mStyledText.size() )
921 position = mStyledText.size() - 1;
924 return mStyledText.at( position ).mStyle;
927 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
929 mDisplayedTextView.SetTextAlignment( align );
930 mOverrideAutomaticAlignment = true;
933 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
935 mDisplayedTextView.SetLineJustification( justification );
936 mOverrideAutomaticAlignment = true;
939 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
941 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
944 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
946 return mDisplayedTextView.GetFadeBoundary();
949 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
951 return mDisplayedTextView.GetTextAlignment();
954 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
956 mDisplayedTextView.SetMultilinePolicy( policy );
959 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
961 return mDisplayedTextView.GetMultilinePolicy();
964 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
966 mDisplayedTextView.SetWidthExceedPolicy( policy );
969 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
971 return mDisplayedTextView.GetWidthExceedPolicy();
974 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
976 mDisplayedTextView.SetHeightExceedPolicy( policy );
979 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
981 return mDisplayedTextView.GetHeightExceedPolicy();
984 void TextInput::SetExceedEnabled( bool enable )
986 mExceedEnabled = enable;
989 bool TextInput::GetExceedEnabled() const
991 return mExceedEnabled;
994 void TextInput::SetBackground(Dali::Image image )
996 // TODO Should add this function and add public api to match.
999 bool TextInput::OnTouchEvent(const TouchEvent& event)
1004 bool TextInput::OnKeyEvent(const KeyEvent& event)
1006 switch( event.state )
1008 case KeyEvent::Down:
1010 return OnKeyDownEvent(event);
1016 return OnKeyUpEvent(event);
1028 void TextInput::OnKeyInputFocusGained()
1030 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1032 mEditModeActive = true;
1034 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1036 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1038 // Updates the line height accordingly with the input style.
1041 // Connect the signals to use in text input.
1042 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1043 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1045 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1048 GetTextLayoutInfo();
1051 SetCursorVisibility( true );
1052 StartCursorBlinkTimer();
1054 Toolkit::TextInput handle( GetOwner() );
1055 mInputStartedSignalV2.Emit( handle );
1057 ImfManager imfManager = ImfManager::Get();
1061 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1063 // Notify that the text editing start.
1064 imfManager.Activate();
1066 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1067 imfManager.SetRestoreAferFocusLost( true );
1069 imfManager.SetCursorPosition( mCursorPosition );
1070 imfManager.NotifyCursorPosition();
1073 mClipboard = Clipboard::Get(); // Store handle to clipboard
1075 // Now in edit mode we can accept string to paste from clipboard
1076 if( Adaptor::IsAvailable() )
1078 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1081 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1086 void TextInput::OnKeyInputFocusLost()
1088 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1092 // If key input focus is lost, it removes the
1093 // underline from the last pre-edit text.
1094 RemovePreEditStyle();
1095 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1096 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1100 ImfManager imfManager = ImfManager::Get();
1103 // The text editing is finished. Therefore the imf manager don't have restore activation.
1104 imfManager.SetRestoreAferFocusLost( false );
1106 // Notify that the text editing finish.
1107 imfManager.Deactivate();
1109 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1111 // Disconnect signal used the text input.
1112 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1114 Toolkit::TextInput handle( GetOwner() );
1115 mInputFinishedSignalV2.Emit( handle );
1116 mEditModeActive = false;
1117 mPreEditFlag = false;
1119 SetCursorVisibility( false );
1120 StopCursorBlinkTimer();
1122 ShowGrabHandleAndSetVisibility( false );
1125 // No longer in edit mode so do not want to receive string from clipboard
1126 if( Adaptor::IsAvailable() )
1128 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1131 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1133 Clipboard clipboard = Clipboard::Get();
1137 clipboard.HideClipboard();
1142 void TextInput::OnControlStageConnection()
1144 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1146 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1148 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1152 void TextInput::CreateActiveLayer()
1154 Actor self = Self();
1155 mActiveLayer = Layer::New();
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 Image mCursorImage = Image::New( DEFAULT_CURSOR );
1174 mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1175 mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1177 Actor self = Self();
1178 self.Add( mCursor );
1179 self.Add( mCursorRTL );
1181 mCursorVisibility = false;
1183 CreateActiveLayer(); // todo move this so layer only created when needed.
1185 // Assign names to image actors
1186 mCursor.SetName("mainCursor");
1187 mCursorRTL.SetName("rtlCursor");
1190 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1192 mDisplayedTextView.SetSize( targetSize );
1193 GetTextLayoutInfo();
1194 mActiveLayer.SetSize(targetSize);
1197 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1199 Relayout( mDisplayedTextView, size, container );
1200 GetTextLayoutInfo();
1205 Vector3 TextInput::GetNaturalSize()
1207 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1209 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1211 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1212 naturalSize.height = mLineHeight;
1218 float TextInput::GetHeightForWidth( float width )
1220 float height = mDisplayedTextView.GetHeightForWidth( width );
1222 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1224 // If the height is zero, it means there is no text. Let's return the cursor height.
1225 height = mLineHeight;
1231 /*end of Virtual methods from parent*/
1233 // Private Internal methods
1235 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1237 switch (gesture.state)
1239 case Gesture::Started:
1240 // fall through so code not duplicated
1241 case Gesture::Continuing:
1243 if (actor == mGrabArea)
1245 SetCursorVisibility( true );
1246 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1247 MoveGrabHandle( gesture.displacement );
1248 HidePopup(); // Do not show popup whilst handle is moving
1250 else 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 MoveSelectionHandle( HandleOne, gesture.displacement );
1258 mState = StateDraggingHandle;
1261 else if (actor == mHandleTwoGrabArea)
1263 // the displacement in PanGesture is affected by the actor's rotation.
1264 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1265 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1267 MoveSelectionHandle( HandleTwo, gesture.displacement );
1269 mState = StateDraggingHandle;
1275 case Gesture::Finished:
1277 // Revert back to non-pressed selection handle images
1278 if (actor == mGrabArea)
1280 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1281 SetCursorVisibility( true );
1282 SetUpPopupSelection();
1285 if (actor == mHandleOneGrabArea)
1287 // the displacement in PanGesture is affected by the actor's rotation.
1288 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1289 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1291 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1293 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1295 ShowPopupCutCopyPaste();
1297 if (actor == mHandleTwoGrabArea)
1299 // the displacement in PanGesture is affected by the actor's rotation.
1300 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1301 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1303 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1305 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1307 ShowPopupCutCopyPaste();
1316 // Stop the flashing animation so easy to see when moved.
1317 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1319 if (touch.GetPoint(0).state == TouchPoint::Down)
1321 SetCursorVisibility( true );
1322 StopCursorBlinkTimer();
1324 else if (touch.GetPoint(0).state == TouchPoint::Up)
1326 SetCursorVisibility( true );
1327 StartCursorBlinkTimer();
1332 // selection handle one
1333 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1335 if (touch.GetPoint(0).state == TouchPoint::Down)
1337 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1339 else if (touch.GetPoint(0).state == TouchPoint::Up)
1341 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1346 // selection handle two
1347 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1349 if (touch.GetPoint(0).state == TouchPoint::Down)
1351 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1353 else if (touch.GetPoint(0).state == TouchPoint::Up)
1355 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1360 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1362 // If text exists then select nearest word.
1363 if ( !mStyledText.empty())
1367 ShowGrabHandleAndSetVisibility( false );
1372 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1373 // converts the pre-edit word being displayed to a committed word.
1374 if ( !mUnderlinedPriorToPreEdit )
1377 style.SetUnderline( false );
1378 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1380 mPreEditFlag = false;
1381 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1382 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1383 PreEditReset( false );
1385 mCursorPosition = 0;
1387 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1388 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1390 ImfManager imfManager = ImfManager::Get();
1393 imfManager.SetCursorPosition ( mCursorPosition );
1394 imfManager.NotifyCursorPosition();
1397 std::size_t start = 0;
1398 std::size_t end = 0;
1399 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1401 SelectText( start, end );
1403 // if no text but clipboard has content then show paste option
1404 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1406 ShowPopupCutCopyPaste();
1409 // If no text and clipboard empty then do nothing
1412 // TODO: Change the function name to be more general.
1413 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1415 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1416 , (mEditOnTouch)?"true":"false"
1417 , (mEditModeActive)?"true":"false");
1419 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1424 if( mGrabArea == actor )
1426 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1428 SetUpPopupSelection();
1438 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1440 // Initially don't create the grab handle.
1441 bool createGrabHandle = false;
1443 if ( !mEditModeActive )
1445 // update line height before calculate the actual position.
1448 // Only start edit mode if TextInput configured to edit on touch
1451 // Set the initial cursor position in the tap point.
1452 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1454 // Create the grab handle.
1455 // TODO Make this a re-usable function.
1456 if ( IsGrabHandleEnabled() )
1458 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1462 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1463 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1464 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1465 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1469 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1470 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1471 // otherwise the Grab handle will be shown when selecting.
1478 // Show the keyboard if it was hidden.
1479 if (!VirtualKeyboard::IsVisible())
1481 VirtualKeyboard::Show();
1484 // Reset keyboard as tap event has occurred.
1485 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1486 PreEditReset( true );
1488 GetTextLayoutInfo();
1490 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1492 // 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.
1494 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1496 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1498 // Notify keyboard so it can 're-capture' word for predictive text.
1499 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1500 ImfManager imfManager = ImfManager::Get();
1503 imfManager.SetCursorPosition ( mCursorPosition );
1504 imfManager.NotifyCursorPosition();
1506 const TextStyle oldInputStyle( mInputStyle );
1508 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1512 // Create the grab handle.
1513 // Grab handle is created later.
1514 createGrabHandle = true;
1516 if( oldInputStyle != mInputStyle )
1518 // Updates the line height accordingly with the input style.
1521 EmitStyleChangedSignal();
1526 if ( createGrabHandle && IsGrabHandleEnabled() )
1528 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1532 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1533 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1534 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1535 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1540 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1542 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1544 if(longPress.state == Dali::Gesture::Started)
1546 // Start edit mode on long press
1547 if ( !mEditModeActive )
1552 // If text exists then select nearest word.
1553 if ( !mStyledText.empty())
1557 ShowGrabHandleAndSetVisibility( false );
1562 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1563 // converts the pre-edit word being displayed to a committed word.
1564 if ( !mUnderlinedPriorToPreEdit )
1567 style.SetUnderline( false );
1568 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1570 mPreEditFlag = false;
1571 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1572 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1573 PreEditReset( false );
1575 mCursorPosition = 0;
1577 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1578 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1580 ImfManager imfManager = ImfManager::Get();
1583 imfManager.SetCursorPosition ( mCursorPosition );
1584 imfManager.NotifyCursorPosition();
1586 std::size_t start = 0;
1587 std::size_t end = 0;
1588 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1590 SelectText( start, end );
1593 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1594 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1596 ShowPopupCutCopyPaste();
1601 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1603 const Text clipboardText( notifier.GetContent() );
1604 PasteText( clipboardText );
1606 SetCursorVisibility( true );
1607 StartCursorBlinkTimer();
1609 ShowGrabHandleAndSetVisibility( false );
1615 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1617 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1619 const std::string& name = button.GetName();
1621 if(name == TextInputPopup::OPTION_SELECT_WORD)
1623 std::size_t start = 0;
1624 std::size_t end = 0;
1625 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1627 SelectText( start, end );
1629 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1631 SetCursorVisibility(false);
1632 StopCursorBlinkTimer();
1634 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1635 std::size_t start = 0;
1637 SelectText( start, end );
1639 else if(name == TextInputPopup::OPTION_CUT)
1641 bool ret = CopySelectedTextToClipboard();
1645 DeleteHighlightedText( true );
1649 SetCursorVisibility( true );
1650 StartCursorBlinkTimer();
1654 else if(name == TextInputPopup::OPTION_COPY)
1656 CopySelectedTextToClipboard();
1660 SetCursorVisibility( true );
1661 StartCursorBlinkTimer();
1665 else if(name == TextInputPopup::OPTION_PASTE)
1667 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1669 PasteText(retrievedString);
1671 SetCursorVisibility( true );
1672 StartCursorBlinkTimer();
1674 ShowGrabHandleAndSetVisibility( false );
1678 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1680 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1681 // Hence pass the false parameter for signalFinished.
1682 HidePopup( true, false );
1683 mClipboard.ShowClipboard();
1689 bool TextInput::OnCursorBlinkTimerTick()
1692 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1693 if ( mCursorRTLEnabled )
1695 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1697 mCursorBlinkStatus = !mCursorBlinkStatus;
1702 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1704 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1706 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1707 if(mHighlightMeshActor && mState == StateEdit)
1709 ShowPopupCutCopyPaste();
1713 //FIXME this routine needs to be re-written as it contains too many branches.
1714 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1716 std::string keyName = event.keyPressedName;
1717 std::string keyString = event.keyPressed;
1719 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1721 // Do not consume "Tab" and "Escape" keys.
1722 if(keyName == "Tab" || keyName == "Escape")
1724 // Escape key to end the edit mode
1730 HidePopup(); // If Pop-up shown then hides it as editing text.
1732 // Update Flag, indicates whether to update the text-input contents or not.
1733 // Any key stroke that results in a visual change of the text-input should
1734 // set this flag to true.
1737 // Whether to scroll text to cursor position.
1738 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1739 bool scroll = false;
1741 if (keyName == "Return")
1743 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1745 bool preEditFlagPreviouslySet( mPreEditFlag );
1747 if (mHighlightMeshActor)
1749 // replaces highlighted text with new line
1750 DeleteHighlightedText( false );
1752 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1754 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1755 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1758 mCommitByKeyInput = true;
1761 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1762 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1764 mPreEditFlag = true;
1765 mIgnoreCommitFlag = false;
1775 else if ( keyName == "space" )
1777 if ( mHighlightMeshActor )
1779 // Some text is selected so erase it before adding space.
1780 DeleteHighlightedText( true );
1784 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1786 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1787 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1790 mCommitByKeyInput = true;
1795 else if (keyName == "BackSpace")
1797 if ( mHighlightMeshActor )
1799 // Some text is selected so erase it
1800 DeleteHighlightedText( true );
1805 if ( mCursorPosition > 0 )
1807 DeleteCharacter( mCursorPosition );
1813 else if (keyName == "Right")
1818 else if (keyName == "Left")
1820 AdvanceCursor(true);
1823 else // event is a character
1825 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1826 if ( !keyString.empty() )
1828 if ( mHighlightMeshActor )
1830 // replaces highlighted text with new character
1831 DeleteHighlightedText( false );
1835 // Received key String
1836 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1842 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1843 // as this is a costly operation.
1849 if(update || scroll)
1851 if( IsScrollEnabled() )
1853 // Calculates the new cursor position (in actor coordinates)
1854 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1856 ScrollTextViewToMakeCursorVisible( cursorPosition );
1863 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1865 std::string keyName = event.keyPressedName;
1866 std::string keyString = event.keyPressed;
1868 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1870 // The selected text become deselected when the key code is DALI_KEY_BACK.
1871 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1880 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1882 // Updates the stored scroll position.
1883 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1885 const Vector3& controlSize = GetControlSize();
1886 Size cursorSize( CURSOR_THICKNESS, 0.f );
1888 // Updates the cursor and grab handle position and visibility.
1889 if( mGrabHandle || mCursor )
1891 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1892 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1894 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1896 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1900 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1901 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1906 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1907 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1911 // Updates the selection handles and highlighted text position and visibility.
1912 if( mSelectionHandleOne && mSelectionHandleTwo )
1914 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1915 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1916 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1917 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1918 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1919 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1921 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1922 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1924 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1925 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1926 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1927 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1929 if( mHighlightMeshActor )
1931 mHighlightMeshActor.SetVisible( true );
1937 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1939 // Scroll the text to make the cursor visible.
1940 const Size cursorSize( CURSOR_THICKNESS,
1941 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1943 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1945 const Vector3& controlSize = GetControlSize();
1947 // Calculates the new scroll position.
1948 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1949 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1951 scrollOffset.x += cursorPosition.x;
1954 if( cursorPosition.y - cursorSize.height < 0.f )
1956 scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1958 else if( cursorPosition.y > controlSize.height )
1960 scrollOffset.y += cursorPosition.y;
1963 // Sets the new scroll position.
1964 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1965 SetScrollPosition( scrollOffset );
1968 void TextInput::StartScrollTimer()
1972 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1973 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1976 if( !mScrollTimer.IsRunning() )
1978 mScrollTimer.Start();
1982 void TextInput::StopScrollTimer()
1986 mScrollTimer.Stop();
1990 bool TextInput::OnScrollTimerTick()
1992 // TODO: need to set the new style accordingly the new handle position.
1994 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1996 // nothing to do if all handles are invisible or doesn't exist.
2002 // Choose between the grab handle or the selection handles.
2003 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2004 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2005 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2007 std::size_t newCursorPosition = 0;
2008 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2010 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2011 // the new selection handle's position needs to be different of the other one.
2012 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2013 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2014 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2016 if( differentSelectionHandles )
2018 handlePosition = newCursorPosition;
2020 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2022 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2024 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2025 scrollPosition += scrollDelta;
2026 SetScrollPosition( scrollPosition );
2028 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2033 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2036 actualHandlePosition.x += mScrollDisplacement.x;
2037 actualHandlePosition.y += mScrollDisplacement.y;
2042 // Public Internal Methods (public for testing purpose)
2044 void TextInput::SetUpTouchEvents()
2046 if ( !mTapDetector )
2048 mTapDetector = TapGestureDetector::New();
2049 // Attach the actors and connect the signal
2050 mTapDetector.Attach(Self());
2052 // As contains children which may register for tap the default control detector is not used.
2053 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2056 if ( !mDoubleTapDetector )
2058 mDoubleTapDetector = TapGestureDetector::New();
2059 mDoubleTapDetector.SetTapsRequired( 2 );
2060 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2062 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2063 // so that we do not, unnecessarily, have a double tap request all the time
2066 if ( !mPanGestureDetector )
2068 mPanGestureDetector = PanGestureDetector::New();
2069 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2072 if ( !mLongPressDetector )
2074 mLongPressDetector = LongPressGestureDetector::New();
2075 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2076 mLongPressDetector.Attach(Self());
2080 void TextInput::CreateTextViewActor()
2082 mDisplayedTextView = Toolkit::TextView::New();
2083 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2084 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2085 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2086 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2087 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2088 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2089 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2090 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2091 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2092 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2094 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2096 Self().Add( mDisplayedTextView );
2099 // Start a timer to initiate, used by the cursor to blink.
2100 void TextInput::StartCursorBlinkTimer()
2102 if ( !mCursorBlinkTimer )
2104 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2105 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2108 if ( !mCursorBlinkTimer.IsRunning() )
2110 mCursorBlinkTimer.Start();
2114 // Start a timer to initiate, used by the cursor to blink.
2115 void TextInput::StopCursorBlinkTimer()
2117 if ( mCursorBlinkTimer )
2119 mCursorBlinkTimer.Stop();
2123 void TextInput::StartEditMode()
2125 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2127 if(!mEditModeActive)
2132 if ( mDoubleTapDetector )
2134 mDoubleTapDetector.Attach( Self() );
2138 void TextInput::EndEditMode()
2140 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2142 ClearKeyInputFocus();
2144 if ( mDoubleTapDetector )
2146 mDoubleTapDetector.Detach( Self() );
2150 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2152 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2154 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2156 style.SetUnderline( true );
2157 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2161 void TextInput::RemovePreEditStyle()
2163 if ( !mUnderlinedPriorToPreEdit )
2166 style.SetUnderline( false );
2167 SetActiveStyle( style, TextStyle::UNDERLINE );
2171 // IMF related methods
2174 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2176 bool update( false );
2177 bool preeditResetRequired ( false );
2179 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2181 HidePopup(); // If Pop-up shown then hides it as editing text.
2184 switch ( imfEvent.eventName )
2186 case ImfManager::PREEDIT:
2188 mIgnoreFirstCommitFlag = false;
2190 // 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
2191 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2193 // replaces highlighted text with new character
2194 DeleteHighlightedText( false );
2197 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2199 if( IsScrollEnabled() )
2201 // Calculates the new cursor position (in actor coordinates)
2202 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2203 ScrollTextViewToMakeCursorVisible( cursorPosition );
2210 case ImfManager::COMMIT:
2212 if( mIgnoreFirstCommitFlag )
2214 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2215 mIgnoreFirstCommitFlag = false;
2219 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2221 // 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
2222 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2224 // replaces highlighted text with new character
2225 DeleteHighlightedText( false );
2228 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2229 // not needed, one such scenario is when the pre-edit word is too long to fit.
2230 if ( !mIgnoreCommitFlag )
2232 update = CommitReceived( imfEvent.predictiveString );
2236 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2242 if( IsScrollEnabled() )
2244 // Calculates the new cursor position (in actor coordinates)
2245 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2247 ScrollTextViewToMakeCursorVisible( cursorPosition );
2252 case ImfManager::DELETESURROUNDING:
2254 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2255 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2257 mPreEditFlag = false;
2259 std::size_t toDelete = 0;
2260 std::size_t numberOfCharacters = 0;
2262 if( mHighlightMeshActor )
2264 // delete highlighted text.
2265 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2266 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2270 if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2272 toDelete = mCursorPosition + imfEvent.cursorOffset;
2274 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2276 numberOfCharacters = mStyledText.size() - toDelete;
2280 numberOfCharacters = imfEvent.numberOfChars;
2283 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2284 DeleteRange( toDelete, numberOfCharacters );
2286 mCursorPosition = toDelete;
2287 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2291 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2294 case ImfManager::GETSURROUNDING:
2296 // 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
2297 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2298 if (! ( mHighlightMeshActor || mSelectingText ) )
2300 std::string text( GetText() );
2301 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2303 imfManager.SetCursorPosition( mCursorPosition );
2304 imfManager.SetSurroundingText( text );
2307 if( 0 != mNumberOfSurroundingCharactersDeleted )
2309 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2310 mNumberOfSurroundingCharactersDeleted = 0;
2312 if( mStyledText.empty() )
2314 // Styled text is empty, so set the placeholder text.
2315 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2316 mPlaceHolderSet = true;
2321 case ImfManager::VOID:
2323 DALI_ASSERT_DEBUG( false );
2327 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2329 return callbackData;
2332 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2334 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2336 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2337 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2339 bool preeditResetRequest ( false );
2341 if( mPreEditFlag ) // Already in pre-edit state.
2343 if( mStyledText.size() >= mMaxStringLength )
2345 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2346 // Cannot fit these characters into field, clear pre-edit.
2347 if ( !mUnderlinedPriorToPreEdit )
2350 style.SetUnderline( false );
2351 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2353 mIgnoreCommitFlag = true;
2354 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2355 mPreEditFlag = false;
2356 EmitMaxInputCharactersReachedSignal();
2360 // delete existing pre-edit string
2361 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2363 // Store new pre-edit string
2364 mPreEditString.SetText( keyString );
2366 if ( keyString.empty() )
2368 mPreEditFlag = false;
2369 mCursorPosition = mPreEditStartPosition;
2371 if( mStyledText.empty() )
2373 // Styled text is empty, so set the placeholder text.
2374 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2375 mPlaceHolderSet = true;
2379 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2381 GetTextLayoutInfo();
2386 // Insert new pre-edit string. InsertAt updates the size and position table.
2387 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2388 // 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.
2389 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2390 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2391 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2394 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2398 else // mPreEditFlag not set
2400 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2402 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2403 // new pre-edit so move into pre-edit state by setting flag
2404 mPreEditFlag = true;
2405 mPreEditString.SetText( keyString ); // store new pre-edit string
2406 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2407 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2408 // 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.
2409 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2410 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2411 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2412 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2418 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2422 return preeditResetRequest;
2425 bool TextInput::CommitReceived(const std::string& keyString )
2427 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2428 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2430 bool update( false );
2432 RemovePreEditStyle();
2434 const std::size_t styledTextSize( mStyledText.size() );
2435 if( styledTextSize >= mMaxStringLength )
2437 // Cannot fit these characters into field, clear pre-edit.
2440 mIgnoreCommitFlag = true;
2441 mPreEditFlag = false;
2443 EmitMaxInputCharactersReachedSignal();
2449 // delete existing pre-edit string
2450 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2451 mPreEditFlag = false;
2453 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2454 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2456 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2458 // No need to update cursor position as Cursor location given by touch.
2459 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2460 mPreserveCursorPosition = false;
2464 // Cursor not set by touch so needs to be re-positioned to input more text
2465 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2467 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2468 if ( mCommitByKeyInput )
2470 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2471 mCommitByKeyInput = false;
2477 if ( mSelectTextOnCommit )
2479 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2484 else // mPreEditFlag not set
2486 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2488 if( mStyledText.empty() && mPlaceHolderSet )
2490 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2491 mDisplayedTextView.SetText( "" );
2492 mNumberOfSurroundingCharactersDeleted = 0;
2493 mPlaceHolderSet = false;
2495 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2497 mNumberOfSurroundingCharactersDeleted = 0;
2502 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2507 mSelectTextOnCommit = false;
2509 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2510 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2515 // End of IMF related methods
2517 std::size_t TextInput::DeletePreEdit()
2519 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2521 DALI_ASSERT_DEBUG( mPreEditFlag );
2523 const std::size_t preEditStringLength = mPreEditString.GetLength();
2524 const std::size_t styledTextSize = mStyledText.size();
2526 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2528 // Prevents erase items outside mStyledText bounds.
2529 if( mPreEditStartPosition > styledTextSize )
2531 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2532 mPreEditStartPosition = styledTextSize;
2535 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2537 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2538 endPosition = styledTextSize;
2541 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2543 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2544 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2546 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2548 return preEditStringLength;
2551 void TextInput::PreEditReset( bool preserveCursorPosition )
2553 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2554 preserveCursorPosition, mCursorPosition);
2556 // 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.
2557 mPreserveCursorPosition = preserveCursorPosition;
2559 // Reset incase we are in a pre-edit state.
2560 ImfManager imfManager = ImfManager::Get();
2563 imfManager.Reset(); // Will trigger a commit message
2567 void TextInput::CursorUpdate()
2571 ImfManager imfManager = ImfManager::Get();
2574 std::string text( GetText() );
2575 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2576 imfManager.SetCursorPosition ( mCursorPosition );
2577 imfManager.NotifyCursorPosition();
2581 /* Delete highlighted characters redisplay*/
2582 void TextInput::DeleteHighlightedText( bool inheritStyle )
2584 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2586 if(mHighlightMeshActor)
2588 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2590 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2591 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2593 // Get the styled text of the characters to be deleted as it may be needed if
2594 // the "exceed the text-input's boundaries" option is disabled.
2595 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2597 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2599 mStyledText.erase( start, end ); // erase range of characters
2601 // Remove text from TextView.
2603 if( mStyledText.empty() )
2605 // Styled text is empty, so set the placeholder text.
2606 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2607 mPlaceHolderSet = true;
2611 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2613 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2615 // It may happen than after removing a white space or a new line character,
2616 // two words merge, this new word could be big enough to not fit in its
2617 // current line, so moved to the next one, and make some part of the text to
2618 // exceed the text-input's boundary.
2619 if( !mExceedEnabled )
2621 // Get the new text layout after removing some characters.
2622 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2624 // Get text-input's size.
2625 const Vector3& size = GetControlSize();
2627 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2628 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2630 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2632 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2633 styledCharactersToDelete.begin(),
2634 styledCharactersToDelete.end() );
2638 GetTextLayoutInfo();
2644 const TextStyle oldInputStyle( mInputStyle );
2646 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2648 if( oldInputStyle != mInputStyle )
2650 // Updates the line height accordingly with the input style.
2653 EmitStyleChangedSignal();
2659 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2661 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2662 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2664 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2667 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2669 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2670 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2672 mStyledText.erase(itStart, itEnd);
2674 // update the selection handles if they are visible.
2675 if( mHighlightMeshActor )
2677 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2678 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2680 if( minHandle >= start + ncharacters )
2682 minHandle -= ncharacters;
2684 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2689 if( maxHandle >= start + ncharacters )
2691 maxHandle -= ncharacters;
2693 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2699 // 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.
2702 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2704 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2705 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2706 // Mean we do not re-draw the text more than we have too.
2709 /* Delete character at current cursor position and redisplay*/
2710 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2712 // Ensure positionToDelete is not out of bounds.
2713 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2714 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2715 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2717 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2720 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2722 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2724 // Get the styled text of the character to be deleted as it may be needed if
2725 // the "exceed the text-input's boundaries" option is disabled.
2726 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2728 mStyledText.erase(it); // erase the character left of positionToDelete
2730 if( mStyledText.empty() )
2732 // Styled text is empty, so set the placeholder text.
2733 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2734 mPlaceHolderSet = true;
2738 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2740 const Character characterToDelete = styledCharacterToDelete.mText[0];
2742 // It may happen than after removing a white space or a new line character,
2743 // two words merge, this new word could be big enough to not fit in its
2744 // current line, so moved to the next one, and make some part of the text to
2745 // exceed the text-input's boundary.
2746 if( !mExceedEnabled )
2748 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2750 // Get the new text layout after removing one character.
2751 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2753 // Get text-input's size.
2754 const Vector3& size = GetControlSize();
2756 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2757 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2759 MarkupProcessor::StyledTextArray array;
2760 array.push_back( styledCharacterToDelete );
2761 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2763 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2768 GetTextLayoutInfo();
2770 ShowGrabHandleAndSetVisibility( false );
2772 mCursorPosition = positionToDelete -1;
2774 const TextStyle oldInputStyle( mInputStyle );
2776 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2778 if( oldInputStyle != mInputStyle )
2780 // Updates the line height accordingly with the input style.
2783 EmitStyleChangedSignal();
2788 /*Insert new character into the string and (optionally) redisplay text-input*/
2789 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2791 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2793 // Ensure insertionPosition is not out of bounds.
2794 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2796 bool textExceedsMaximunNumberOfCharacters = false;
2797 bool textExceedsBoundary = false;
2798 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2800 ShowGrabHandleAndSetVisibility( false );
2802 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2806 mIgnoreCommitFlag = true;
2807 mPreEditFlag = false;
2808 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2809 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2812 if( textExceedsMaximunNumberOfCharacters )
2814 EmitMaxInputCharactersReachedSignal();
2817 if( textExceedsBoundary )
2819 EmitInputTextExceedsBoundariesSignal();
2820 PreEditReset( false );
2824 return insertedStringLength;
2827 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2833 cursor = ImageActor::New( cursorImage );
2837 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2840 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2841 cursor.SetNinePatchBorder( border );
2843 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2844 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2845 cursor.SetVisible(false);
2850 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2852 // As cursor is not moving due to grab handle, handle should be hidden.
2853 ShowGrabHandleAndSetVisibility( false );
2855 bool cursorPositionChanged = false;
2858 if ( mCursorPosition >= places )
2860 mCursorPosition = mCursorPosition - places;
2861 cursorPositionChanged = true;
2866 if ((mCursorPosition + places) <= mStyledText.size())
2868 mCursorPosition = mCursorPosition + places;
2869 cursorPositionChanged = true;
2873 if( cursorPositionChanged )
2875 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2877 const TextStyle oldInputStyle( mInputStyle );
2878 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2882 if( oldInputStyle != mInputStyle )
2884 // Updates the line height accordingly with the input style.
2887 EmitStyleChangedSignal();
2890 ImfManager imfManager = ImfManager::Get();
2893 imfManager.SetCursorPosition ( mCursorPosition );
2894 imfManager.NotifyCursorPosition();
2899 void TextInput::DrawCursor(const std::size_t nthChar)
2901 // Get height of cursor and set its size
2902 Size size( CURSOR_THICKNESS, 0.0f );
2903 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2905 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2909 // Measure Font so know how big text will be if no initial text to measure.
2910 size.height = mLineHeight;
2913 mCursor.SetSize(size);
2915 // If the character is italic then the cursor also tilts.
2916 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2918 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2920 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2922 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2923 bool altPositionValid; // Alternate cursor validity flag.
2924 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2925 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2927 SetAltCursorEnabled( altPositionValid );
2929 if(!altPositionValid)
2931 mCursor.SetPosition( position + UI_OFFSET );
2935 size.height *= 0.5f;
2936 mCursor.SetSize(size);
2937 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2939 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2940 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2941 size.height = rowSize.height * 0.5f;
2942 mCursorRTL.SetSize(size);
2943 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2946 if( IsScrollEnabled() )
2948 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2949 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2954 void TextInput::SetAltCursorEnabled( bool enabled )
2956 mCursorRTLEnabled = enabled;
2957 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2960 void TextInput::SetCursorVisibility( bool visible )
2962 mCursorVisibility = visible;
2963 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2964 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2967 void TextInput::CreateGrabHandle( Dali::Image image )
2973 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2977 mGrabHandleImage = image;
2980 mGrabHandle = ImageActor::New(mGrabHandleImage);
2981 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2982 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2984 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2986 ShowGrabHandleAndSetVisibility( false );
2988 CreateGrabArea( mGrabHandle );
2990 mActiveLayer.Add(mGrabHandle);
2994 void TextInput::CreateGrabArea( Actor& parent )
2996 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2997 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2998 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2999 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3000 mTapDetector.Attach( mGrabArea );
3001 mPanGestureDetector.Attach( mGrabArea );
3003 parent.Add(mGrabArea);
3006 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3008 Vector3 actualHandlePosition;
3012 mActualGrabHandlePosition.x += displacement.x;
3013 mActualGrabHandlePosition.y += displacement.y;
3015 // Grab handle should jump to the nearest character and take cursor with it
3016 std::size_t newCursorPosition = 0;
3017 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3019 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3021 bool handleVisible = true;
3023 if( IsScrollEnabled() )
3025 const Vector3 controlSize = GetControlSize();
3026 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3027 // Scrolls the text if the handle is not in a visible position
3028 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3035 mCurrentHandlePosition = actualHandlePosition;
3036 mScrollDisplacement = Vector2::ZERO;
3040 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3042 mScrollDisplacement.x = -SCROLL_SPEED;
3044 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3046 mScrollDisplacement.x = SCROLL_SPEED;
3048 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3050 mScrollDisplacement.y = -SCROLL_SPEED;
3052 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3054 mScrollDisplacement.y = SCROLL_SPEED;
3060 if( handleVisible && // Only redraw cursor and do updates if position changed
3061 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3063 mCursorPosition = newCursorPosition;
3065 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3067 const TextStyle oldInputStyle( mInputStyle );
3069 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3071 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3073 if( oldInputStyle != mInputStyle )
3075 // Updates the line height accordingly with the input style.
3078 EmitStyleChangedSignal();
3083 return actualHandlePosition;
3086 void TextInput::ShowGrabHandle( bool visible )
3088 if ( IsGrabHandleEnabled() )
3092 mGrabHandle.SetVisible( mGrabHandleVisibility );
3094 StartMonitoringStageForTouch();
3098 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3100 mGrabHandleVisibility = visible;
3101 ShowGrabHandle( visible );
3104 // Callbacks connected to be Property notifications for Boundary checking.
3106 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3108 mIsSelectionHandleOneFlipped = true;
3109 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3110 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3113 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3115 mIsSelectionHandleOneFlipped = false;
3116 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3117 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3120 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3122 mIsSelectionHandleTwoFlipped = true;
3123 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3124 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3127 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3129 mIsSelectionHandleTwoFlipped = false;
3130 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3131 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3134 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3135 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3137 mSelectionHandleOne.SetOpacity(0.0f);
3140 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3142 mSelectionHandleOne.SetOpacity(1.0f);
3145 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3147 mSelectionHandleTwo.SetOpacity(0.0f);
3150 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3152 mSelectionHandleTwo.SetOpacity(1.0f);
3155 // End of Callbacks connected to be Property notifications for Boundary checking.
3157 void TextInput::SetUpHandlePropertyNotifications()
3159 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3161 Vector3 handlesize = GetSelectionHandleSize();
3163 // Exceeding horizontal boundary
3164 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3165 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3167 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3168 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3170 // Within horizontal boundary
3171 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3172 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3174 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3175 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3177 // Exceeding vertical boundary
3178 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3179 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3180 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3181 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3183 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3184 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3185 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3186 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3188 // Within vertical boundary
3189 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3190 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3191 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3192 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3194 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3195 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3196 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3197 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3200 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3202 mSelectionHandleOnePosition = start;
3203 mSelectionHandleTwoPosition = end;
3205 if ( !mSelectionHandleOne )
3207 // create normal and pressed images
3208 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3209 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3211 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3212 mSelectionHandleOne.SetName("SelectionHandleOne");
3213 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3214 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3215 mIsSelectionHandleOneFlipped = false;
3216 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3218 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3219 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3221 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3222 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3224 mTapDetector.Attach( mHandleOneGrabArea );
3225 mPanGestureDetector.Attach( mHandleOneGrabArea );
3227 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3229 mSelectionHandleOne.Add( mHandleOneGrabArea );
3230 mActiveLayer.Add( mSelectionHandleOne );
3233 if ( !mSelectionHandleTwo )
3235 // create normal and pressed images
3236 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3237 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3239 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3240 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3241 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3242 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3243 mIsSelectionHandleTwoFlipped = false;
3244 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3246 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3247 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3248 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3249 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3251 mTapDetector.Attach( mHandleTwoGrabArea );
3252 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3254 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3256 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3258 mActiveLayer.Add( mSelectionHandleTwo );
3261 SetUpHandlePropertyNotifications();
3263 // update table as text may have changed.
3264 GetTextLayoutInfo();
3266 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3267 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3269 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3270 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3272 // Calculates and set the visibility if the scroll mode is enabled.
3273 bool isSelectionHandleOneVisible = true;
3274 bool isSelectionHandleTwoVisible = true;
3275 if( IsScrollEnabled() )
3277 const Vector3& controlSize( GetControlSize() );
3278 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3279 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3280 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3281 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3284 CreateHighlight(); // function will only create highlight if not already created.
3287 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3289 Vector3 actualHandlePosition;
3291 if ( mSelectionHandleOne && mSelectionHandleTwo )
3293 const Vector3& controlSize = GetControlSize();
3295 Size cursorSize( CURSOR_THICKNESS, 0.f );
3297 // Get a reference of the wanted selection handle (handle one or two).
3298 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3300 // Get a reference for the current position of the handle and a copy of its pair
3301 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3302 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3304 // Get a handle of the selection handle actor
3305 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3307 // Selection handles should jump to the nearest character
3308 std::size_t newHandlePosition = 0;
3309 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3311 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3313 bool handleVisible = true;
3315 if( IsScrollEnabled() )
3317 mCurrentSelectionId = handleId;
3319 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3320 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3321 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3328 mCurrentSelectionHandlePosition = actualHandlePosition;
3329 mScrollDisplacement = Vector2::ZERO;
3333 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3335 mScrollDisplacement.x = -SCROLL_SPEED;
3337 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3339 mScrollDisplacement.x = SCROLL_SPEED;
3341 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3343 mScrollDisplacement.y = -SCROLL_SPEED;
3345 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3347 mScrollDisplacement.y = SCROLL_SPEED;
3353 if ( handleVisible && // Ensure the handle is visible.
3354 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3355 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3357 currentSelectionHandlePosition = newHandlePosition;
3359 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3360 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3364 if ( handleId == HandleOne )
3366 const TextStyle oldInputStyle( mInputStyle );
3368 // Set Active Style to that of first character in selection
3369 if( mSelectionHandleOnePosition < mStyledText.size() )
3371 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3374 if( oldInputStyle != mInputStyle )
3376 // Updates the line height accordingly with the input style.
3379 EmitStyleChangedSignal();
3385 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3388 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3391 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3392 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3394 if ( selectionHandleActor )
3396 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3397 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3398 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3400 if( IsScrollEnabled() )
3402 const Size cursorSize( CURSOR_THICKNESS,
3403 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3404 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3406 GetControlSize() ) );
3411 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3413 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3414 // For now the visual position of end of logical string will be end of visual string.
3415 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3417 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3420 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3422 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3423 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3424 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3425 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3427 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3429 // Deselect text prior to startSelectionIt
3430 for(;it!=startSelectionIt;++it)
3432 selectedVisualText[*it] = false;
3435 // Select text from startSelectionIt -> endSelectionIt
3436 for(;it!=endSelectionIt;++it)
3438 selectedVisualText[*it] = true;
3441 // Deselect text after endSelection
3444 selectedVisualText[*it] = false;
3447 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3450 // Calculate the dimensions of the quads they will make the highlight mesh
3451 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3453 // At the moment there is no public API to modify the block alignment option.
3454 const bool blockAlignEnabled = true;
3456 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3458 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3460 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3461 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3463 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3464 std::vector<bool> selectedVisualText;
3465 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3466 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3467 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3469 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3470 float rowLeft = 0.0f;
3471 float rowRight = 0.0f;
3472 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3473 float maxRowLeft = std::numeric_limits<float>::max();
3474 float maxRowRight = 0.0f;
3476 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3478 // Scan through entire text.
3481 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3483 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3484 bool charSelected( false );
3485 if( selectedIt != selectedEndIt )
3487 charSelected = *selectedIt++;
3490 if(selectionState == SelectionNone)
3494 selectionState = SelectionStarted;
3495 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3496 rowRight = rowLeft + charInfo.mSize.width;
3499 else if(selectionState == SelectionStarted)
3501 // break selection on:
3502 // 1. new line causing selection break. (\n or wordwrap)
3503 // 2. character not selected.
3504 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3507 // finished selection.
3508 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3509 // that it resides on. That way this enumeration is not necessary.
3511 if(lastIt->mIsNewLineChar)
3513 // 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.
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;
3522 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3523 if(charSelected && blockAlignEnabled)
3525 rowRight = std::numeric_limits<float>::max();
3527 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3529 selectionState = SelectionNone;
3531 // Still selected? start a new selection
3534 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3535 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3536 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3537 selectionState = SelectionStarted;
3542 // build up highlight(s) with this selection data.
3543 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3544 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3551 // If reached end, and still on selection, then close selection.
3554 if(selectionState == SelectionStarted)
3556 // finished selection.
3558 if(lastIt->mIsNewLineChar)
3560 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3562 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3563 maxRowLeft = std::min(maxRowLeft, min.x);
3564 maxRowRight = std::max(maxRowRight, max.x);
3565 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3566 float rowTop = rowBottom - rowSize.height;
3567 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3571 // Get the top left and bottom right corners.
3572 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3573 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3574 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3576 // Clamp quads so they appear to clip to borders of the whole text.
3577 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3579 // For block-align align Further Clamp quads to max left and right extents
3580 if(blockAlignEnabled)
3582 // BlockAlign: Will adjust highlight to block:
3584 // H[ello] (top row right = max of all rows right)
3585 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3586 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3587 // [text] (bottom row left = min of all rows left)
3588 // (common in SMS messaging selection)
3590 // As opposed to the default which is tight text highlighting.
3595 // (common in regular text editors/web browser selection)
3597 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3600 // Finally clamp quads again so they don't exceed the boundry of the control.
3601 const Vector3& controlSize = GetControlSize();
3602 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3605 return mNewHighlightInfo;
3608 void TextInput::UpdateHighlight()
3610 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3612 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3614 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3615 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3616 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3617 // [BOTTOM] [ MIDDLE ]
3620 // Each quad is created as 2 triangles.
3621 // Middle is just 1 quad regardless of its size.
3635 if ( mHighlightMeshActor )
3637 // vertex and triangle buffers should always be present if MeshActor is alive.
3638 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3639 MeshData::VertexContainer vertices;
3640 Dali::MeshData::FaceIndices faceIndices;
3642 if( !newHighlightInfo.mQuadList.empty() )
3644 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3645 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3647 // vertex position defaults to (0 0 0)
3648 MeshData::Vertex vertex;
3649 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3652 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3654 // Add each quad geometry (a sub-selection) to the mesh data.
3664 QuadCoordinates& quad = *iter;
3666 vertex.x = quad.min.x;
3667 vertex.y = quad.min.y;
3668 vertices.push_back( vertex );
3671 vertex.x = quad.max.x;
3672 vertex.y = quad.min.y;
3673 vertices.push_back( vertex );
3675 // bottom-left (v+2)
3676 vertex.x = quad.min.x;
3677 vertex.y = quad.max.y;
3678 vertices.push_back( vertex );
3680 // bottom-right (v+3)
3681 vertex.x = quad.max.x;
3682 vertex.y = quad.max.y;
3683 vertices.push_back( vertex );
3685 // triangle A (3, 1, 0)
3686 faceIndices.push_back( v + 3 );
3687 faceIndices.push_back( v + 1 );
3688 faceIndices.push_back( v );
3690 // triangle B (0, 2, 3)
3691 faceIndices.push_back( v );
3692 faceIndices.push_back( v + 2 );
3693 faceIndices.push_back( v + 3 );
3695 mMeshData.SetFaceIndices( faceIndices );
3698 BoneContainer bones(0); // passed empty as bones not required
3699 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3700 mHighlightMesh.UpdateMeshData(mMeshData);
3705 void TextInput::ClearPopup()
3707 mPopupPanel.Clear();
3710 void TextInput::AddPopupOptions()
3712 mPopupPanel.AddPopupOptions();
3715 void TextInput::SetPopupPosition( const Vector3& position )
3717 mPopupPanel.SetTailPosition( position );
3718 mPopupPanel.GetRootActor().SetPosition( position );
3721 void TextInput::HidePopup(bool animate, bool signalFinished )
3723 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3725 mPopupPanel.Hide( animate );
3727 if( animate && signalFinished )
3729 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3734 void TextInput::ShowPopup(bool animate)
3738 if(mHighlightMeshActor && mState == StateEdit)
3741 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3743 // When text is selected, show popup above top handle (and text), or below bottom handle.
3744 // topHandle: referring to the top most point of the handle or the top line of selection.
3745 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3747 topHandle = mSelectionHandleOneActualPosition;
3748 bottomHandle = mSelectionHandleTwoActualPosition;
3749 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3753 topHandle = mSelectionHandleTwoActualPosition;
3754 bottomHandle = mSelectionHandleOneActualPosition;
3755 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3757 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3758 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3760 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3761 mPopupPanel.SetAlternativeOffset(Vector2( mBoundingRectangleWorldCoordinates.x, bottomHandle.y - topHandle.y));
3763 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( topHandle.x , bottomHandle.x );
3765 position.x = xPosition;
3769 // When no text is selected, show popup at world position of grab handle or cursor
3770 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3771 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3772 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3773 // if can't be positioned above, then position below row.
3774 Vector2 alternativePopupPosition( mBoundingRectangleWorldCoordinates.x, position.y ); // default if no grab handle
3777 // If grab handle enabled then position pop-up below the grab handle.
3778 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3780 mPopupPanel.SetAlternativeOffset( alternativePopupPosition );
3783 // reposition popup above the desired cursor posiiton.
3784 Vector3 textViewSize = mDisplayedTextView.GetCurrentSize();
3785 textViewSize.z = 0.0f;
3786 // World position = world position of local position i.e. top-left corner of TextView
3787 Vector3 worldPosition = mDisplayedTextView.GetCurrentWorldPosition() - ( textViewSize * 0.5f ) + position;
3789 SetPopupPosition( worldPosition );
3792 mPopupPanel.Show(animate);
3793 StartMonitoringStageForTouch();
3795 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3798 void TextInput::ShowPopupCutCopyPaste()
3802 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3803 // Check the selected text is whole text or not.
3804 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3806 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3809 if ( !mStyledText.empty() )
3811 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3812 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3815 if( mClipboard && mClipboard.NumberOfItems() )
3817 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3818 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3823 mPopupPanel.Hide(false);
3827 void TextInput::SetUpPopupSelection()
3830 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3831 // If no text exists then don't offer to select
3832 if ( !mStyledText.empty() )
3834 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3835 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3836 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3838 // if clipboard has valid contents then offer paste option
3839 if( mClipboard && mClipboard.NumberOfItems() )
3841 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3842 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3847 mPopupPanel.Hide(false);
3850 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3855 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3856 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3857 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3858 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3860 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3862 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3864 float closestYdifference = std::numeric_limits<float>::max();
3865 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3866 std::size_t numberOfMatchedCharacters = 0;
3868 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3869 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3871 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3873 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3874 float baselinePosition = info.mPosition.y - info.mDescender;
3876 if( info.mIsVisible )
3878 // store difference between source y point and the y position of the current character
3879 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3881 if( currentYdifference < closestYdifference )
3883 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3884 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3885 closestYdifference = currentYdifference;
3886 matchedCharacters.clear();
3887 numberOfMatchedCharacters = 0; // reset count
3890 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3891 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3893 // ignore new line character.
3894 if( !info.mIsNewLineChar )
3896 matchedCharacters.push_back( info );
3897 numberOfMatchedCharacters++;
3901 } // End of loop checking each character's y position in the character layout table
3903 // Check if last character is a newline, if it is
3904 // then need pretend there is an imaginary line afterwards,
3905 // and check if user is touching below previous line.
3906 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3908 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3910 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3914 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3915 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3917 bool matched( false );
3919 // 2 Iterate through matching list of y positions and find closest matching X position.
3920 for( ; it != endIt; ++it )
3922 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3924 if( info.mIsVisible )
3926 // stop when on left side of character's center.
3927 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3928 if( sourceScrollOffset.x < characterMidPointPosition )
3930 if(info.mIsRightToLeftCharacter)
3932 rightToLeftChar = true;
3934 glyphIntersection = info.mPosition.x;
3939 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3945 rightToLeftChar = lastRightToLeftChar;
3948 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3949 closestIndex = lineOffset + matchCharacterIndex;
3951 mClosestCursorPositionEOL = false; // reset
3952 if ( it == endIt && !matched )
3954 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3957 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3958 if( rightToLeftChar && lastRightToLeftChar )
3960 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3965 // closestIndex is the visual index, need to convert it to the logical index
3966 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3968 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3970 // Checks for situations where user is touching between LTR and RTL
3971 // characters. To identify if the user means the end of a LTR string
3972 // or the beginning of an RTL string, and vice versa.
3973 if( closestIndex > 0 )
3975 if( rightToLeftChar && !lastRightToLeftChar )
3980 // A: In this touch range, the user is indicating that they wish to place
3981 // the cursor at the end of the LTR text.
3982 // B: In this touch range, the user is indicating that they wish to place
3983 // the cursor at the end of the RTL text.
3985 // Result of touching A area:
3986 // [.....LTR]|[RTL......]+
3988 // |: primary cursor (for typing LTR chars)
3989 // +: secondary cursor (for typing RTL chars)
3991 // Result of touching B area:
3992 // [.....LTR]+[RTL......]|
3994 // |: primary cursor (for typing RTL chars)
3995 // +: secondary cursor (for typing LTR chars)
3997 if( sourceScrollOffset.x < glyphIntersection )
4002 else if( !rightToLeftChar && lastRightToLeftChar )
4004 if( sourceScrollOffset.x < glyphIntersection )
4011 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4012 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4013 // one further ahead
4014 if( rightToLeftChar && !lastRightToLeftChar )
4019 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4021 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4023 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4032 float TextInput::GetLineJustificationPosition() const
4034 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4035 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4036 float alignmentOffset = 0.f;
4038 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4039 if( alignment & Toolkit::Alignment::HorizontalLeft )
4041 alignmentOffset = 0.f;
4043 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4045 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4047 else if( alignment & Toolkit::Alignment::HorizontalRight )
4049 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4052 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4053 float justificationOffset = 0.f;
4055 switch( justification )
4057 case Toolkit::TextView::Left:
4059 justificationOffset = 0.f;
4062 case Toolkit::TextView::Center:
4064 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4067 case Toolkit::TextView::Right:
4069 justificationOffset = mTextLayoutInfo.mTextSize.width;
4072 case Toolkit::TextView::Justified:
4074 justificationOffset = 0.f;
4079 DALI_ASSERT_ALWAYS( false );
4083 return alignmentOffset + justificationOffset;
4086 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4088 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4089 A newline character is not inserted in this case */
4091 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4093 Vector3 cursorPosition;
4095 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4097 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4099 // end character so use
4100 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4101 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4105 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4108 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4110 // If previous character on a different line then use current characters position
4111 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4113 if ( mClosestCursorPositionEOL )
4115 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4119 cursorPosition = Vector3(currentCharInfo.mPosition);
4124 // Previous character is on same line so use position of previous character plus it's width.
4125 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4128 return cursorPosition;
4131 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4133 bool direction(false);
4134 Vector3 alternatePosition;
4135 bool alternatePositionValid(false);
4137 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4140 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4142 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4144 alternatePositionValid = false;
4145 directionRTL = false;
4147 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4149 std::size_t visualCharacterPosition;
4151 // When cursor is not at beginning, consider possibility of
4152 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4153 if(characterPosition > 0)
4155 // Cursor position should be the end of the last character.
4156 // If the last character is LTR, then the end is on the right side of the glyph.
4157 // If the last character is RTL, then the end is on the left side of the glyph.
4158 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4160 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4162 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4165 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4166 if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4168 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4169 const Vector3& size = GetControlSize();
4171 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4173 --visualCharacterPosition;
4175 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4178 if(!info.mIsNewLineChar)
4180 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4184 // When cursor points to first character on new line, position cursor at the start of this glyph.
4185 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4187 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4188 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4189 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4191 cursorPosition.x = infoNext.mPosition.x + start;
4192 cursorPosition.y = infoNext.mPosition.y;
4196 // If cursor points to the end of text, then can only position
4197 // cursor where the new line starts based on the line-justification position.
4198 cursorPosition.x = GetLineJustificationPosition();
4200 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4202 // If this is after the last character, then we can assume that the new cursor
4203 // should be exactly one row below the current row.
4205 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4206 cursorPosition.y = info.mPosition.y + rowRect.height;
4210 // If this is not after last character, then we can use this row's height.
4211 // should be exactly one row below the current row.
4213 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4214 cursorPosition.y = info.mPosition.y + rowRect.height;
4219 directionRTL = info.mIsRightToLeftCharacter;
4221 // 1. When the cursor is neither at the beginning or the end,
4222 // we can show multiple cursors under situations when the cursor is
4223 // between RTL and LTR text...
4224 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4226 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4228 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4229 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4231 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4233 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4234 // Text: [...LTR...]|[...RTL...]
4236 // Alternate cursor pos: ^
4237 // In which case we need to display an alternate cursor for the RTL text.
4239 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4240 alternatePosition.y = infoAlt.mPosition.y;
4241 alternatePositionValid = true;
4243 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4245 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4246 // Text: |[...RTL...] [...LTR....]
4248 // Alternate cursor pos: ^
4249 // In which case we need to display an alternate cursor for the RTL text.
4251 alternatePosition.x = infoAlt.mPosition.x;
4252 alternatePosition.y = infoAlt.mPosition.y;
4253 alternatePositionValid = true;
4258 // 2. When the cursor is at the end of the text,
4259 // and we have multi-directional text,
4260 // we can also consider showing mulitple cursors.
4261 // The rule here is:
4262 // If first and last characters on row are different
4263 // Directions, then two cursors need to be displayed.
4265 // Get first logical glyph on row
4266 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4268 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4269 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4271 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4273 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4274 // Text: [...LTR...]|[...RTL...]
4276 // Alternate cursor pos: ^
4277 // In which case we need to display an alternate cursor for the RTL text, this cursor
4278 // should be at the end of the given line.
4280 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4281 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4282 alternatePosition.y = infoAlt.mPosition.y;
4283 alternatePositionValid = true;
4285 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4287 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4288 // Text: |[...RTL...] [...LTR....]
4290 // Alternate cursor pos: ^
4291 // In which case we need to display an alternate cursor for the RTL text.
4293 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4294 alternatePosition.x = infoAlt.mPosition.x;
4295 alternatePosition.y = infoAlt.mPosition.y;
4296 alternatePositionValid = true;
4299 } // characterPosition > 0
4300 else if(characterPosition == 0)
4302 // When the cursor position is at the beginning, it should be at the start of the current character.
4303 // If the current character is LTR, then the start is on the right side of the glyph.
4304 // If the current character is RTL, then the start is on the left side of the glyph.
4305 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4307 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4309 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4312 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4313 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4315 cursorPosition.x = info.mPosition.x + start;
4316 cursorPosition.y = info.mPosition.y;
4317 directionRTL = info.mIsRightToLeftCharacter;
4322 // If the character table is void, place the cursor accordingly the text alignment.
4323 const Vector3& size = GetControlSize();
4325 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4326 float alignmentOffset = 0.f;
4328 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4329 if( alignment & Toolkit::Alignment::HorizontalLeft )
4331 alignmentOffset = 0.f;
4333 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4335 alignmentOffset = 0.5f * ( size.width );
4337 else if( alignment & Toolkit::Alignment::HorizontalRight )
4339 alignmentOffset = size.width;
4342 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4343 cursorPosition.x = alignmentOffset;
4345 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4346 if( alignment & Toolkit::Alignment::VerticalTop )
4348 cursorPosition.y = mLineHeight;
4350 else if( alignment & Toolkit::Alignment::VerticalCenter )
4352 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4354 else if( alignment & Toolkit::Alignment::VerticalBottom )
4356 cursorPosition.y = size.height;
4360 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4361 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4362 if( alternatePositionValid )
4364 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4365 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4368 return cursorPosition;
4371 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4373 // scan string from current position to beginning of current line to note direction of line
4374 while(logicalPosition)
4377 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4378 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4385 return logicalPosition;
4388 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4392 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4395 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4397 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4398 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4400 min = Vector2::ZERO;
4401 max = Vector2(0.0f, mLineHeight);
4405 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4406 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4407 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4409 // If cursor is pointing to end of line, then start from last character.
4410 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4412 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4414 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4415 if( !it->mIsVisible )
4417 characterPosition = FindVisibleCharacter( Left, characterPosition );
4418 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4421 // Scan characters left and right of cursor, stopping when end of line/string reached or
4422 // y position greater than threshold of reference line.
4424 // 1. scan left until we reach the beginning or a different line.
4425 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4426 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4427 // min-x position is the left-most char's left (x)
4428 // max-x position is the right-most char's right (x)
4429 // min-y position is the minimum of all character's top (y)
4430 // max-y position is the maximum of all character's bottom (y+height)
4431 min.y = validCharIt->mPosition.y;
4432 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4437 min.y = std::min(min.y, validCharIt->mPosition.y);
4438 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4447 if( (it->mPosition.y < referenceLine) ||
4448 (it->mIsNewLineChar) ||
4455 // info refers to the first character on this line.
4456 min.x = validCharIt->mPosition.x;
4458 // 2. scan right until we reach end or a different line
4459 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4460 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4464 if( (it->mPosition.y > referenceLine) ||
4465 (it->mIsNewLineChar) ||
4472 min.y = std::min(min.y, validCharIt->mPosition.y);
4473 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4478 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4480 if ( validCharIt != end )
4482 // info refers to the last character on this line.
4483 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4486 return Size( max.x - min.x, max.y - min.y );
4489 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4491 Actor popUpPanel = mPopupPanel.GetRootActor();
4493 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4499 Dali::Actor parent( touchedActor.GetParent() );
4503 return WasTouchedCheck( parent );
4510 void TextInput::StartMonitoringStageForTouch()
4512 Stage stage = Stage::GetCurrent();
4513 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4516 void TextInput::EndMonitoringStageForTouch()
4518 Stage stage = Stage::GetCurrent();
4519 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4522 void TextInput::OnStageTouched(const TouchEvent& event)
4524 if( event.GetPointCount() > 0 )
4526 if ( TouchPoint::Down == event.GetPoint(0).state )
4528 const Actor touchedActor(event.GetPoint(0).hitActor);
4530 bool popUpShown( false );
4532 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4537 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4539 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4541 EndMonitoringStageForTouch();
4542 HidePopup( true, false );
4545 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4547 EndMonitoringStageForTouch();
4548 ShowGrabHandleAndSetVisibility( false );
4554 void TextInput::SelectText(std::size_t start, std::size_t end)
4556 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4557 IsGrabHandleEnabled()?"true":"false",
4558 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4559 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4560 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4562 StartMonitoringStageForTouch();
4564 if ( mEditModeActive ) // Only allow text selection when in edit mode
4566 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4567 mSelectingText = true;
4569 mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4571 ImfManager imfManager = ImfManager::Get();
4574 imfManager.SetCursorPosition ( mCursorPosition );
4575 imfManager.SetSurroundingText( GetText() );
4576 imfManager.NotifyCursorPosition();
4578 // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead.
4580 // Hide grab handle when selecting.
4581 ShowGrabHandleAndSetVisibility( false );
4583 if( start != end ) // something to select
4585 SetCursorVisibility( false );
4586 StopCursorBlinkTimer();
4588 CreateSelectionHandles(start, end);
4591 const TextStyle oldInputStyle( mInputStyle );
4592 mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4594 if( oldInputStyle != mInputStyle )
4596 // Updates the line height accordingly with the input style.
4599 EmitStyleChangedSignal();
4605 mSelectingText = false;
4609 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4611 MarkupProcessor::StyledTextArray currentSelectedText;
4613 if ( IsTextSelected() )
4615 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4616 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4618 for(; it != end; ++it)
4620 MarkupProcessor::StyledText& styledText( *it );
4621 currentSelectedText.push_back( styledText );
4624 return currentSelectedText;
4627 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4629 const std::size_t beginIndex = std::min( begin, end );
4630 const std::size_t endIndex = std::max( begin, end );
4633 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4635 // Create a styled text array used to replace the text into the text-view.
4636 MarkupProcessor::StyledTextArray text;
4637 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4639 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4640 GetTextLayoutInfo();
4642 if( IsScrollEnabled() )
4644 // Need to set the scroll position as the text's size may have changed.
4645 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4648 ShowGrabHandleAndSetVisibility( false );
4654 // Set Handle positioning as the new style may have repositioned the characters.
4655 SetSelectionHandlePosition(HandleOne);
4656 SetSelectionHandlePosition(HandleTwo);
4659 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4661 // Just hide the grab handle when keyboard is hidden.
4662 if (!keyboardShown )
4664 ShowGrabHandleAndSetVisibility( false );
4666 // If the keyboard is not now being shown, then hide the popup panel
4667 mPopupPanel.Hide( true );
4671 // Removes highlight and resumes edit mode state
4672 void TextInput::RemoveHighlight()
4674 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4676 if ( mHighlightMeshActor )
4678 if ( mSelectionHandleOne )
4680 mActiveLayer.Remove( mSelectionHandleOne );
4681 mSelectionHandleOne.Reset();
4682 mSelectionHandleOneOffset.x = 0.0f;
4684 if ( mSelectionHandleTwo )
4686 mActiveLayer.Remove( mSelectionHandleTwo );
4687 mSelectionHandleTwo.Reset();
4688 mSelectionHandleTwoOffset.x = 0.0f;
4691 mNewHighlightInfo.mQuadList.clear();
4693 Self().Remove( mHighlightMeshActor );
4695 SetCursorVisibility( true );
4696 StartCursorBlinkTimer();
4698 mHighlightMeshActor.Reset();
4699 // NOTE: We cannot dereference mHighlightMesh, due
4700 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4705 mSelectionHandleOnePosition = 0;
4706 mSelectionHandleTwoPosition = 0;
4709 void TextInput::CreateHighlight()
4711 if ( !mHighlightMeshActor )
4713 mMeshData = MeshData( );
4714 mMeshData.SetHasNormals( true );
4716 mCustomMaterial = Material::New("CustomMaterial");
4717 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4719 mMeshData.SetMaterial( mCustomMaterial );
4721 mHighlightMesh = Mesh::New( mMeshData );
4723 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4724 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4725 mHighlightMeshActor.SetInheritShaderEffect( false );
4726 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4727 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4728 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4729 mHighlightMeshActor.SetAffectedByLighting(false);
4731 Self().Add(mHighlightMeshActor);
4736 bool TextInput::CopySelectedTextToClipboard()
4738 mCurrentCopySelecton.clear();
4740 mCurrentCopySelecton = GetSelectedText();
4742 std::string stringToStore;
4744 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4745 * a marked up string.
4747 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4748 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4749 bool success = mClipboard.SetItem( stringToStore );
4753 void TextInput::PasteText( const Text& text )
4755 // Update Flag, indicates whether to update the text-input contents or not.
4756 // Any key stroke that results in a visual change of the text-input should
4757 // set this flag to true.
4758 bool update = false;
4759 if( mHighlightMeshActor )
4761 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4762 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4764 ImfManager imfManager = ImfManager::Get();
4767 imfManager.SetCursorPosition( mCursorPosition );
4768 imfManager.NotifyCursorPosition();
4770 DeleteHighlightedText( true );
4774 bool textExceedsMaximunNumberOfCharacters = false;
4775 bool textExceedsBoundary = false;
4777 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4779 mCursorPosition += insertedStringLength;
4780 ImfManager imfManager = ImfManager::Get();
4783 imfManager.SetCursorPosition ( mCursorPosition );
4784 imfManager.NotifyCursorPosition();
4787 update = update || ( insertedStringLength > 0 );
4793 if( insertedStringLength < text.GetLength() )
4795 EmitMaxInputCharactersReachedSignal();
4798 if( textExceedsBoundary )
4800 EmitInputTextExceedsBoundariesSignal();
4804 void TextInput::SetTextDirection()
4806 // Put the cursor to the right if we are empty and an RTL language is being used.
4807 if ( mStyledText.empty() )
4809 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4811 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4812 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4814 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4815 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4817 int alignment( mDisplayedTextView.GetTextAlignment() &
4818 ( Toolkit::Alignment::VerticalTop |
4819 Toolkit::Alignment::VerticalCenter |
4820 Toolkit::Alignment::VerticalBottom |
4821 Toolkit::Alignment::HorizontalCenter ) );
4822 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4824 // If our alignment is in the center, then do not change.
4825 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4827 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4830 // If our justification is in the center, then do not change.
4831 if ( justification != Toolkit::TextView::Center )
4833 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4836 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4837 mDisplayedTextView.SetLineJustification( justification );
4841 void TextInput::UpdateLineHeight()
4843 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4844 mLineHeight = font.GetLineHeight();
4846 // 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.
4848 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4850 if( !mExceedEnabled || shrink )
4852 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4856 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4858 std::size_t position = 0;
4860 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4866 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4868 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4870 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4876 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4877 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4879 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4885 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4890 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4897 void TextInput::SetSortModifier( float depthOffset )
4899 if(mDisplayedTextView)
4901 mDisplayedTextView.SetSortModifier(depthOffset);
4905 void TextInput::SetSnapshotModeEnabled( bool enable )
4907 if(mDisplayedTextView)
4909 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4913 bool TextInput::IsSnapshotModeEnabled() const
4915 bool snapshotEnabled = false;
4917 if(mDisplayedTextView)
4919 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4922 return snapshotEnabled;
4925 void TextInput::SetMarkupProcessingEnabled( bool enable )
4927 mMarkUpEnabled = enable;
4930 bool TextInput::IsMarkupProcessingEnabled() const
4932 return mMarkUpEnabled;
4935 void TextInput::SetScrollEnabled( bool enable )
4937 if( mDisplayedTextView )
4939 mDisplayedTextView.SetScrollEnabled( enable );
4944 // Don't set cursor's and handle's visibility to false if they are outside the
4945 // boundaries of the text-input.
4946 mIsCursorInScrollArea = true;
4947 mIsGrabHandleInScrollArea = true;
4948 if( mSelectionHandleOne && mSelectionHandleTwo )
4950 mSelectionHandleOne.SetVisible( true );
4951 mSelectionHandleTwo.SetVisible( true );
4953 if( mHighlightMeshActor )
4955 mHighlightMeshActor.SetVisible( true );
4961 bool TextInput::IsScrollEnabled() const
4963 bool scrollEnabled = false;
4965 if( mDisplayedTextView )
4967 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4970 return scrollEnabled;
4973 void TextInput::SetScrollPosition( const Vector2& position )
4975 if( mDisplayedTextView )
4977 mDisplayedTextView.SetScrollPosition( position );
4981 Vector2 TextInput::GetScrollPosition() const
4983 Vector2 scrollPosition;
4985 if( mDisplayedTextView )
4987 scrollPosition = mDisplayedTextView.GetScrollPosition();
4990 return scrollPosition;
4993 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4995 // determine number of characters that we can write to style text buffer, this is the insertStringLength
4996 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4997 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4999 // Add style to the new input text.
5000 MarkupProcessor::StyledTextArray textToInsert;
5001 for( std::size_t i = 0; i < insertedStringLength; ++i )
5003 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5004 textToInsert.push_back( newStyledCharacter );
5007 //Insert text to the TextView.
5008 const bool emptyTextView = mStyledText.empty();
5009 if( emptyTextView && mPlaceHolderSet )
5011 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5012 mDisplayedTextView.SetText( textToInsert );
5016 if( 0 == numberOfCharactersToReplace )
5018 mDisplayedTextView.InsertTextAt( position, textToInsert );
5022 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5025 mPlaceHolderSet = false;
5027 if( textToInsert.empty() )
5029 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5030 GetTextLayoutInfo();
5034 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5035 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5038 textExceedsBoundary = false;
5040 if( !mExceedEnabled )
5042 const Vector3& size = GetControlSize();
5044 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5046 // If new text does not fit within TextView
5047 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5048 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5049 GetTextLayoutInfo();
5050 textExceedsBoundary = true;
5051 insertedStringLength = 0;
5054 if( textExceedsBoundary )
5056 // Add the part of the text which fits on the text-input.
5058 // Split the text which doesn't fit in two halves.
5059 MarkupProcessor::StyledTextArray firstHalf;
5060 MarkupProcessor::StyledTextArray secondHalf;
5061 SplitText( textToInsert, firstHalf, secondHalf );
5063 // Clear text. This text will be filled with the text inserted.
5064 textToInsert.clear();
5066 // Where to insert the text.
5067 std::size_t positionToInsert = position;
5069 bool end = text.GetLength() <= 1;
5072 // Insert text and check ...
5073 const std::size_t textLength = firstHalf.size();
5074 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5075 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5077 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5079 // Inserted text doesn't fit.
5081 // Remove inserted text
5082 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5083 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5085 // The iteration finishes when only one character doesn't fit.
5086 end = textLength <= 1;
5090 // Prepare next two halves for next iteration.
5091 MarkupProcessor::StyledTextArray copyText = firstHalf;
5092 SplitText( copyText, firstHalf, secondHalf );
5099 // store text to be inserted in mStyledText.
5100 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5102 // Increase the inserted characters counter.
5103 insertedStringLength += textLength;
5105 // Prepare next two halves for next iteration.
5106 MarkupProcessor::StyledTextArray copyText = secondHalf;
5107 SplitText( copyText, firstHalf, secondHalf );
5109 // Update where next text has to be inserted
5110 positionToInsert += textLength;
5116 if( textToInsert.empty() && emptyTextView )
5118 // No character has been added and the text-view was empty.
5119 // Set the placeholder text.
5120 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5121 mPlaceHolderSet = true;
5125 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5126 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5127 mPlaceHolderSet = false;
5130 return insertedStringLength;
5133 void TextInput::GetTextLayoutInfo()
5135 if( mStyledText.empty() )
5137 // The text-input has no text, clear the text-view's layout info.
5138 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5142 if( mDisplayedTextView )
5144 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5148 // There is no text-view.
5149 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5154 void TextInput::SetOffsetFromText( const Vector4& offset )
5156 mPopupOffsetFromText = offset;
5159 const Vector4& TextInput::GetOffsetFromText() const
5161 return mPopupOffsetFromText;
5164 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5166 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5170 TextInput& textInputImpl( GetImpl( textInput ) );
5172 switch ( propertyIndex )
5174 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5176 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5179 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5181 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5184 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5186 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5189 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5191 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5194 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5196 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5199 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5201 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5204 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5206 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5209 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5211 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5214 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5216 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5219 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5221 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5224 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5226 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5229 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5231 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5234 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5236 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5239 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5241 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5244 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5246 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5253 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5255 Property::Value value;
5257 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5261 TextInput& textInputImpl( GetImpl( textInput ) );
5263 switch ( propertyIndex )
5265 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5267 value = textInputImpl.GetMaterialDiffuseColor();
5270 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5272 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5275 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5277 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5280 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5282 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5285 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5287 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5290 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5292 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5295 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5297 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5300 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5302 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5305 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5307 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5310 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5312 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5315 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5317 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5320 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5322 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5325 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5327 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5330 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5332 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5335 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5337 value = textInputImpl.GetOffsetFromText();
5345 void TextInput::EmitStyleChangedSignal()
5347 // emit signal if input style changes.
5348 Toolkit::TextInput handle( GetOwner() );
5349 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5352 void TextInput::EmitTextModified()
5354 // emit signal when text changes.
5355 Toolkit::TextInput handle( GetOwner() );
5356 mTextModifiedSignal.Emit( handle );
5360 void TextInput::EmitMaxInputCharactersReachedSignal()
5362 // emit signal if max characters is reached during text input.
5363 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5365 Toolkit::TextInput handle( GetOwner() );
5366 mMaxInputCharactersReachedSignalV2.Emit( handle );
5369 void TextInput::EmitInputTextExceedsBoundariesSignal()
5371 // Emit a signal when the input text exceeds the boundaries of the text input.
5373 Toolkit::TextInput handle( GetOwner() );
5374 mInputTextExceedBoundariesSignalV2.Emit( handle );
5377 } // namespace Internal
5379 } // namespace Toolkit