2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
89 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
91 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
95 if( ( *it ).mIsVisible )
97 return --cursorPosition;
106 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
108 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
110 if( ( *it ).mIsVisible )
112 return cursorPosition;
118 return cursorPosition;
122 * Whether the given position plus the cursor size offset is inside the given boundary.
124 * @param[in] position The given position.
125 * @param[in] cursorSize The cursor size.
126 * @param[in] controlSize The given boundary.
128 * @return whether the given position is inside the given boundary.
130 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
132 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
133 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
134 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
135 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
139 * Splits a text in two halves.
141 * If the text's number of characters is odd, firstHalf has one more character.
143 * @param[in] text The text to be split.
144 * @param[out] firstHalf The first half of the text.
145 * @param[out] secondHalf The second half of the text.
147 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
148 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
149 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
154 const std::size_t textLength = text.size();
155 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
157 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
158 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
161 } // end of namespace
169 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
170 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
171 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
172 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
173 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
174 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
175 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
176 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
177 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
178 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
179 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
180 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
181 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
182 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
183 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
184 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
195 return Toolkit::TextInput::New();
198 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
200 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
201 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
202 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
203 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
204 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
205 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
209 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
210 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
211 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
212 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
213 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
214 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
215 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
216 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
217 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
227 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
229 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
231 QuadCoordinates quad(x1, y1, x2, y2);
232 mQuadList.push_back( quad );
235 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
237 for(std::size_t i = 0;i < mQuadList.size(); i++)
239 QuadCoordinates& quad = mQuadList[i];
241 quad.min.Clamp(min, max);
242 quad.max.Clamp(min, max);
246 // [TextInput] ////////////////////////////////////////////////////////////////
248 Dali::Toolkit::TextInput TextInput::New()
250 // Create the implementation
251 TextInputPtr textInput(new TextInput());
252 // Pass ownership to CustomActor via derived handle
253 Dali::Toolkit::TextInput handle(*textInput);
254 handle.SetName( "TextInput");
256 textInput->Initialize();
260 TextInput::TextInput()
261 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
266 mDisplayedTextView(),
267 mStyledPlaceHolderText(),
268 mMaxStringLength( DEFAULT_MAX_SIZE ),
269 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
270 mCursorPosition( 0 ),
271 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
272 mIsSelectionHandleOneFlipped( false ),
273 mIsSelectionHandleTwoFlipped( false ),
274 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
275 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
276 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
277 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
278 mSelectionHandleOnePosition( 0 ),
279 mSelectionHandleTwoPosition( 0 ),
281 mPreEditStartPosition( 0 ),
282 mPreEditLength ( 0 ),
283 mNumberOfSurroundingCharactersDeleted( 0 ),
284 mTouchStartTime( 0 ),
286 mCurrentCopySelecton(),
289 mScrollDisplacement(),
290 mCurrentHandlePosition(),
291 mCurrentSelectionId(),
292 mCurrentSelectionHandlePosition(),
293 mRequestedSelection( 0, 0 ),
294 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
295 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
297 mMaterialColor( LIGHTBLUE ),
298 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
299 mOverrideAutomaticAlignment( false ),
300 mCursorRTLEnabled( false ),
301 mClosestCursorPositionEOL ( false ),
302 mCursorBlinkStatus( true ),
303 mCursorVisibility( false ),
304 mGrabHandleVisibility( false ),
305 mIsCursorInScrollArea( true ),
306 mIsGrabHandleInScrollArea( true ),
307 mEditModeActive( false ),
308 mEditOnTouch( true ),
309 mTextSelection( true ),
310 mExceedEnabled( true ),
311 mGrabHandleEnabled( true ),
312 mIsSelectionHandleFlipEnabled( true ),
313 mPreEditFlag( false ),
314 mIgnoreCommitFlag( false ),
315 mIgnoreFirstCommitFlag( false ),
316 mSelectingText( false ),
317 mPreserveCursorPosition( false ),
318 mSelectTextOnCommit( false ),
319 mUnderlinedPriorToPreEdit ( false ),
320 mCommitByKeyInput( false ),
321 mPlaceHolderSet( false ),
322 mMarkUpEnabled( false )
324 // Updates the line height accordingly with the input style.
328 TextInput::~TextInput()
330 StopCursorBlinkTimer();
335 std::string TextInput::GetText() const
339 // Return text-view's text only if the text-input's text is not empty
340 // in order to not to return the placeholder text.
341 if( !mStyledText.empty() )
343 text = mDisplayedTextView.GetText();
349 std::string TextInput::GetMarkupText() const
351 std::string markupString;
352 MarkupProcessor::GetMarkupString( mStyledText, markupString );
357 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
359 mDisplayedTextView.SetText( stylePlaceHolderText );
360 mPlaceHolderSet = true;
361 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
364 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
366 // Get the placeholder styled text array from the markup string.
367 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
368 if( mStyledText.empty() )
370 ShowPlaceholderText( mStyledPlaceHolderText );
374 std::string TextInput::GetPlaceholderText()
376 // Traverses the styled placeholder array getting only the text.
377 // Note that for some languages a 'character' could be represented by more than one 'char'
379 std::string placeholderText;
380 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
382 placeholderText.append( (*it).mText.GetText() );
385 return placeholderText ;
388 void TextInput::SetInitialText(const std::string& initialText)
390 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
392 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
394 mPreEditFlag = false;
395 mIgnoreCommitFlag = true;
398 SetText( initialText );
399 PreEditReset( false ); // Reset keyboard as text changed
402 void TextInput::SetText(const std::string& initialText)
404 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
406 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
408 if( mStyledText.empty() )
410 ShowPlaceholderText( mStyledPlaceHolderText );
414 mDisplayedTextView.SetText( mStyledText );
415 mPlaceHolderSet = false;
420 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
422 ImfManager imfManager = ImfManager::Get();
425 imfManager.SetCursorPosition( mCursorPosition );
426 imfManager.SetSurroundingText( initialText );
427 imfManager.NotifyCursorPosition();
430 if( IsScrollEnabled() )
432 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
435 ShowGrabHandleAndSetVisibility( false );
444 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
446 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
448 mDisplayedTextView.SetText( styleText );
449 mPlaceHolderSet = false;
451 // If text alignment hasn't been manually set by application developer, then we
452 // automatically determine the alignment based on the content of the text i.e. what
453 // language the text begins with.
454 // TODO: This should determine different alignments for each line (broken by '\n') of text.
455 if(!mOverrideAutomaticAlignment)
457 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
458 bool leftToRight(true);
460 if( !styleText.empty() )
462 bool breakOut(false);
464 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
466 const Text& text = textIter->mText;
468 for( std::size_t i = 0; i < text.GetLength(); ++i )
470 Character character( text[i] );
471 if( character.GetCharacterDirection() != Character::Neutral )
473 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
481 // Based on this direction, either left or right align text if not manually set by application developer.
482 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
483 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
484 Toolkit::Alignment::VerticalTop ) );
485 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
491 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
493 mMaxStringLength = maxChars;
496 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
498 DALI_ASSERT_DEBUG( maxLines > 0 )
502 mNumberOflinesLimit = maxLines;
506 std::size_t TextInput::GetNumberOfLinesLimit() const
508 return mNumberOflinesLimit;
511 std::size_t TextInput::GetNumberOfCharacters() const
513 return mStyledText.size();
517 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
519 mMaterialColor = color;
520 if ( mCustomMaterial )
522 mCustomMaterial.SetDiffuseColor( mMaterialColor );
523 mMeshData.SetMaterial( mCustomMaterial );
527 const Vector4& TextInput::GetMaterialDiffuseColor() const
529 return mMaterialColor;
534 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
536 return mInputStartedSignalV2;
539 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
541 return mInputFinishedSignalV2;
544 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
546 return mCutAndPasteToolBarDisplayedV2;
549 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
551 return mStyleChangedSignalV2;
554 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
556 return mTextModifiedSignal;
559 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
561 return mMaxInputCharactersReachedSignalV2;
564 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
566 return mInputTextExceedBoundariesSignalV2;
569 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
571 Dali::BaseHandle handle( object );
573 bool connected( true );
574 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
576 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
578 textInput.InputStartedSignal().Connect( tracker, functor );
580 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
582 textInput.InputFinishedSignal().Connect( tracker, functor );
584 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
586 textInput.StyleChangedSignal().Connect( tracker, functor );
588 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
590 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
592 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
594 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
598 // signalName does not match any signal
605 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
609 // update line height before calculate the actual position.
614 if( setCursorOnTouchPoint )
616 // Sets the cursor position for the given touch point.
617 ReturnClosestIndex( touchPoint, mCursorPosition );
619 // Creates the grab handle.
620 if( IsGrabHandleEnabled() )
622 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
626 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
627 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
628 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
629 ShowGrabHandleAndSetVisibility( true );
631 // Scrolls the text-view if needed.
632 if( IsScrollEnabled() )
634 ScrollTextViewToMakeCursorVisible( cursorPosition );
640 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
652 bool TextInput::IsEditable() const
654 return mEditModeActive;
657 void TextInput::SetEditOnTouch( bool editOnTouch )
659 mEditOnTouch = editOnTouch;
662 bool TextInput::IsEditOnTouch() const
667 void TextInput::SetTextSelectable( bool textSelectable )
669 mTextSelection = textSelectable;
672 bool TextInput::IsTextSelectable() const
674 return mTextSelection;
677 bool TextInput::IsTextSelected() const
679 return mHighlightMeshActor;
682 void TextInput::DeSelectText()
689 void TextInput::SetGrabHandleImage(Dali::Image image )
693 CreateGrabHandle(image);
697 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
699 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
703 mCursor.SetImage( image );
704 mCursor.SetNinePatchBorder( border );
708 Vector3 TextInput::GetSelectionHandleSize()
710 return DEFAULT_SELECTION_HANDLE_SIZE;
713 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
715 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
719 mCursorRTL.SetImage( image);
720 mCursorRTL.SetNinePatchBorder( border );
724 void TextInput::EnableGrabHandle(bool toggle)
726 // enables grab handle with will in turn de-activate magnifier
727 mGrabHandleEnabled = toggle;
730 bool TextInput::IsGrabHandleEnabled()
732 // if false then magnifier will be shown instead.
733 return mGrabHandleEnabled;
736 void TextInput::EnableSelectionHandleFlip( bool toggle )
738 // Deprecated function. To be removed.
739 mIsSelectionHandleFlipEnabled = toggle;
742 bool TextInput::IsSelectionHandleFlipEnabled()
744 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
748 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
750 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
751 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
752 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
754 mSelectionHandleFlipMargin = margin;
757 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
759 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
760 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
762 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
763 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
765 const Vector4 boundary( originX,
767 originX + boundingRectangle.width,
768 originY + boundingRectangle.height );
770 mBoundingRectangleWorldCoordinates = boundary;
773 const Rect<float> TextInput::GetBoundingRectangle() const
775 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
777 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
778 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
780 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
785 const Vector4& TextInput::GetSelectionHandleFlipMargin()
787 return mSelectionHandleFlipMargin;
790 void TextInput::SetTextColor( const Vector4& color )
792 mDisplayedTextView.SetColor( color );
795 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
797 if( style != mInputStyle )
800 bool emitSignal = false;
802 // mask: modify style according to mask, if different emit signal.
803 const TextStyle oldInputStyle( mInputStyle );
805 // Copy the new style.
806 mInputStyle.Copy( style, mask );
808 // if style has changed, emit signal.
809 if( oldInputStyle != mInputStyle )
814 // Updates the line height accordingly with the input style.
817 // Changing font point size will require the cursor to be re-sized
822 EmitStyleChangedSignal();
827 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
829 if ( IsTextSelected() )
831 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
832 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
834 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
836 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
839 // Keeps the old style to be compared with the new one.
840 const TextStyle oldInputStyle( mInputStyle );
842 // Copy only those parameters from the style which are set in the mask.
843 mInputStyle.Copy( style, mask );
845 if( mInputStyle != oldInputStyle )
847 // Updates the line height accordingly with the input style.
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
857 if( !mStyledText.empty() )
859 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
863 TextStyle TextInput::GetStyleAtCursor() const
867 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
869 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
870 style = mStyledText.at( mCursorPosition-1 ).mStyle;
876 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
878 Dali::Font defaultFont = Dali::Font::New();
879 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
886 TextStyle TextInput::GetStyleAt( std::size_t position ) const
888 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
890 if( position >= mStyledText.size() )
892 position = mStyledText.size() - 1;
895 return mStyledText.at( position ).mStyle;
898 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
900 mDisplayedTextView.SetTextAlignment( align );
901 mOverrideAutomaticAlignment = true;
904 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
906 mDisplayedTextView.SetLineJustification( justification );
907 mOverrideAutomaticAlignment = true;
910 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
912 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
915 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
917 return mDisplayedTextView.GetFadeBoundary();
920 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
922 return mDisplayedTextView.GetTextAlignment();
925 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
927 mDisplayedTextView.SetMultilinePolicy( policy );
930 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
932 return mDisplayedTextView.GetMultilinePolicy();
935 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
937 mDisplayedTextView.SetWidthExceedPolicy( policy );
940 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
942 return mDisplayedTextView.GetWidthExceedPolicy();
945 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
947 mDisplayedTextView.SetHeightExceedPolicy( policy );
950 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
952 return mDisplayedTextView.GetHeightExceedPolicy();
955 void TextInput::SetExceedEnabled( bool enable )
957 mExceedEnabled = enable;
960 bool TextInput::GetExceedEnabled() const
962 return mExceedEnabled;
965 void TextInput::SetBackground(Dali::Image image )
967 // TODO Should add this function and add public api to match.
970 bool TextInput::OnTouchEvent(const TouchEvent& event)
975 bool TextInput::OnKeyEvent(const KeyEvent& event)
977 switch( event.state )
981 return OnKeyDownEvent(event);
987 return OnKeyUpEvent(event);
999 void TextInput::OnKeyInputFocusGained()
1001 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1003 mEditModeActive = true;
1005 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1007 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1009 // Updates the line height accordingly with the input style.
1012 // Connect the signals to use in text input.
1013 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1014 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1016 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1019 GetTextLayoutInfo();
1022 SetCursorVisibility( true );
1023 StartCursorBlinkTimer();
1025 Toolkit::TextInput handle( GetOwner() );
1026 mInputStartedSignalV2.Emit( handle );
1028 ImfManager imfManager = ImfManager::Get();
1032 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1034 // Notify that the text editing start.
1035 imfManager.Activate();
1037 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1038 imfManager.SetRestoreAferFocusLost( true );
1040 imfManager.SetCursorPosition( mCursorPosition );
1041 imfManager.NotifyCursorPosition();
1044 mClipboard = Clipboard::Get(); // Store handle to clipboard
1046 // Now in edit mode we can accept string to paste from clipboard
1047 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1050 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1054 void TextInput::OnKeyInputFocusLost()
1056 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1060 // If key input focus is lost, it removes the
1061 // underline from the last pre-edit text.
1062 RemovePreEditStyle();
1063 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1064 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1068 ImfManager imfManager = ImfManager::Get();
1071 // The text editing is finished. Therefore the imf manager don't have restore activation.
1072 imfManager.SetRestoreAferFocusLost( false );
1074 // Notify that the text editing finish.
1075 imfManager.Deactivate();
1077 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1079 // Disconnect signal used the text input.
1080 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1082 Toolkit::TextInput handle( GetOwner() );
1083 mInputFinishedSignalV2.Emit( handle );
1084 mEditModeActive = false;
1085 mPreEditFlag = false;
1087 SetCursorVisibility( false );
1088 StopCursorBlinkTimer();
1090 ShowGrabHandleAndSetVisibility( false );
1093 // No longer in edit mode so do not want to receive string from clipboard
1094 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1097 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1100 Clipboard clipboard = Clipboard::Get();
1103 clipboard.HideClipboard();
1107 void TextInput::OnControlStageConnection()
1109 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1111 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1113 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1117 void TextInput::CreateActiveLayer()
1119 Actor self = Self();
1120 mActiveLayer = Layer::New();
1121 mActiveLayer.SetName ( "ActiveLayerActor" );
1123 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1124 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1125 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1127 self.Add( mActiveLayer );
1128 mActiveLayer.RaiseToTop();
1131 void TextInput::OnInitialize()
1133 CreateTextViewActor();
1137 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1138 // different positions depending on language)
1139 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1140 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1142 Actor self = Self();
1143 self.Add( mCursor );
1144 self.Add( mCursorRTL );
1146 mCursorVisibility = false;
1148 CreateActiveLayer(); // todo move this so layer only created when needed.
1150 // Assign names to image actors
1151 mCursor.SetName("mainCursor");
1152 mCursorRTL.SetName("rtlCursor");
1155 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1157 mDisplayedTextView.SetSize( targetSize );
1158 GetTextLayoutInfo();
1159 mActiveLayer.SetSize(targetSize);
1162 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1164 Relayout( mDisplayedTextView, size, container );
1165 Relayout( mPopupPanel.GetRootActor(), size, container );
1167 GetTextLayoutInfo();
1172 Vector3 TextInput::GetNaturalSize()
1174 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1176 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1178 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1179 naturalSize.height = mLineHeight;
1185 float TextInput::GetHeightForWidth( float width )
1187 float height = mDisplayedTextView.GetHeightForWidth( width );
1189 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1191 // If the height is zero, it means there is no text. Let's return the cursor height.
1192 height = mLineHeight;
1198 /*end of Virtual methods from parent*/
1200 // Private Internal methods
1202 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1204 switch (gesture.state)
1206 case Gesture::Started:
1207 // fall through so code not duplicated
1208 case Gesture::Continuing:
1210 if (actor == mGrabArea)
1212 SetCursorVisibility( true );
1213 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1214 MoveGrabHandle( gesture.displacement );
1215 HidePopup(); // Do not show popup whilst handle is moving
1217 else if (actor == mHandleOneGrabArea)
1219 // the displacement in PanGesture is affected by the actor's rotation.
1220 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1221 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1223 MoveSelectionHandle( HandleOne, gesture.displacement );
1225 mState = StateDraggingHandle;
1228 else if (actor == mHandleTwoGrabArea)
1230 // the displacement in PanGesture is affected by the actor's rotation.
1231 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1232 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1234 MoveSelectionHandle( HandleTwo, gesture.displacement );
1236 mState = StateDraggingHandle;
1242 case Gesture::Finished:
1244 // Revert back to non-pressed selection handle images
1245 if (actor == mGrabArea)
1247 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1248 SetCursorVisibility( true );
1249 SetUpPopupSelection();
1252 if (actor == mHandleOneGrabArea)
1254 // the displacement in PanGesture is affected by the actor's rotation.
1255 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1256 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1258 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1260 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1262 ShowPopupCutCopyPaste();
1264 if (actor == mHandleTwoGrabArea)
1266 // the displacement in PanGesture is affected by the actor's rotation.
1267 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1268 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1270 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1272 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1274 ShowPopupCutCopyPaste();
1283 // Stop the flashing animation so easy to see when moved.
1284 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1286 if (touch.GetPoint(0).state == TouchPoint::Down)
1288 SetCursorVisibility( true );
1289 StopCursorBlinkTimer();
1291 else if (touch.GetPoint(0).state == TouchPoint::Up)
1293 SetCursorVisibility( true );
1294 StartCursorBlinkTimer();
1299 // selection handle one
1300 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1302 if (touch.GetPoint(0).state == TouchPoint::Down)
1304 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1306 else if (touch.GetPoint(0).state == TouchPoint::Up)
1308 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1313 // selection handle two
1314 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1316 if (touch.GetPoint(0).state == TouchPoint::Down)
1318 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1320 else if (touch.GetPoint(0).state == TouchPoint::Up)
1322 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1327 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1329 // If text exists then select nearest word.
1330 if ( !mStyledText.empty())
1334 ShowGrabHandleAndSetVisibility( false );
1339 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1340 // converts the pre-edit word being displayed to a committed word.
1341 if ( !mUnderlinedPriorToPreEdit )
1344 style.SetUnderline( false );
1345 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1347 mPreEditFlag = false;
1348 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1349 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1350 PreEditReset( false );
1352 mCursorPosition = 0;
1354 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1355 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1357 std::size_t start = 0;
1358 std::size_t end = 0;
1359 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1361 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1363 ImfManager imfManager = ImfManager::Get();
1366 imfManager.SetCursorPosition ( mCursorPosition );
1367 imfManager.NotifyCursorPosition();
1370 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1372 SelectText( start, end );
1373 ShowPopupCutCopyPaste();
1377 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1378 HidePopup( false ); // Hide popup with setting to do auto show.
1379 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1383 else if ( mClipboard && mClipboard.NumberOfItems() )
1385 ShowPopupCutCopyPaste();
1388 // If no text and clipboard empty then do nothing
1391 // TODO: Change the function name to be more general.
1392 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1394 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1395 , (mEditOnTouch)?"true":"false"
1396 , (mEditModeActive)?"true":"false");
1398 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1403 if( mGrabArea == actor )
1405 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1407 SetUpPopupSelection();
1417 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1419 // Initially don't create the grab handle.
1420 bool createGrabHandle = false;
1422 if ( !mEditModeActive )
1424 // update line height before calculate the actual position.
1427 // Only start edit mode if TextInput configured to edit on touch
1430 // Set the initial cursor position in the tap point.
1431 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1437 // Show the keyboard if it was hidden.
1438 if (!VirtualKeyboard::IsVisible())
1440 VirtualKeyboard::Show();
1443 // Reset keyboard as tap event has occurred.
1444 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1445 PreEditReset( true );
1447 GetTextLayoutInfo();
1449 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1451 // 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.
1453 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1455 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1457 // Notify keyboard so it can 're-capture' word for predictive text.
1458 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1459 ImfManager imfManager = ImfManager::Get();
1462 imfManager.SetCursorPosition ( mCursorPosition );
1463 imfManager.NotifyCursorPosition();
1465 const TextStyle oldInputStyle( mInputStyle );
1467 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1471 // Create the grab handle.
1472 // Grab handle is created later.
1473 createGrabHandle = true;
1475 if( oldInputStyle != mInputStyle )
1477 // Updates the line height accordingly with the input style.
1480 EmitStyleChangedSignal();
1485 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1486 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1487 // otherwise the Grab handle will be shown when selecting.
1488 if ( createGrabHandle && IsGrabHandleEnabled() )
1490 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1491 bool altPositionValid; // Alternate cursor validity flag.
1492 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1493 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1495 if( altPositionValid )
1497 // Check which of the positions is the closest.
1498 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1500 cursorPosition = altPosition;
1506 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1507 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1508 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1509 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1514 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1516 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1518 // Ignore longpress if in selection mode already
1519 if( mHighlightMeshActor )
1524 if(longPress.state == Dali::Gesture::Started)
1526 // Start edit mode on long press
1527 if ( !mEditModeActive )
1532 // If text exists then select nearest word.
1533 if ( !mStyledText.empty())
1537 ShowGrabHandleAndSetVisibility( false );
1542 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1543 // converts the pre-edit word being displayed to a committed word.
1544 if ( !mUnderlinedPriorToPreEdit )
1547 style.SetUnderline( false );
1548 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1550 mPreEditFlag = false;
1551 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1552 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1553 PreEditReset( false );
1555 mCursorPosition = 0;
1557 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1558 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1560 std::size_t start = 0;
1561 std::size_t end = 0;
1562 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1564 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1566 ImfManager imfManager = ImfManager::Get();
1569 imfManager.SetCursorPosition ( mCursorPosition );
1570 imfManager.NotifyCursorPosition();
1573 SelectText( start, end );
1576 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1577 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1579 ShowPopupCutCopyPaste();
1584 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1586 const Text clipboardText( notifier.GetContent() );
1587 PasteText( clipboardText );
1589 SetCursorVisibility( true );
1590 StartCursorBlinkTimer();
1592 ShowGrabHandleAndSetVisibility( false );
1598 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1600 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1602 const std::string& name = button.GetName();
1604 if(name == TextInputPopup::OPTION_SELECT_WORD)
1606 std::size_t start = 0;
1607 std::size_t end = 0;
1608 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1610 SelectText( start, end );
1612 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1614 SetCursorVisibility(false);
1615 StopCursorBlinkTimer();
1617 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1618 std::size_t start = 0;
1620 SelectText( start, end );
1622 else if(name == TextInputPopup::OPTION_CUT)
1624 bool ret = CopySelectedTextToClipboard();
1628 DeleteHighlightedText( true );
1632 SetCursorVisibility( true );
1633 StartCursorBlinkTimer();
1637 else if(name == TextInputPopup::OPTION_COPY)
1639 CopySelectedTextToClipboard();
1643 SetCursorVisibility( true );
1644 StartCursorBlinkTimer();
1648 else if(name == TextInputPopup::OPTION_PASTE)
1650 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1652 PasteText(retrievedString);
1654 SetCursorVisibility( true );
1655 StartCursorBlinkTimer();
1657 ShowGrabHandleAndSetVisibility( false );
1661 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1663 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1664 // Hence pass the false parameter for signalFinished.
1665 HidePopup( true, false );
1666 mClipboard.ShowClipboard();
1672 bool TextInput::OnCursorBlinkTimerTick()
1675 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1676 if ( mCursorRTLEnabled )
1678 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1680 mCursorBlinkStatus = !mCursorBlinkStatus;
1685 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1687 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1689 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1690 if(mHighlightMeshActor && mState == StateEdit)
1692 ShowPopupCutCopyPaste();
1696 //FIXME this routine needs to be re-written as it contains too many branches.
1697 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1699 std::string keyName = event.keyPressedName;
1700 std::string keyString = event.keyPressed;
1702 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1704 // Do not consume "Tab" and "Escape" keys.
1705 if(keyName == "Tab" || keyName == "Escape")
1707 // Escape key to end the edit mode
1713 HidePopup(); // If Pop-up shown then hides it as editing text.
1715 // Update Flag, indicates whether to update the text-input contents or not.
1716 // Any key stroke that results in a visual change of the text-input should
1717 // set this flag to true.
1720 // Whether to scroll text to cursor position.
1721 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1722 bool scroll = false;
1724 if (keyName == "Return")
1726 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1728 bool preEditFlagPreviouslySet( mPreEditFlag );
1730 // replaces highlighted text with new line
1731 DeleteHighlightedText( false );
1733 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1735 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1736 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1739 mCommitByKeyInput = true;
1742 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1743 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1745 mPreEditFlag = true;
1746 mIgnoreCommitFlag = false;
1756 else if ( keyName == "space" )
1758 if ( mHighlightMeshActor )
1760 // Some text is selected so erase it before adding space.
1761 DeleteHighlightedText( true );
1765 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1767 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1768 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1771 mCommitByKeyInput = true;
1776 else if (keyName == "BackSpace")
1778 if ( mHighlightMeshActor )
1780 // Some text is selected so erase it
1781 DeleteHighlightedText( true );
1786 if ( mCursorPosition > 0 )
1788 DeleteCharacter( mCursorPosition );
1794 else if (keyName == "Right")
1799 else if (keyName == "Left")
1801 AdvanceCursor(true);
1804 else // event is a character
1806 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1807 if ( !keyString.empty() )
1809 // replaces highlighted text with new character
1810 DeleteHighlightedText( false );
1812 // Received key String
1813 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1819 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1820 // as this is a costly operation.
1826 if(update || scroll)
1828 if( IsScrollEnabled() )
1830 // Calculates the new cursor position (in actor coordinates)
1831 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1833 ScrollTextViewToMakeCursorVisible( cursorPosition );
1840 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1842 std::string keyName = event.keyPressedName;
1843 std::string keyString = event.keyPressed;
1845 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1847 // The selected text become deselected when the key code is DALI_KEY_BACK.
1848 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1857 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1858 const Vector3& cursorPositionTwo,
1859 bool altPositionValidOne,
1860 bool altPositionValidTwo,
1861 const Vector3& altPositionOne,
1862 const Vector3& altPositionTwo )
1864 // TODO VCC Valid for one line.
1865 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1866 if( cursorPositionOne != cursorPositionTwo )
1868 if( cursorPositionOne.x < cursorPositionTwo.x )
1870 mSelectionHandleOneActualPosition = cursorPositionOne;
1871 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1875 mSelectionHandleOneActualPosition = cursorPositionTwo;
1876 mSelectionHandleTwoActualPosition = cursorPositionOne;
1881 mSelectionHandleOneActualPosition = cursorPositionOne;
1882 if( altPositionValidOne )
1884 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1886 mSelectionHandleOneActualPosition = altPositionOne;
1889 if( altPositionValidTwo )
1891 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1893 mSelectionHandleOneActualPosition = altPositionTwo;
1897 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1898 if( altPositionValidTwo )
1900 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1902 mSelectionHandleTwoActualPosition = altPositionTwo;
1905 if( altPositionValidOne )
1907 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1909 mSelectionHandleTwoActualPosition = altPositionOne;
1915 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1917 // Updates the stored scroll position.
1918 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1920 const Vector3& controlSize = GetControlSize();
1921 Size cursorSize( CURSOR_THICKNESS, 0.f );
1923 // Updates the cursor and grab handle position and visibility.
1924 if( mGrabHandle || mCursor )
1926 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1928 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1929 bool altPositionValid; // Alternate cursor validity flag.
1930 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1931 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1933 if( altPositionValid )
1935 // Check which of the positions is the closest.
1936 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1938 cursorPosition = altPosition;
1942 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1944 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1948 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1949 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1954 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1955 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1959 // Updates the selection handles and highlighted text position and visibility.
1960 if( mSelectionHandleOne && mSelectionHandleTwo )
1962 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1963 bool altPositionValidOne; // Alternate cursor validity flag.
1964 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1965 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1967 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1968 bool altPositionValidTwo; // Alternate cursor validity flag.
1969 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1970 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1972 // VCC TODO: This method is a hack for one line.
1973 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1975 altPositionValidOne,
1976 altPositionValidTwo,
1980 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1981 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1982 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1983 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1985 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1986 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1987 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1988 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1990 if( mHighlightMeshActor )
1992 mHighlightMeshActor.SetVisible( true );
1998 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2000 // Scroll the text to make the cursor visible.
2001 const Size cursorSize( CURSOR_THICKNESS,
2002 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2004 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2006 const Vector3& controlSize = GetControlSize();
2008 // Calculates the new scroll position.
2009 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2010 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2012 scrollOffset.x += cursorPosition.x;
2015 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2017 scrollOffset.y += cursorPosition.y;
2020 // Sets the new scroll position.
2021 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2022 SetScrollPosition( scrollOffset );
2025 void TextInput::StartScrollTimer()
2029 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2030 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2033 if( !mScrollTimer.IsRunning() )
2035 mScrollTimer.Start();
2039 void TextInput::StopScrollTimer()
2043 mScrollTimer.Stop();
2047 bool TextInput::OnScrollTimerTick()
2049 // TODO: need to set the new style accordingly the new handle position.
2051 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2053 // nothing to do if all handles are invisible or doesn't exist.
2059 // Choose between the grab handle or the selection handles.
2060 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2061 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2062 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2064 std::size_t newCursorPosition = 0;
2065 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2067 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2068 // the new selection handle's position needs to be different of the other one.
2069 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2070 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2071 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2073 if( differentSelectionHandles )
2075 handlePosition = newCursorPosition;
2077 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2079 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2081 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2082 scrollPosition += scrollDelta;
2083 SetScrollPosition( scrollPosition );
2085 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2090 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2093 actualHandlePosition.x += mScrollDisplacement.x;
2094 actualHandlePosition.y += mScrollDisplacement.y;
2099 // Public Internal Methods (public for testing purpose)
2101 void TextInput::SetUpTouchEvents()
2103 if ( !mTapDetector )
2105 mTapDetector = TapGestureDetector::New();
2106 // Attach the actors and connect the signal
2107 mTapDetector.Attach(Self());
2109 // As contains children which may register for tap the default control detector is not used.
2110 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2113 if ( !mDoubleTapDetector )
2115 mDoubleTapDetector = TapGestureDetector::New();
2116 mDoubleTapDetector.SetTapsRequired( 2 );
2117 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2119 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2120 // so that we do not, unnecessarily, have a double tap request all the time
2123 if ( !mPanGestureDetector )
2125 mPanGestureDetector = PanGestureDetector::New();
2126 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2129 if ( !mLongPressDetector )
2131 mLongPressDetector = LongPressGestureDetector::New();
2132 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2133 mLongPressDetector.Attach(Self());
2137 void TextInput::CreateTextViewActor()
2139 mDisplayedTextView = Toolkit::TextView::New();
2140 mDisplayedTextView.SetName( "DisplayedTextView ");
2141 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2142 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2143 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2144 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2145 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2146 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2147 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2148 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2149 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2150 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2152 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2154 Self().Add( mDisplayedTextView );
2157 // Start a timer to initiate, used by the cursor to blink.
2158 void TextInput::StartCursorBlinkTimer()
2160 if ( !mCursorBlinkTimer )
2162 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2163 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2166 if ( !mCursorBlinkTimer.IsRunning() )
2168 mCursorBlinkTimer.Start();
2172 // Start a timer to initiate, used by the cursor to blink.
2173 void TextInput::StopCursorBlinkTimer()
2175 if ( mCursorBlinkTimer )
2177 mCursorBlinkTimer.Stop();
2181 void TextInput::StartEditMode()
2183 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2185 if(!mEditModeActive)
2190 if ( mDoubleTapDetector )
2192 mDoubleTapDetector.Attach( Self() );
2196 void TextInput::EndEditMode()
2198 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2200 ClearKeyInputFocus();
2202 if ( mDoubleTapDetector )
2204 mDoubleTapDetector.Detach( Self() );
2208 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2210 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2212 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2214 style.SetUnderline( true );
2215 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2219 void TextInput::RemovePreEditStyle()
2221 if ( !mUnderlinedPriorToPreEdit )
2224 style.SetUnderline( false );
2225 SetActiveStyle( style, TextStyle::UNDERLINE );
2229 // IMF related methods
2232 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2234 bool update( false );
2235 bool preeditResetRequired ( false );
2237 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2239 HidePopup(); // If Pop-up shown then hides it as editing text.
2242 switch ( imfEvent.eventName )
2244 case ImfManager::PREEDIT:
2246 mIgnoreFirstCommitFlag = false;
2248 // 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
2249 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2251 // replaces highlighted text with new character
2252 DeleteHighlightedText( false );
2255 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2257 if( IsScrollEnabled() )
2259 // Calculates the new cursor position (in actor coordinates)
2260 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2261 ScrollTextViewToMakeCursorVisible( cursorPosition );
2268 case ImfManager::COMMIT:
2270 if( mIgnoreFirstCommitFlag )
2272 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2273 mIgnoreFirstCommitFlag = false;
2277 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2279 // 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
2280 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2282 // replaces highlighted text with new character
2283 DeleteHighlightedText( false );
2286 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2287 // not needed, one such scenario is when the pre-edit word is too long to fit.
2288 if ( !mIgnoreCommitFlag )
2290 update = CommitReceived( imfEvent.predictiveString );
2294 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2300 if( IsScrollEnabled() )
2302 // Calculates the new cursor position (in actor coordinates)
2303 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2305 ScrollTextViewToMakeCursorVisible( cursorPosition );
2310 case ImfManager::DELETESURROUNDING:
2312 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2313 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2315 mPreEditFlag = false;
2317 std::size_t toDelete = 0;
2318 std::size_t numberOfCharacters = 0;
2320 if( mHighlightMeshActor )
2322 // delete highlighted text.
2323 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2324 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2328 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2330 toDelete = mCursorPosition + imfEvent.cursorOffset;
2332 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2334 numberOfCharacters = mStyledText.size() - toDelete;
2338 numberOfCharacters = imfEvent.numberOfChars;
2341 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2342 DeleteRange( toDelete, numberOfCharacters );
2344 mCursorPosition = toDelete;
2345 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2349 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2352 case ImfManager::GETSURROUNDING:
2354 // 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
2355 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2356 if (! ( mHighlightMeshActor || mSelectingText ) )
2358 std::string text( GetText() );
2359 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2361 imfManager.SetCursorPosition( mCursorPosition );
2362 imfManager.SetSurroundingText( text );
2365 if( 0 != mNumberOfSurroundingCharactersDeleted )
2367 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2368 mNumberOfSurroundingCharactersDeleted = 0;
2370 if( mStyledText.empty() )
2372 ShowPlaceholderText( mStyledPlaceHolderText );
2377 case ImfManager::VOID:
2379 DALI_ASSERT_DEBUG( false );
2383 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2385 return callbackData;
2388 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2390 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2392 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2393 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2395 bool preeditResetRequest ( false );
2397 if( mPreEditFlag ) // Already in pre-edit state.
2399 if( mStyledText.size() >= mMaxStringLength )
2401 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2402 // Cannot fit these characters into field, clear pre-edit.
2403 if ( !mUnderlinedPriorToPreEdit )
2406 style.SetUnderline( false );
2407 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2409 mIgnoreCommitFlag = true;
2410 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2411 mPreEditFlag = false;
2412 EmitMaxInputCharactersReachedSignal();
2416 // delete existing pre-edit string
2417 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2419 // Store new pre-edit string
2420 mPreEditString.SetText( keyString );
2422 if ( keyString.empty() )
2424 mPreEditFlag = false;
2425 mCursorPosition = mPreEditStartPosition;
2427 if( mStyledText.empty() )
2429 ShowPlaceholderText( mStyledPlaceHolderText );
2433 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2436 GetTextLayoutInfo();
2441 // Insert new pre-edit string. InsertAt updates the size and position table.
2442 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2443 // 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.
2444 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2445 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2446 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2449 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2453 else // mPreEditFlag not set
2455 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2457 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2458 // new pre-edit so move into pre-edit state by setting flag
2459 mPreEditFlag = true;
2460 mPreEditString.SetText( keyString ); // store new pre-edit string
2461 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2462 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2463 // 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.
2464 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2465 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2466 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2467 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2473 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2477 return preeditResetRequest;
2480 bool TextInput::CommitReceived(const std::string& keyString )
2482 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2483 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2485 bool update( false );
2487 RemovePreEditStyle();
2489 const std::size_t styledTextSize( mStyledText.size() );
2490 if( styledTextSize >= mMaxStringLength )
2492 // Cannot fit these characters into field, clear pre-edit.
2495 mIgnoreCommitFlag = true;
2496 mPreEditFlag = false;
2498 EmitMaxInputCharactersReachedSignal();
2504 // delete existing pre-edit string
2505 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2506 mPreEditFlag = false;
2508 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2509 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2511 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2513 // No need to update cursor position as Cursor location given by touch.
2514 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2515 mPreserveCursorPosition = false;
2519 // Cursor not set by touch so needs to be re-positioned to input more text
2520 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2522 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2523 if ( mCommitByKeyInput )
2525 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2526 mCommitByKeyInput = false;
2532 if ( mSelectTextOnCommit )
2534 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2539 else // mPreEditFlag not set
2541 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2543 if( mStyledText.empty() && mPlaceHolderSet )
2545 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2546 mDisplayedTextView.SetText( "" );
2547 mNumberOfSurroundingCharactersDeleted = 0;
2548 mPlaceHolderSet = false;
2550 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2552 mNumberOfSurroundingCharactersDeleted = 0;
2557 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2562 mSelectTextOnCommit = false;
2564 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2565 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2570 // End of IMF related methods
2572 std::size_t TextInput::DeletePreEdit()
2574 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2576 DALI_ASSERT_DEBUG( mPreEditFlag );
2578 const std::size_t preEditStringLength = mPreEditString.GetLength();
2579 const std::size_t styledTextSize = mStyledText.size();
2581 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2583 // Prevents erase items outside mStyledText bounds.
2584 if( mPreEditStartPosition > styledTextSize )
2586 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2587 mPreEditStartPosition = styledTextSize;
2590 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2592 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2593 endPosition = styledTextSize;
2596 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2598 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2599 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2601 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2603 return preEditStringLength;
2606 void TextInput::PreEditReset( bool preserveCursorPosition )
2608 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2609 preserveCursorPosition, mCursorPosition);
2611 // 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.
2612 mPreserveCursorPosition = preserveCursorPosition;
2614 // Reset incase we are in a pre-edit state.
2615 ImfManager imfManager = ImfManager::Get();
2618 imfManager.Reset(); // Will trigger a commit message
2622 void TextInput::CursorUpdate()
2626 ImfManager imfManager = ImfManager::Get();
2629 std::string text( GetText() );
2630 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2631 imfManager.SetCursorPosition ( mCursorPosition );
2632 imfManager.NotifyCursorPosition();
2636 /* Delete highlighted characters redisplay*/
2637 void TextInput::DeleteHighlightedText( bool inheritStyle )
2639 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2641 if( mHighlightMeshActor )
2643 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2645 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2646 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2648 // Get the styled text of the characters to be deleted as it may be needed if
2649 // the "exceed the text-input's boundaries" option is disabled.
2650 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2652 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2654 mStyledText.erase( start, end ); // erase range of characters
2656 // Remove text from TextView and update place holder text if required
2658 // Set the placeholder text only if the styled text is empty.
2659 if( mStyledText.empty() )
2661 ShowPlaceholderText( mStyledPlaceHolderText );
2665 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2667 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2669 // It may happen than after removing a white space or a new line character,
2670 // two words merge, this new word could be big enough to not fit in its
2671 // current line, so moved to the next one, and make some part of the text to
2672 // exceed the text-input's boundary.
2673 if( !mExceedEnabled )
2675 // Get the new text layout after removing some characters.
2676 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2678 // Get text-input's size.
2679 const Vector3& size = GetControlSize();
2681 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2682 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2684 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2686 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2687 styledCharactersToDelete.begin(),
2688 styledCharactersToDelete.end() );
2692 GetTextLayoutInfo();
2700 const TextStyle oldInputStyle( mInputStyle );
2702 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2704 if( oldInputStyle != mInputStyle )
2706 // Updates the line height accordingly with the input style.
2709 EmitStyleChangedSignal();
2715 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2717 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2718 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2720 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2723 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2725 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2726 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2728 mStyledText.erase(itStart, itEnd);
2730 // update the selection handles if they are visible.
2731 if( mHighlightMeshActor )
2733 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2734 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2736 if( minHandle >= start + ncharacters )
2738 minHandle -= ncharacters;
2740 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2745 if( maxHandle >= start + ncharacters )
2747 maxHandle -= ncharacters;
2749 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2755 // 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.
2758 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2760 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2761 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2762 // Mean we do not re-draw the text more than we have too.
2765 /* Delete character at current cursor position and redisplay*/
2766 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2768 // Ensure positionToDelete is not out of bounds.
2769 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2770 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2771 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2773 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2776 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2778 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2780 // Get the styled text of the character to be deleted as it may be needed if
2781 // the "exceed the text-input's boundaries" option is disabled.
2782 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2784 mStyledText.erase(it); // erase the character left of positionToDelete
2786 if( mStyledText.empty() )
2788 ShowPlaceholderText( mStyledPlaceHolderText );
2792 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2794 const Character characterToDelete = styledCharacterToDelete.mText[0];
2796 // It may happen than after removing a white space or a new line character,
2797 // two words merge, this new word could be big enough to not fit in its
2798 // current line, so moved to the next one, and make some part of the text to
2799 // exceed the text-input's boundary.
2800 if( !mExceedEnabled )
2802 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2804 // Get the new text layout after removing one character.
2805 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2807 // Get text-input's size.
2808 const Vector3& size = GetControlSize();
2810 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2811 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2813 MarkupProcessor::StyledTextArray array;
2814 array.push_back( styledCharacterToDelete );
2815 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2817 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2822 GetTextLayoutInfo();
2824 ShowGrabHandleAndSetVisibility( false );
2826 mCursorPosition = positionToDelete -1;
2828 const TextStyle oldInputStyle( mInputStyle );
2830 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2832 if( oldInputStyle != mInputStyle )
2834 // Updates the line height accordingly with the input style.
2837 EmitStyleChangedSignal();
2842 /*Insert new character into the string and (optionally) redisplay text-input*/
2843 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2845 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2847 // Ensure insertionPosition is not out of bounds.
2848 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2850 bool textExceedsMaximunNumberOfCharacters = false;
2851 bool textExceedsBoundary = false;
2852 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2854 ShowGrabHandleAndSetVisibility( false );
2856 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2860 mIgnoreCommitFlag = true;
2861 mPreEditFlag = false;
2862 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2863 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2866 if( textExceedsMaximunNumberOfCharacters )
2868 EmitMaxInputCharactersReachedSignal();
2871 if( textExceedsBoundary )
2873 EmitInputTextExceedsBoundariesSignal();
2874 PreEditReset( false );
2878 return insertedStringLength;
2881 ImageActor TextInput::CreateCursor( const Vector4& color)
2884 cursor = CreateSolidColorActor(color);
2885 cursor.SetName( "Cursor" );
2887 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2888 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2889 cursor.SetVisible(false);
2894 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2896 // As cursor is not moving due to grab handle, handle should be hidden.
2897 ShowGrabHandleAndSetVisibility( false );
2899 bool cursorPositionChanged = false;
2902 if ( mCursorPosition >= places )
2904 mCursorPosition = mCursorPosition - places;
2905 cursorPositionChanged = true;
2910 if ((mCursorPosition + places) <= mStyledText.size())
2912 mCursorPosition = mCursorPosition + places;
2913 cursorPositionChanged = true;
2917 if( cursorPositionChanged )
2919 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2921 const TextStyle oldInputStyle( mInputStyle );
2922 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2926 if( oldInputStyle != mInputStyle )
2928 // Updates the line height accordingly with the input style.
2931 EmitStyleChangedSignal();
2934 ImfManager imfManager = ImfManager::Get();
2937 imfManager.SetCursorPosition ( mCursorPosition );
2938 imfManager.NotifyCursorPosition();
2943 void TextInput::DrawCursor()
2945 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2947 // Get height of cursor and set its size
2948 Size size( CURSOR_THICKNESS, 0.0f );
2949 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2951 size.height = rowRect.height;
2955 // Measure Font so know how big text will be if no initial text to measure.
2956 size.height = mLineHeight;
2959 mCursor.SetSize(size);
2961 // If the character is italic then the cursor also tilts.
2962 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2964 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2966 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2968 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2969 bool altPositionValid; // Alternate cursor validity flag.
2970 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2971 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2973 SetAltCursorEnabled( altPositionValid );
2975 if( !altPositionValid )
2977 mCursor.SetPosition( position + UI_OFFSET );
2981 size.height *= 0.5f;
2982 mCursor.SetSize(size);
2983 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2985 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2986 size.height = rowRect.height * 0.5f;
2987 mCursorRTL.SetSize(size);
2988 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2991 if( IsScrollEnabled() )
2993 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2994 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2999 void TextInput::SetAltCursorEnabled( bool enabled )
3001 mCursorRTLEnabled = enabled;
3002 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3005 void TextInput::SetCursorVisibility( bool visible )
3007 mCursorVisibility = visible;
3008 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3009 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3012 void TextInput::CreateGrabHandle( Dali::Image image )
3018 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
3022 mGrabHandleImage = image;
3025 mGrabHandle = ImageActor::New(mGrabHandleImage);
3026 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3027 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3029 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3031 ShowGrabHandleAndSetVisibility( false );
3033 CreateGrabArea( mGrabHandle );
3035 mActiveLayer.Add(mGrabHandle);
3039 void TextInput::CreateGrabArea( Actor& parent )
3041 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3042 mGrabArea.SetName( "GrabArea" );
3043 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3044 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3045 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3046 mTapDetector.Attach( mGrabArea );
3047 mPanGestureDetector.Attach( mGrabArea );
3048 mLongPressDetector.Attach( mGrabArea );
3050 parent.Add(mGrabArea);
3053 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3055 Vector3 actualHandlePosition;
3059 mActualGrabHandlePosition.x += displacement.x;
3060 mActualGrabHandlePosition.y += displacement.y;
3062 // Grab handle should jump to the nearest character and take cursor with it
3063 std::size_t newCursorPosition = 0;
3064 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3066 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3067 bool altPositionValid; // Alternate cursor validity flag.
3068 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3069 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3071 if( altPositionValid )
3073 // Check which of the positions is the closest.
3074 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3076 actualHandlePosition = altPosition;
3080 bool handleVisible = true;
3082 if( IsScrollEnabled() )
3084 const Vector3 controlSize = GetControlSize();
3085 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3086 // Scrolls the text if the handle is not in a visible position
3087 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3094 mCurrentHandlePosition = actualHandlePosition;
3095 mScrollDisplacement = Vector2::ZERO;
3099 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3101 mScrollDisplacement.x = -SCROLL_SPEED;
3103 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3105 mScrollDisplacement.x = SCROLL_SPEED;
3107 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3109 mScrollDisplacement.y = -SCROLL_SPEED;
3111 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3113 mScrollDisplacement.y = SCROLL_SPEED;
3119 if( handleVisible && // Only redraw cursor and do updates if position changed
3120 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3122 mCursorPosition = newCursorPosition;
3124 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3126 const TextStyle oldInputStyle( mInputStyle );
3128 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3130 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3132 if( oldInputStyle != mInputStyle )
3134 // Updates the line height accordingly with the input style.
3137 EmitStyleChangedSignal();
3142 return actualHandlePosition;
3145 void TextInput::ShowGrabHandle( bool visible )
3147 if ( IsGrabHandleEnabled() )
3151 mGrabHandle.SetVisible( mGrabHandleVisibility );
3153 StartMonitoringStageForTouch();
3157 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3159 mGrabHandleVisibility = visible;
3160 ShowGrabHandle( visible );
3163 // Callbacks connected to be Property notifications for Boundary checking.
3165 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3167 mIsSelectionHandleOneFlipped = true;
3168 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3169 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3172 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3174 mIsSelectionHandleOneFlipped = false;
3175 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3176 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3179 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3181 mIsSelectionHandleTwoFlipped = true;
3182 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3183 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3186 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3188 mIsSelectionHandleTwoFlipped = false;
3189 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3190 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3193 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3194 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3196 mSelectionHandleOne.SetOpacity(0.0f);
3199 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3201 mSelectionHandleOne.SetOpacity(1.0f);
3204 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3206 mSelectionHandleTwo.SetOpacity(0.0f);
3209 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3211 mSelectionHandleTwo.SetOpacity(1.0f);
3214 // End of Callbacks connected to be Property notifications for Boundary checking.
3216 void TextInput::SetUpHandlePropertyNotifications()
3218 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3220 Vector3 handlesize = GetSelectionHandleSize();
3222 // Exceeding horizontal boundary
3223 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3224 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3226 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3227 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3229 // Within horizontal boundary
3230 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3231 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3233 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3234 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3236 // Exceeding vertical boundary
3237 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3238 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3239 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3240 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3242 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3243 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3244 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3245 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3247 // Within vertical boundary
3248 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3249 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3250 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3251 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3253 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3254 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3255 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3256 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3259 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3261 mSelectionHandleOnePosition = start;
3262 mSelectionHandleTwoPosition = end;
3264 if ( !mSelectionHandleOne )
3266 // create normal and pressed images
3267 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3268 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3270 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3271 mSelectionHandleOne.SetName("SelectionHandleOne");
3272 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3273 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3274 mIsSelectionHandleOneFlipped = false;
3275 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3277 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3278 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3280 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3281 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3283 mTapDetector.Attach( mHandleOneGrabArea );
3284 mPanGestureDetector.Attach( mHandleOneGrabArea );
3286 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3288 mSelectionHandleOne.Add( mHandleOneGrabArea );
3289 mActiveLayer.Add( mSelectionHandleOne );
3292 if ( !mSelectionHandleTwo )
3294 // create normal and pressed images
3295 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3296 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3298 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3299 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3300 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3301 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3302 mIsSelectionHandleTwoFlipped = false;
3303 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3305 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3306 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3307 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3308 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3310 mTapDetector.Attach( mHandleTwoGrabArea );
3311 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3313 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3315 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3317 mActiveLayer.Add( mSelectionHandleTwo );
3320 SetUpHandlePropertyNotifications();
3322 // update table as text may have changed.
3323 GetTextLayoutInfo();
3325 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3326 bool altPositionValidOne; // Alternate cursor validity flag.
3327 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3328 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3330 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3331 bool altPositionValidTwo; // Alternate cursor validity flag.
3332 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3333 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3335 // VCC TODO: This method is a hack for one line.
3336 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3338 altPositionValidOne,
3339 altPositionValidTwo,
3343 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3344 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3346 // Calculates and set the visibility if the scroll mode is enabled.
3347 bool isSelectionHandleOneVisible = true;
3348 bool isSelectionHandleTwoVisible = true;
3349 if( IsScrollEnabled() )
3351 const Vector3& controlSize( GetControlSize() );
3352 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3353 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3354 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3355 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3358 CreateHighlight(); // function will only create highlight if not already created.
3361 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3363 Vector3 actualHandlePosition;
3365 if ( mSelectionHandleOne && mSelectionHandleTwo )
3367 const Vector3& controlSize = GetControlSize();
3369 Size cursorSize( CURSOR_THICKNESS, 0.f );
3371 // Get a reference of the wanted selection handle (handle one or two).
3372 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3374 // Get a reference for the current position of the handle and a copy of its pair
3375 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3376 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3378 // Get a handle of the selection handle actor
3379 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3381 // Selection handles should jump to the nearest character
3382 std::size_t newHandlePosition = 0;
3383 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3385 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3386 bool altPositionValid; // Alternate cursor validity flag.
3387 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3388 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3389 if( altPositionValid )
3391 // Check which of the positions is the closest.
3392 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3394 actualHandlePosition = altPosition;
3398 bool handleVisible = true;
3400 if( IsScrollEnabled() )
3402 mCurrentSelectionId = handleId;
3404 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3405 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3406 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3413 mCurrentSelectionHandlePosition = actualHandlePosition;
3414 mScrollDisplacement = Vector2::ZERO;
3418 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3420 mScrollDisplacement.x = -SCROLL_SPEED;
3422 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3424 mScrollDisplacement.x = SCROLL_SPEED;
3426 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3428 mScrollDisplacement.y = -SCROLL_SPEED;
3430 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3432 mScrollDisplacement.y = SCROLL_SPEED;
3438 if ( handleVisible && // Ensure the handle is visible.
3439 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3440 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3442 currentSelectionHandlePosition = newHandlePosition;
3444 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3445 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3449 if ( handleId == HandleOne )
3451 const TextStyle oldInputStyle( mInputStyle );
3453 // Set Active Style to that of first character in selection
3454 if( mSelectionHandleOnePosition < mStyledText.size() )
3456 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3459 if( oldInputStyle != mInputStyle )
3461 // Updates the line height accordingly with the input style.
3464 EmitStyleChangedSignal();
3470 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3473 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3475 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3476 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3478 if ( selectionHandleActor )
3480 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3481 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3482 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3484 if( IsScrollEnabled() )
3486 const Size cursorSize( CURSOR_THICKNESS,
3487 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3488 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3490 GetControlSize() ) );
3495 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3497 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3499 // VCC Set true/false in logical order. TODO : It needs to be checked.
3501 if( startSelection > endSelection )
3503 std::swap( startSelection, endSelection );
3505 std::size_t index = 0u;
3506 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3508 if( ( index < startSelection ) || ( endSelection <= index ) )
3519 // Calculate the dimensions of the quads they will make the highlight mesh
3520 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3522 // At the moment there is no public API to modify the block alignment option.
3523 const bool blockAlignEnabled = true;
3525 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3527 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3529 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3530 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3532 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3533 std::vector<bool> selectedVisualText;
3534 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3535 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3536 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3538 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3539 float rowLeft = 0.0f;
3540 float rowRight = 0.0f;
3541 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3542 float maxRowLeft = std::numeric_limits<float>::max();
3543 float maxRowRight = 0.0f;
3545 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3547 // Scan through entire text.
3550 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3552 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3553 bool charSelected = false;
3554 if( selectedIt != selectedEndIt )
3556 charSelected = *selectedIt++;
3559 if( selectionState == SelectionNone )
3563 selectionState = SelectionStarted;
3564 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3565 rowRight = rowLeft + charInfo.mSize.width;
3568 else if( selectionState == SelectionStarted )
3570 // break selection on:
3571 // 1. new line causing selection break. (\n or wordwrap)
3572 // 2. character not selected.
3573 if( !charSelected ||
3574 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3576 // finished selection.
3577 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3578 // that it resides on. That way this enumeration is not necessary.
3580 if(lastIt->mIsNewParagraphChar)
3582 // 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.
3583 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3585 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3586 maxRowLeft = std::min(maxRowLeft, min.x);
3587 maxRowRight = std::max(maxRowRight, max.x);
3588 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3589 float rowTop = rowBottom - rowSize.height;
3591 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3592 if(charSelected && blockAlignEnabled)
3594 rowRight = std::numeric_limits<float>::max();
3596 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3598 selectionState = SelectionNone;
3600 // Still selected? start a new selection
3603 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3604 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3605 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3606 selectionState = SelectionStarted;
3611 // build up highlight(s) with this selection data.
3612 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3613 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3620 // If reached end, and still on selection, then close selection.
3623 if(selectionState == SelectionStarted)
3625 // finished selection.
3627 if(lastIt->mIsNewParagraphChar)
3629 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3631 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3632 maxRowLeft = std::min(maxRowLeft, min.x);
3633 maxRowRight = std::max(maxRowRight, max.x);
3634 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3635 float rowTop = rowBottom - rowSize.height;
3636 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3640 // Get the top left and bottom right corners.
3641 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3642 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3643 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3645 // Clamp quads so they appear to clip to borders of the whole text.
3646 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3648 // For block-align align Further Clamp quads to max left and right extents
3649 if(blockAlignEnabled)
3651 // BlockAlign: Will adjust highlight to block:
3653 // H[ello] (top row right = max of all rows right)
3654 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3655 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3656 // [text] (bottom row left = min of all rows left)
3657 // (common in SMS messaging selection)
3659 // As opposed to the default which is tight text highlighting.
3664 // (common in regular text editors/web browser selection)
3666 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3669 // Finally clamp quads again so they don't exceed the boundry of the control.
3670 const Vector3& controlSize = GetControlSize();
3671 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3674 return mNewHighlightInfo;
3677 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3678 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3679 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3681 // At the moment there is no public API to modify the block alignment option.
3683 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3685 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3687 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3688 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3690 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3691 std::vector<bool> selectedVisualText;
3692 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3693 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3694 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3696 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3697 float rowLeft = 0.0f;
3698 float rowRight = 0.0f;
3700 // VCC TODO this is valid for one line.
3702 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3704 // Scan through entire text.
3707 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3709 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3710 bool charSelected = false;
3711 if( selectedIt != selectedEndIt )
3713 charSelected = *selectedIt++;
3718 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3719 rowRight = rowLeft + charInfo.mSize.width;
3721 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3722 float rowTop = rowBottom - rowSize.height;
3723 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3729 // Finally clamp quads again so they don't exceed the boundry of the control.
3730 const Vector3& controlSize = GetControlSize();
3731 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3734 return mNewHighlightInfo;
3737 void TextInput::UpdateHighlight()
3739 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3741 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3743 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3744 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3745 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3746 // [BOTTOM] [ MIDDLE ]
3749 // Each quad is created as 2 triangles.
3750 // Middle is just 1 quad regardless of its size.
3764 if ( mHighlightMeshActor )
3766 // vertex and triangle buffers should always be present if MeshActor is alive.
3767 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3768 MeshData::VertexContainer vertices;
3769 Dali::MeshData::FaceIndices faceIndices;
3771 if( !newHighlightInfo.mQuadList.empty() )
3773 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3774 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3776 // vertex position defaults to (0 0 0)
3777 MeshData::Vertex vertex;
3778 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3781 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3783 // Add each quad geometry (a sub-selection) to the mesh data.
3793 QuadCoordinates& quad = *iter;
3795 vertex.x = quad.min.x;
3796 vertex.y = quad.min.y;
3797 vertices.push_back( vertex );
3800 vertex.x = quad.max.x;
3801 vertex.y = quad.min.y;
3802 vertices.push_back( vertex );
3804 // bottom-left (v+2)
3805 vertex.x = quad.min.x;
3806 vertex.y = quad.max.y;
3807 vertices.push_back( vertex );
3809 // bottom-right (v+3)
3810 vertex.x = quad.max.x;
3811 vertex.y = quad.max.y;
3812 vertices.push_back( vertex );
3814 // triangle A (3, 1, 0)
3815 faceIndices.push_back( v + 3 );
3816 faceIndices.push_back( v + 1 );
3817 faceIndices.push_back( v );
3819 // triangle B (0, 2, 3)
3820 faceIndices.push_back( v );
3821 faceIndices.push_back( v + 2 );
3822 faceIndices.push_back( v + 3 );
3824 mMeshData.SetFaceIndices( faceIndices );
3827 BoneContainer bones(0); // passed empty as bones not required
3828 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3829 mHighlightMesh.UpdateMeshData(mMeshData);
3834 void TextInput::ClearPopup()
3836 mPopupPanel.Clear();
3839 void TextInput::AddPopupOptions()
3841 mPopupPanel.AddPopupOptions();
3844 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3846 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3848 Vector3 clampedPosition ( position );
3849 Vector3 tailOffsetPosition ( position );
3851 float xOffSet( 0.0f );
3853 Actor self = Self();
3854 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3856 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3857 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3859 // Clamp to left or right or of boundary
3860 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3862 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3864 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3866 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3869 clampedPosition.x = position.x + xOffSet;
3870 tailOffsetPosition.x = -xOffSet;
3872 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3873 bool flipTail( false );
3875 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3877 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3881 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3882 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3885 void TextInput::HidePopup(bool animate, bool signalFinished )
3887 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3889 mPopupPanel.Hide( animate );
3891 if( animate && signalFinished )
3893 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3898 void TextInput::ShowPopup( bool animate )
3901 Vector2 alternativePopupPosition;
3903 if(mHighlightMeshActor && mState == StateEdit)
3906 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3908 // When text is selected, show popup above top handle (and text), or below bottom handle.
3909 // topHandle: referring to the top most point of the handle or the top line of selection.
3910 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3912 topHandle = mSelectionHandleOneActualPosition;
3913 bottomHandle = mSelectionHandleTwoActualPosition;
3914 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3918 topHandle = mSelectionHandleTwoActualPosition;
3919 bottomHandle = mSelectionHandleOneActualPosition;
3920 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3922 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3923 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3925 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3927 position.x = xPosition;
3929 // Alternative position if no upper space
3930 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3931 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3935 // When no text is selected, show popup at world position of grab handle or cursor
3936 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3937 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3938 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3939 // if can't be positioned above, then position below row.
3940 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3943 // If grab handle enabled then position pop-up below the grab handle.
3944 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3948 SetPopupPosition( position, alternativePopupPosition );
3951 mPopupPanel.Show( Self(), animate );
3952 StartMonitoringStageForTouch();
3954 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3957 void TextInput::ShowPopupCutCopyPaste()
3961 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3962 // Check the selected text is whole text or not.
3963 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3965 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3968 if ( !mStyledText.empty() && IsTextSelected() )
3970 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3971 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3974 if( mClipboard && mClipboard.NumberOfItems() )
3976 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3977 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3982 mPopupPanel.Hide(false);
3986 void TextInput::SetUpPopupSelection( bool showCutButton )
3989 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3990 // If no text exists then don't offer to select
3991 if ( !mStyledText.empty() )
3993 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3994 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3995 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
3997 // if clipboard has valid contents then offer paste option
3998 if( mClipboard && mClipboard.NumberOfItems() )
4000 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4001 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4006 mPopupPanel.Hide(false);
4009 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4014 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4015 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4016 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4017 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4019 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4021 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4023 float closestYdifference = std::numeric_limits<float>::max();
4024 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4025 std::size_t numberOfMatchedCharacters = 0;
4027 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4028 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4030 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4032 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4033 float baselinePosition = info.mPosition.y - info.mDescender;
4035 if( info.mIsVisible )
4037 // store difference between source y point and the y position of the current character
4038 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4040 if( currentYdifference < closestYdifference )
4042 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4043 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4044 closestYdifference = currentYdifference;
4045 matchedCharacters.clear();
4046 numberOfMatchedCharacters = 0; // reset count
4049 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4050 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4052 // ignore new line character.
4053 if( !info.mIsNewParagraphChar )
4055 matchedCharacters.push_back( info );
4056 numberOfMatchedCharacters++;
4060 } // End of loop checking each character's y position in the character layout table
4062 // Check if last character is a newline, if it is
4063 // then need pretend there is an imaginary line afterwards,
4064 // and check if user is touching below previous line.
4065 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4067 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4069 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4073 // 2 Iterate through matching list of y positions and find closest matching X position.
4075 bool matched( false );
4077 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4078 std::size_t visualIndex = 0u;
4079 const std::size_t matchedCharactersSize = matchedCharacters.size();
4080 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4082 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4084 if( info.mIsVisible )
4086 // stop when on left side of character's center.
4087 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4088 if( sourceScrollOffset.x < characterMidPointPosition )
4090 if(info.mIsRightToLeftCharacter)
4092 rightToLeftChar = true;
4094 glyphIntersection = info.mPosition.x;
4099 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4103 if( visualIndex == matchedCharactersSize )
4105 rightToLeftChar = lastRightToLeftChar;
4108 closestIndex = lineOffset + visualIndex;
4110 mClosestCursorPositionEOL = false; // reset
4111 if( ( visualIndex == matchedCharactersSize ) && !matched )
4113 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4116 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4117 if( rightToLeftChar && lastRightToLeftChar )
4119 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4124 // closestIndex is the visual index, need to convert it to the logical index
4125 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4127 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4129 // Checks for situations where user is touching between LTR and RTL
4130 // characters. To identify if the user means the end of a LTR string
4131 // or the beginning of an RTL string, and vice versa.
4132 if( closestIndex > 0 )
4134 if( rightToLeftChar && !lastRightToLeftChar )
4139 // A: In this touch range, the user is indicating that they wish to place
4140 // the cursor at the end of the LTR text.
4141 // B: In this touch range, the user is indicating that they wish to place
4142 // the cursor at the end of the RTL text.
4144 // Result of touching A area:
4145 // [.....LTR]|[RTL......]+
4147 // |: primary cursor (for typing LTR chars)
4148 // +: secondary cursor (for typing RTL chars)
4150 // Result of touching B area:
4151 // [.....LTR]+[RTL......]|
4153 // |: primary cursor (for typing RTL chars)
4154 // +: secondary cursor (for typing LTR chars)
4156 if( sourceScrollOffset.x < glyphIntersection )
4161 else if( !rightToLeftChar && lastRightToLeftChar )
4163 if( sourceScrollOffset.x < glyphIntersection )
4170 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4171 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4172 // one further ahead
4173 if( rightToLeftChar && !lastRightToLeftChar )
4178 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4180 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4182 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4191 float TextInput::GetLineJustificationPosition() const
4193 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4194 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4195 float alignmentOffset = 0.f;
4197 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4198 if( alignment & Toolkit::Alignment::HorizontalLeft )
4200 alignmentOffset = 0.f;
4202 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4204 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4206 else if( alignment & Toolkit::Alignment::HorizontalRight )
4208 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4211 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4212 float justificationOffset = 0.f;
4214 switch( justification )
4216 case Toolkit::TextView::Left:
4218 justificationOffset = 0.f;
4221 case Toolkit::TextView::Center:
4223 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4226 case Toolkit::TextView::Right:
4228 justificationOffset = mTextLayoutInfo.mTextSize.width;
4231 case Toolkit::TextView::Justified:
4233 justificationOffset = 0.f;
4238 DALI_ASSERT_ALWAYS( false );
4242 return alignmentOffset + justificationOffset;
4245 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4247 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4248 A newline character is not inserted in this case */
4250 Vector3 cursorPosition;
4252 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4256 if( characterPosition > 0u )
4258 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4260 // If previous character on a different line then use current characters position
4261 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4263 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4264 if ( mClosestCursorPositionEOL )
4266 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4270 cursorPosition = Vector3( currentCharInfo.mPosition );
4279 // If the character is left to right, the position is the character's position plus its width.
4280 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4282 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4283 cursorPosition.y = currentCharInfo.mPosition.y;
4286 return cursorPosition;
4289 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4291 bool direction = false;
4292 Vector3 alternatePosition;
4293 bool alternatePositionValid = false;
4295 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4298 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4300 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4301 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4302 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4304 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4306 alternatePositionValid = false;
4307 directionRTL = false;
4309 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4311 if( characterPosition == 0u )
4313 // When the cursor position is at the beginning, it should be at the start of the current character.
4314 // If the current character is LTR, then the start is on the right side of the glyph.
4315 // If the current character is RTL, then the start is on the left side of the glyph.
4317 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4319 characterPosition = FindVisibleCharacter( Right, 0u );
4322 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4323 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4325 cursorPosition.x = info.mPosition.x + rtlOffset;
4326 cursorPosition.y = info.mPosition.y;
4327 directionRTL = info.mIsRightToLeftCharacter;
4329 else if( characterPosition > 0u )
4331 // Get the direction of the paragraph.
4332 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4333 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4335 // When cursor is not at beginning, consider possibility of
4336 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4338 // Cursor position should be the end of the last character.
4339 // If the last character is LTR, then the end is on the right side of the glyph.
4340 // If the last character is RTL, then the end is on the left side of the glyph.
4342 --characterPosition;
4344 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4346 characterPosition = FindVisibleCharacter( Left, characterPosition );
4349 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4350 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4352 // VCC TODO : check for a new paragraph character.
4354 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4355 const Vector3& size = GetControlSize();
4357 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4359 --characterPosition;
4361 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4364 if( !info.mIsNewParagraphChar )
4366 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4370 // VCC TODO : check for a new paragraph character.
4372 // When cursor points to first character on new line, position cursor at the start of this glyph.
4373 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4375 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4376 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4378 cursorPosition.x = infoNext.mPosition.x + start;
4379 cursorPosition.y = infoNext.mPosition.y;
4383 // If cursor points to the end of text, then can only position
4384 // cursor where the new line starts based on the line-justification position.
4385 cursorPosition.x = GetLineJustificationPosition();
4387 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4389 // If this is after the last character, then we can assume that the new cursor
4390 // should be exactly one row below the current row.
4392 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4393 cursorPosition.y = info.mPosition.y + rowRect.height;
4397 // If this is not after last character, then we can use this row's height.
4398 // should be exactly one row below the current row.
4400 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4401 cursorPosition.y = info.mPosition.y + rowRect.height;
4406 directionRTL = info.mIsRightToLeftCharacter;
4408 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4410 // 1. When the cursor is neither at the beginning or the end,
4411 // we can show multiple cursors under situations when the cursor is
4412 // between RTL and LTR text...
4413 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4415 std::size_t characterAltPosition = characterPosition + 1u;
4417 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4419 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4421 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4422 // Text: [...LTR...]|[...RTL...]
4424 // Alternate cursor pos: ^
4425 // In which case we need to display an alternate cursor for the RTL text.
4427 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4428 alternatePosition.y = infoAlt.mPosition.y;
4429 alternatePositionValid = true;
4431 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4433 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4434 // Text: |[...RTL...] [...LTR....]
4436 // Alternate cursor pos: ^
4437 // In which case we need to display an alternate cursor for the RTL text.
4439 alternatePosition.x = infoAlt.mPosition.x;
4440 alternatePosition.y = infoAlt.mPosition.y;
4441 alternatePositionValid = true;
4446 // 2. When the cursor is at the end of the text,
4447 // and we have multi-directional text,
4448 // we can also consider showing mulitple cursors.
4449 // The rule here is:
4450 // If first and last characters on row are different
4451 // Directions, then two cursors need to be displayed.
4453 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4455 // The last character's direction is differernt than the first one of current paragraph.
4458 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4460 if(info.mIsRightToLeftCharacter)
4462 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4463 // Text: [...LTR...]|[...RTL...]
4465 // Alternate cursor pos: ^
4466 // In which case we need to display an alternate cursor for the RTL text, this cursor
4467 // should be at the end of the given line.
4469 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4470 alternatePosition.y = infoStart.mPosition.y;
4471 alternatePositionValid = true;
4473 else if(!info.mIsRightToLeftCharacter) // starting RTL
4475 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4476 // Text: |[...RTL...] [...LTR....]
4478 // Alternate cursor pos: ^
4479 // In which case we need to display an alternate cursor for the RTL text.
4481 alternatePosition.x = infoStart.mPosition.x;
4482 alternatePosition.y = infoStart.mPosition.y;
4483 alternatePositionValid = true;
4488 } // characterPosition > 0
4492 // If the character table is void, place the cursor accordingly the text alignment.
4493 const Vector3& size = GetControlSize();
4495 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4496 float alignmentOffset = 0.f;
4498 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4499 if( alignment & Toolkit::Alignment::HorizontalLeft )
4501 alignmentOffset = 0.f;
4503 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4505 alignmentOffset = 0.5f * ( size.width );
4507 else if( alignment & Toolkit::Alignment::HorizontalRight )
4509 alignmentOffset = size.width;
4512 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4513 cursorPosition.x = alignmentOffset;
4515 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4516 if( alignment & Toolkit::Alignment::VerticalTop )
4518 cursorPosition.y = mLineHeight;
4520 else if( alignment & Toolkit::Alignment::VerticalCenter )
4522 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4524 else if( alignment & Toolkit::Alignment::VerticalBottom )
4526 cursorPosition.y = size.height;
4530 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4531 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4533 if( alternatePositionValid )
4535 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4536 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4539 return cursorPosition;
4542 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4544 // scan string from current position to beginning of current line to note direction of line
4545 while( logicalPosition )
4548 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4555 return logicalPosition;
4558 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4560 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4562 while( logicalPosition )
4565 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4572 return logicalPosition;
4575 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4579 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4582 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4584 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4585 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4587 min = Vector2::ZERO;
4588 max = Vector2(0.0f, mLineHeight);
4592 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4594 // Initializes the min and max position.
4595 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4596 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4600 // 1) Find the line where the character is laid-out.
4601 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4602 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4605 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4607 // Index within the whole text to the last character of the current line.
4608 std::size_t lastCharacterOfLine = 0u;
4610 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4611 if( lineNextIt != lineEndIt )
4613 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4617 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4620 // Check if the given chracter position is within the line.
4621 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4623 // 2) Get the row rect of all laid-out characters on the line.
4625 // Need to scan all characters of the line because they are in the logical position.
4626 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4627 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4631 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4633 min.x = std::min( min.x, characterInfo.mPosition.x );
4634 min.y = std::min( min.y, characterInfo.mPosition.y );
4635 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4636 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4643 return Size( max.x - min.x, max.y - min.y );
4646 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4648 Actor popUpPanel = mPopupPanel.GetRootActor();
4650 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4656 Dali::Actor parent( touchedActor.GetParent() );
4660 return WasTouchedCheck( parent );
4667 void TextInput::StartMonitoringStageForTouch()
4669 Stage stage = Stage::GetCurrent();
4670 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4673 void TextInput::EndMonitoringStageForTouch()
4675 Stage stage = Stage::GetCurrent();
4676 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4679 void TextInput::OnStageTouched(const TouchEvent& event)
4681 if( event.GetPointCount() > 0 )
4683 if ( TouchPoint::Down == event.GetPoint(0).state )
4685 const Actor touchedActor(event.GetPoint(0).hitActor);
4687 bool popUpShown( false );
4689 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4694 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4696 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4698 EndMonitoringStageForTouch();
4699 HidePopup( true, false );
4702 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4704 EndMonitoringStageForTouch();
4705 ShowGrabHandleAndSetVisibility( false );
4711 void TextInput::SelectText(std::size_t start, std::size_t end)
4713 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4714 IsGrabHandleEnabled()?"true":"false",
4715 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4716 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4717 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4719 StartMonitoringStageForTouch();
4721 if ( mEditModeActive ) // Only allow text selection when in edit mode
4723 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4724 mSelectingText = true;
4726 std::size_t selectionStartPosition = std::min( start, end );
4728 // Hide grab handle when selecting.
4729 ShowGrabHandleAndSetVisibility( false );
4731 if( start != end ) // something to select
4733 SetCursorVisibility( false );
4734 StopCursorBlinkTimer();
4736 CreateSelectionHandles(start, end);
4739 const TextStyle oldInputStyle( mInputStyle );
4740 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4742 if( oldInputStyle != mInputStyle )
4744 // Updates the line height accordingly with the input style.
4747 EmitStyleChangedSignal();
4753 mSelectingText = false;
4757 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4759 MarkupProcessor::StyledTextArray currentSelectedText;
4761 if ( IsTextSelected() )
4763 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4764 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4766 for(; it != end; ++it)
4768 MarkupProcessor::StyledText& styledText( *it );
4769 currentSelectedText.push_back( styledText );
4772 return currentSelectedText;
4775 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4777 const std::size_t beginIndex = std::min( begin, end );
4778 const std::size_t endIndex = std::max( begin, end );
4781 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4783 // Create a styled text array used to replace the text into the text-view.
4784 MarkupProcessor::StyledTextArray text;
4785 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4787 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4788 GetTextLayoutInfo();
4790 if( IsScrollEnabled() )
4792 // Need to set the scroll position as the text's size may have changed.
4793 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4796 ShowGrabHandleAndSetVisibility( false );
4802 // Set Handle positioning as the new style may have repositioned the characters.
4803 SetSelectionHandlePosition(HandleOne);
4804 SetSelectionHandlePosition(HandleTwo);
4807 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4809 // Just hide the grab handle when keyboard is hidden.
4810 if (!keyboardShown )
4812 ShowGrabHandleAndSetVisibility( false );
4814 // If the keyboard is not now being shown, then hide the popup panel
4815 mPopupPanel.Hide( true );
4819 // Removes highlight and resumes edit mode state
4820 void TextInput::RemoveHighlight( bool hidePopup )
4822 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4824 if ( mHighlightMeshActor )
4826 if ( mSelectionHandleOne )
4828 mActiveLayer.Remove( mSelectionHandleOne );
4829 mSelectionHandleOne.Reset();
4830 mSelectionHandleOneOffset.x = 0.0f;
4832 if ( mSelectionHandleTwo )
4834 mActiveLayer.Remove( mSelectionHandleTwo );
4835 mSelectionHandleTwo.Reset();
4836 mSelectionHandleTwoOffset.x = 0.0f;
4839 mNewHighlightInfo.mQuadList.clear();
4841 Self().Remove( mHighlightMeshActor );
4843 SetCursorVisibility( true );
4844 StartCursorBlinkTimer();
4846 mHighlightMeshActor.Reset();
4847 // NOTE: We cannot dereference mHighlightMesh, due
4848 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4856 mSelectionHandleOnePosition = 0;
4857 mSelectionHandleTwoPosition = 0;
4860 void TextInput::CreateHighlight()
4862 if ( !mHighlightMeshActor )
4864 mMeshData = MeshData( );
4865 mMeshData.SetHasNormals( true );
4867 mCustomMaterial = Material::New("CustomMaterial");
4868 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4870 mMeshData.SetMaterial( mCustomMaterial );
4872 mHighlightMesh = Mesh::New( mMeshData );
4874 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4875 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4876 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4877 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4878 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4879 mHighlightMeshActor.SetAffectedByLighting(false);
4881 Self().Add(mHighlightMeshActor);
4886 bool TextInput::CopySelectedTextToClipboard()
4888 mCurrentCopySelecton.clear();
4890 mCurrentCopySelecton = GetSelectedText();
4892 std::string stringToStore;
4894 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4895 * a marked up string.
4897 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4898 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4900 bool success = mClipboard.SetItem( stringToStore );
4904 void TextInput::PasteText( const Text& text )
4906 // Update Flag, indicates whether to update the text-input contents or not.
4907 // Any key stroke that results in a visual change of the text-input should
4908 // set this flag to true.
4909 bool update = false;
4910 if( mHighlightMeshActor )
4912 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4913 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4915 ImfManager imfManager = ImfManager::Get();
4918 imfManager.SetCursorPosition( mCursorPosition );
4919 imfManager.NotifyCursorPosition();
4921 DeleteHighlightedText( true );
4925 bool textExceedsMaximunNumberOfCharacters = false;
4926 bool textExceedsBoundary = false;
4928 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4930 mCursorPosition += insertedStringLength;
4931 ImfManager imfManager = ImfManager::Get();
4934 imfManager.SetCursorPosition ( mCursorPosition );
4935 imfManager.NotifyCursorPosition();
4938 update = update || ( insertedStringLength > 0 );
4945 if( insertedStringLength < text.GetLength() )
4947 EmitMaxInputCharactersReachedSignal();
4950 if( textExceedsBoundary )
4952 EmitInputTextExceedsBoundariesSignal();
4956 void TextInput::SetTextDirection()
4958 // Put the cursor to the right if we are empty and an RTL language is being used.
4959 if ( mStyledText.empty() )
4961 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4963 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4964 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4966 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4967 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4969 int alignment( mDisplayedTextView.GetTextAlignment() &
4970 ( Toolkit::Alignment::VerticalTop |
4971 Toolkit::Alignment::VerticalCenter |
4972 Toolkit::Alignment::VerticalBottom |
4973 Toolkit::Alignment::HorizontalCenter ) );
4974 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4976 // If our alignment is in the center, then do not change.
4977 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4979 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4982 // If our justification is in the center, then do not change.
4983 if ( justification != Toolkit::TextView::Center )
4985 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4988 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4989 mDisplayedTextView.SetLineJustification( justification );
4993 void TextInput::UpdateLineHeight()
4995 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4996 mLineHeight = font.GetLineHeight();
4998 // 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.
5000 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5002 if( !mExceedEnabled || shrink )
5004 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5008 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5010 // VCC check if we need do this in the visual order ...
5011 std::size_t position = 0u;
5013 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5019 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5021 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5023 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5029 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5030 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5032 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5038 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5043 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5050 void TextInput::SetSortModifier( float depthOffset )
5052 if(mDisplayedTextView)
5054 mDisplayedTextView.SetSortModifier(depthOffset);
5058 void TextInput::SetSnapshotModeEnabled( bool enable )
5060 if(mDisplayedTextView)
5062 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5066 bool TextInput::IsSnapshotModeEnabled() const
5068 bool snapshotEnabled = false;
5070 if(mDisplayedTextView)
5072 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5075 return snapshotEnabled;
5078 void TextInput::SetMarkupProcessingEnabled( bool enable )
5080 mMarkUpEnabled = enable;
5083 bool TextInput::IsMarkupProcessingEnabled() const
5085 return mMarkUpEnabled;
5088 void TextInput::SetScrollEnabled( bool enable )
5090 if( mDisplayedTextView )
5092 mDisplayedTextView.SetScrollEnabled( enable );
5097 // Don't set cursor's and handle's visibility to false if they are outside the
5098 // boundaries of the text-input.
5099 mIsCursorInScrollArea = true;
5100 mIsGrabHandleInScrollArea = true;
5101 if( mSelectionHandleOne && mSelectionHandleTwo )
5103 mSelectionHandleOne.SetVisible( true );
5104 mSelectionHandleTwo.SetVisible( true );
5106 if( mHighlightMeshActor )
5108 mHighlightMeshActor.SetVisible( true );
5114 bool TextInput::IsScrollEnabled() const
5116 bool scrollEnabled = false;
5118 if( mDisplayedTextView )
5120 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5123 return scrollEnabled;
5126 void TextInput::SetScrollPosition( const Vector2& position )
5128 if( mDisplayedTextView )
5130 mDisplayedTextView.SetScrollPosition( position );
5134 Vector2 TextInput::GetScrollPosition() const
5136 Vector2 scrollPosition;
5138 if( mDisplayedTextView )
5140 scrollPosition = mDisplayedTextView.GetScrollPosition();
5143 return scrollPosition;
5146 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5148 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5149 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5150 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5152 // Add style to the new input text.
5153 MarkupProcessor::StyledTextArray textToInsert;
5154 for( std::size_t i = 0; i < insertedStringLength; ++i )
5156 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5157 textToInsert.push_back( newStyledCharacter );
5160 //Insert text to the TextView.
5161 const bool emptyTextView = mStyledText.empty();
5162 if( emptyTextView && mPlaceHolderSet )
5164 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5165 mDisplayedTextView.SetText( textToInsert );
5169 if( 0 == numberOfCharactersToReplace )
5171 mDisplayedTextView.InsertTextAt( position, textToInsert );
5175 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5178 mPlaceHolderSet = false;
5180 if( textToInsert.empty() )
5182 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5183 GetTextLayoutInfo();
5187 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5188 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5191 textExceedsBoundary = false;
5193 if( !mExceedEnabled )
5195 const Vector3& size = GetControlSize();
5197 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5199 // If new text does not fit within TextView
5200 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5201 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5202 GetTextLayoutInfo();
5203 textExceedsBoundary = true;
5204 insertedStringLength = 0;
5207 if( textExceedsBoundary )
5209 // Add the part of the text which fits on the text-input.
5211 // Split the text which doesn't fit in two halves.
5212 MarkupProcessor::StyledTextArray firstHalf;
5213 MarkupProcessor::StyledTextArray secondHalf;
5214 SplitText( textToInsert, firstHalf, secondHalf );
5216 // Clear text. This text will be filled with the text inserted.
5217 textToInsert.clear();
5219 // Where to insert the text.
5220 std::size_t positionToInsert = position;
5222 bool end = text.GetLength() <= 1;
5225 // Insert text and check ...
5226 const std::size_t textLength = firstHalf.size();
5227 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5228 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5230 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5232 // Inserted text doesn't fit.
5234 // Remove inserted text
5235 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5236 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5238 // The iteration finishes when only one character doesn't fit.
5239 end = textLength <= 1;
5243 // Prepare next two halves for next iteration.
5244 MarkupProcessor::StyledTextArray copyText = firstHalf;
5245 SplitText( copyText, firstHalf, secondHalf );
5252 // store text to be inserted in mStyledText.
5253 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5255 // Increase the inserted characters counter.
5256 insertedStringLength += textLength;
5258 // Prepare next two halves for next iteration.
5259 MarkupProcessor::StyledTextArray copyText = secondHalf;
5260 SplitText( copyText, firstHalf, secondHalf );
5262 // Update where next text has to be inserted
5263 positionToInsert += textLength;
5269 if( textToInsert.empty() && emptyTextView )
5271 // No character has been added and the text-view was empty.
5272 // Show the placeholder text.
5273 ShowPlaceholderText( mStyledPlaceHolderText );
5277 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5278 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5279 mPlaceHolderSet = false;
5282 return insertedStringLength;
5285 void TextInput::GetTextLayoutInfo()
5287 if( mStyledText.empty() )
5289 // The text-input has no text, clear the text-view's layout info.
5290 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5294 if( mDisplayedTextView )
5296 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5300 // There is no text-view.
5301 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5306 void TextInput::SetOffsetFromText( const Vector4& offset )
5308 mPopupOffsetFromText = offset;
5311 const Vector4& TextInput::GetOffsetFromText() const
5313 return mPopupOffsetFromText;
5316 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5318 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5322 TextInput& textInputImpl( GetImpl( textInput ) );
5324 switch ( propertyIndex )
5326 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5328 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5331 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5333 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5336 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5338 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5341 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5343 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5346 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5348 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5351 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5353 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5356 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5358 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5361 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5363 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5366 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5368 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5371 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5373 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5376 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5378 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5381 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5383 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5386 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5388 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5391 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5393 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5396 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5398 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5401 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5403 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5409 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5411 Property::Value value;
5413 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5417 TextInput& textInputImpl( GetImpl( textInput ) );
5419 switch ( propertyIndex )
5421 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5423 value = textInputImpl.GetMaterialDiffuseColor();
5426 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5428 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5431 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5433 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5436 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5438 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5441 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5443 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5446 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5448 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5451 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5453 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5456 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5458 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5461 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5463 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5466 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5468 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5471 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5473 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5476 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5478 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5481 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5483 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5486 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5488 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5491 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5493 value = textInputImpl.GetOffsetFromText();
5496 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5498 value = textInputImpl.mCursor.GetCurrentColor();
5505 void TextInput::EmitStyleChangedSignal()
5507 // emit signal if input style changes.
5508 Toolkit::TextInput handle( GetOwner() );
5509 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5512 void TextInput::EmitTextModified()
5514 // emit signal when text changes.
5515 Toolkit::TextInput handle( GetOwner() );
5516 mTextModifiedSignal.Emit( handle );
5520 void TextInput::EmitMaxInputCharactersReachedSignal()
5522 // emit signal if max characters is reached during text input.
5523 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5525 Toolkit::TextInput handle( GetOwner() );
5526 mMaxInputCharactersReachedSignalV2.Emit( handle );
5529 void TextInput::EmitInputTextExceedsBoundariesSignal()
5531 // Emit a signal when the input text exceeds the boundaries of the text input.
5533 Toolkit::TextInput handle( GetOwner() );
5534 mInputTextExceedBoundariesSignalV2.Emit( handle );
5537 } // namespace Internal
5539 } // namespace Toolkit