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
90 * Whether the given style is the default style or not.
91 * @param[in] style The given style.
92 * @return \e true if the given style is the default. Otherwise it returns \e false.
94 bool IsDefaultStyle( const TextStyle& style )
96 return DEFAULT_TEXT_STYLE == style;
100 * Whether the given styled text is using the default style or not.
101 * @param[in] textArray The given text.
102 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108 const TextStyle& style( (*it).mStyle );
110 if( !IsDefaultStyle( style ) )
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
125 if( ( *it ).mIsVisible )
127 return --cursorPosition;
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
138 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140 if( ( *it ).mIsVisible )
142 return cursorPosition;
148 return cursorPosition;
152 * Whether the given position plus the cursor size offset is inside the given boundary.
154 * @param[in] position The given position.
155 * @param[in] cursorSize The cursor size.
156 * @param[in] controlSize The given boundary.
158 * @return whether the given position is inside the given boundary.
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
169 * Splits a text in two halves.
171 * If the text's number of characters is odd, firstHalf has one more character.
173 * @param[in] text The text to be split.
174 * @param[out] firstHalf The first half of the text.
175 * @param[out] secondHalf The second half of the text.
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
184 const std::size_t textLength = text.size();
185 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
191 } // end of namespace
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
261 QuadCoordinates quad(x1, y1, x2, y2);
262 mQuadList.push_back( quad );
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
267 for(std::size_t i = 0;i < mQuadList.size(); i++)
269 QuadCoordinates& quad = mQuadList[i];
271 quad.min.Clamp(min, max);
272 quad.max.Clamp(min, max);
276 // [TextInput] ////////////////////////////////////////////////////////////////
278 Dali::Toolkit::TextInput TextInput::New()
280 // Create the implementation
281 TextInputPtr textInput(new TextInput());
282 // Pass ownership to CustomActor via derived handle
283 Dali::Toolkit::TextInput handle(*textInput);
284 handle.SetName( "TextInput");
286 textInput->Initialize();
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
296 mDisplayedTextView(),
297 mStyledPlaceHolderText(),
298 mMaxStringLength( DEFAULT_MAX_SIZE ),
299 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300 mCursorPosition( 0 ),
301 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302 mIsSelectionHandleOneFlipped( false ),
303 mIsSelectionHandleTwoFlipped( false ),
304 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308 mSelectionHandleOnePosition( 0 ),
309 mSelectionHandleTwoPosition( 0 ),
311 mPreEditStartPosition( 0 ),
312 mPreEditLength ( 0 ),
313 mNumberOfSurroundingCharactersDeleted( 0 ),
314 mTouchStartTime( 0 ),
316 mCurrentCopySelecton(),
319 mScrollDisplacement(),
320 mCurrentHandlePosition(),
321 mCurrentSelectionId(),
322 mCurrentSelectionHandlePosition(),
323 mRequestedSelection( 0, 0 ),
324 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
327 mMaterialColor( LIGHTBLUE ),
328 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329 mOverrideAutomaticAlignment( false ),
330 mCursorRTLEnabled( false ),
331 mClosestCursorPositionEOL ( false ),
332 mCursorBlinkStatus( true ),
333 mCursorVisibility( false ),
334 mGrabHandleVisibility( false ),
335 mIsCursorInScrollArea( true ),
336 mIsGrabHandleInScrollArea( true ),
337 mEditModeActive( false ),
338 mEditOnTouch( true ),
339 mTextSelection( true ),
340 mExceedEnabled( true ),
341 mGrabHandleEnabled( true ),
342 mIsSelectionHandleFlipEnabled( true ),
343 mPreEditFlag( false ),
344 mIgnoreCommitFlag( false ),
345 mIgnoreFirstCommitFlag( false ),
346 mSelectingText( false ),
347 mPreserveCursorPosition( false ),
348 mSelectTextOnCommit( false ),
349 mUnderlinedPriorToPreEdit ( false ),
350 mCommitByKeyInput( false ),
351 mPlaceHolderSet( false ),
352 mMarkUpEnabled( false )
354 // Updates the line height accordingly with the input style.
358 TextInput::~TextInput()
360 StopCursorBlinkTimer();
365 std::string TextInput::GetText() const
369 // Return text-view's text only if the text-input's text is not empty
370 // in order to not to return the placeholder text.
371 if( !mStyledText.empty() )
373 text = mDisplayedTextView.GetText();
379 std::string TextInput::GetMarkupText() const
381 std::string markupString;
382 MarkupProcessor::GetMarkupString( mStyledText, markupString );
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
389 // Get the placeholder styled text array from the markup string.
390 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
392 if( mStyledText.empty() )
394 // Set the placeholder text only if the styled text is empty.
395 mDisplayedTextView.SetText( mStyledPlaceHolderText );
396 mPlaceHolderSet = true;
400 std::string TextInput::GetPlaceholderText()
402 // Traverses the styled placeholder array getting only the text.
403 // Note that for some languages a 'character' could be represented by more than one 'char'
405 std::string placeholderText;
406 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
408 placeholderText.append( (*it).mText.GetText() );
411 return placeholderText ;
414 void TextInput::SetInitialText(const std::string& initialText)
416 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
418 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
420 mPreEditFlag = false;
421 mIgnoreCommitFlag = true;
424 SetText( initialText );
425 PreEditReset( false ); // Reset keyboard as text changed
428 void TextInput::SetText(const std::string& initialText)
430 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
432 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
434 if( mStyledText.empty() )
436 // If the initial text is empty, set the placeholder text.
437 mDisplayedTextView.SetText( mStyledPlaceHolderText );
438 mPlaceHolderSet = true;
442 mDisplayedTextView.SetText( mStyledText );
443 mPlaceHolderSet = false;
448 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
450 ImfManager imfManager = ImfManager::Get();
453 imfManager.SetCursorPosition( mCursorPosition );
454 imfManager.SetSurroundingText( initialText );
455 imfManager.NotifyCursorPosition();
458 if( IsScrollEnabled() )
460 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
463 ShowGrabHandleAndSetVisibility( false );
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
474 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
476 mDisplayedTextView.SetText( styleText );
477 mPlaceHolderSet = false;
479 // If text alignment hasn't been manually set by application developer, then we
480 // automatically determine the alignment based on the content of the text i.e. what
481 // language the text begins with.
482 // TODO: This should determine different alignments for each line (broken by '\n') of text.
483 if(!mOverrideAutomaticAlignment)
485 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486 bool leftToRight(true);
488 if( !styleText.empty() )
490 bool breakOut(false);
492 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
494 const Text& text = textIter->mText;
496 for( std::size_t i = 0; i < text.GetLength(); ++i )
498 Character character( text[i] );
499 if( character.GetCharacterDirection() != Character::Neutral )
501 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
509 // Based on this direction, either left or right align text if not manually set by application developer.
510 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512 Toolkit::Alignment::VerticalTop ) );
513 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
521 mMaxStringLength = maxChars;
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
526 DALI_ASSERT_DEBUG( maxLines > 0 )
530 mNumberOflinesLimit = maxLines;
534 std::size_t TextInput::GetNumberOfLinesLimit() const
536 return mNumberOflinesLimit;
539 std::size_t TextInput::GetNumberOfCharacters() const
541 return mStyledText.size();
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
547 mMaterialColor = color;
548 if ( mCustomMaterial )
550 mCustomMaterial.SetDiffuseColor( mMaterialColor );
551 mMeshData.SetMaterial( mCustomMaterial );
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
557 return mMaterialColor;
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
564 return mInputStartedSignalV2;
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
569 return mInputFinishedSignalV2;
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
574 return mCutAndPasteToolBarDisplayedV2;
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
579 return mStyleChangedSignalV2;
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
584 return mTextModifiedSignal;
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
589 return mMaxInputCharactersReachedSignalV2;
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
594 return mInputTextExceedBoundariesSignalV2;
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
599 Dali::BaseHandle handle( object );
601 bool connected( true );
602 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
604 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
606 textInput.InputStartedSignal().Connect( tracker, functor );
608 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
610 textInput.InputFinishedSignal().Connect( tracker, functor );
612 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
614 textInput.StyleChangedSignal().Connect( tracker, functor );
616 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
618 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
620 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
622 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
626 // signalName does not match any signal
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
637 // update line height before calculate the actual position.
642 if( setCursorOnTouchPoint )
644 // Sets the cursor position for the given touch point.
645 ReturnClosestIndex( touchPoint, mCursorPosition );
647 // Creates the grab handle.
648 if( IsGrabHandleEnabled() )
650 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
654 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657 ShowGrabHandleAndSetVisibility( true );
659 // Scrolls the text-view if needed.
660 if( IsScrollEnabled() )
662 ScrollTextViewToMakeCursorVisible( cursorPosition );
668 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
680 bool TextInput::IsEditable() const
682 return mEditModeActive;
685 void TextInput::SetEditOnTouch( bool editOnTouch )
687 mEditOnTouch = editOnTouch;
690 bool TextInput::IsEditOnTouch() const
695 void TextInput::SetTextSelectable( bool textSelectable )
697 mTextSelection = textSelectable;
700 bool TextInput::IsTextSelectable() const
702 return mTextSelection;
705 bool TextInput::IsTextSelected() const
707 return mHighlightMeshActor;
710 void TextInput::DeSelectText()
717 void TextInput::SetGrabHandleImage(Dali::Image image )
721 CreateGrabHandle(image);
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
727 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
731 mCursor.SetImage( image );
732 mCursor.SetNinePatchBorder( border );
736 Vector3 TextInput::GetSelectionHandleSize()
738 return DEFAULT_SELECTION_HANDLE_SIZE;
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
743 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
747 mCursorRTL.SetImage( image);
748 mCursorRTL.SetNinePatchBorder( border );
752 void TextInput::EnableGrabHandle(bool toggle)
754 // enables grab handle with will in turn de-activate magnifier
755 mGrabHandleEnabled = toggle;
758 bool TextInput::IsGrabHandleEnabled()
760 // if false then magnifier will be shown instead.
761 return mGrabHandleEnabled;
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
766 // Deprecated function. To be removed.
767 mIsSelectionHandleFlipEnabled = toggle;
770 bool TextInput::IsSelectionHandleFlipEnabled()
772 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
778 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
782 mSelectionHandleFlipMargin = margin;
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
787 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
793 const Vector4 boundary( originX,
795 originX + boundingRectangle.width,
796 originY + boundingRectangle.height );
798 mBoundingRectangleWorldCoordinates = boundary;
801 const Rect<float> TextInput::GetBoundingRectangle() const
803 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
805 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
808 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
815 return mSelectionHandleFlipMargin;
818 void TextInput::SetTextColor( const Vector4& color )
820 mDisplayedTextView.SetColor( color );
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
825 if( style != mInputStyle )
828 bool emitSignal = false;
830 // mask: modify style according to mask, if different emit signal.
831 const TextStyle oldInputStyle( mInputStyle );
833 // Copy the new style.
834 mInputStyle.Copy( style, mask );
836 // if style has changed, emit signal.
837 if( oldInputStyle != mInputStyle )
842 // Updates the line height accordingly with the input style.
845 // Changing font point size will require the cursor to be re-sized
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
857 if ( IsTextSelected() )
859 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
862 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
864 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
867 // Keeps the old style to be compared with the new one.
868 const TextStyle oldInputStyle( mInputStyle );
870 // Copy only those parameters from the style which are set in the mask.
871 mInputStyle.Copy( style, mask );
873 if( mInputStyle != oldInputStyle )
875 // Updates the line height accordingly with the input style.
878 EmitStyleChangedSignal();
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
885 if( !mStyledText.empty() )
887 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
891 TextStyle TextInput::GetStyleAtCursor() const
895 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
897 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898 style = mStyledText.at( mCursorPosition-1 ).mStyle;
904 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
906 Dali::Font defaultFont = Dali::Font::New();
907 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
916 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
918 if( position >= mStyledText.size() )
920 position = mStyledText.size() - 1;
923 return mStyledText.at( position ).mStyle;
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
928 mDisplayedTextView.SetTextAlignment( align );
929 mOverrideAutomaticAlignment = true;
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
934 mDisplayedTextView.SetLineJustification( justification );
935 mOverrideAutomaticAlignment = true;
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
940 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
945 return mDisplayedTextView.GetFadeBoundary();
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
950 return mDisplayedTextView.GetTextAlignment();
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
955 mDisplayedTextView.SetMultilinePolicy( policy );
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
960 return mDisplayedTextView.GetMultilinePolicy();
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
965 mDisplayedTextView.SetWidthExceedPolicy( policy );
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
970 return mDisplayedTextView.GetWidthExceedPolicy();
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
975 mDisplayedTextView.SetHeightExceedPolicy( policy );
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
980 return mDisplayedTextView.GetHeightExceedPolicy();
983 void TextInput::SetExceedEnabled( bool enable )
985 mExceedEnabled = enable;
988 bool TextInput::GetExceedEnabled() const
990 return mExceedEnabled;
993 void TextInput::SetBackground(Dali::Image image )
995 // TODO Should add this function and add public api to match.
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1005 switch( event.state )
1007 case KeyEvent::Down:
1009 return OnKeyDownEvent(event);
1015 return OnKeyUpEvent(event);
1027 void TextInput::OnKeyInputFocusGained()
1029 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1031 mEditModeActive = true;
1033 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1035 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1037 // Updates the line height accordingly with the input style.
1040 // Connect the signals to use in text input.
1041 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1044 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1047 GetTextLayoutInfo();
1050 SetCursorVisibility( true );
1051 StartCursorBlinkTimer();
1053 Toolkit::TextInput handle( GetOwner() );
1054 mInputStartedSignalV2.Emit( handle );
1056 ImfManager imfManager = ImfManager::Get();
1060 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1062 // Notify that the text editing start.
1063 imfManager.Activate();
1065 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066 imfManager.SetRestoreAferFocusLost( true );
1068 imfManager.SetCursorPosition( mCursorPosition );
1069 imfManager.NotifyCursorPosition();
1072 mClipboard = Clipboard::Get(); // Store handle to clipboard
1074 // Now in edit mode we can accept string to paste from clipboard
1075 if( Adaptor::IsAvailable() )
1077 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1080 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1085 void TextInput::OnKeyInputFocusLost()
1087 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1091 // If key input focus is lost, it removes the
1092 // underline from the last pre-edit text.
1093 RemovePreEditStyle();
1094 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1095 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1099 ImfManager imfManager = ImfManager::Get();
1102 // The text editing is finished. Therefore the imf manager don't have restore activation.
1103 imfManager.SetRestoreAferFocusLost( false );
1105 // Notify that the text editing finish.
1106 imfManager.Deactivate();
1108 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1110 // Disconnect signal used the text input.
1111 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1113 Toolkit::TextInput handle( GetOwner() );
1114 mInputFinishedSignalV2.Emit( handle );
1115 mEditModeActive = false;
1116 mPreEditFlag = false;
1118 SetCursorVisibility( false );
1119 StopCursorBlinkTimer();
1121 ShowGrabHandleAndSetVisibility( false );
1124 // No longer in edit mode so do not want to receive string from clipboard
1125 if( Adaptor::IsAvailable() )
1127 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1130 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1132 Clipboard clipboard = Clipboard::Get();
1136 clipboard.HideClipboard();
1141 void TextInput::OnControlStageConnection()
1143 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1145 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1147 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1151 void TextInput::CreateActiveLayer()
1153 Actor self = Self();
1154 mActiveLayer = Layer::New();
1155 mActiveLayer.SetName ( "ActiveLayerActor" );
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1174 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1176 Actor self = Self();
1177 self.Add( mCursor );
1178 self.Add( mCursorRTL );
1180 mCursorVisibility = false;
1182 CreateActiveLayer(); // todo move this so layer only created when needed.
1184 // Assign names to image actors
1185 mCursor.SetName("mainCursor");
1186 mCursorRTL.SetName("rtlCursor");
1189 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1191 mDisplayedTextView.SetSize( targetSize );
1192 GetTextLayoutInfo();
1193 mActiveLayer.SetSize(targetSize);
1196 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1198 Relayout( mDisplayedTextView, size, container );
1199 Relayout( mPopupPanel.GetRootActor(), size, container );
1201 GetTextLayoutInfo();
1206 Vector3 TextInput::GetNaturalSize()
1208 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1210 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1212 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1213 naturalSize.height = mLineHeight;
1219 float TextInput::GetHeightForWidth( float width )
1221 float height = mDisplayedTextView.GetHeightForWidth( width );
1223 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1225 // If the height is zero, it means there is no text. Let's return the cursor height.
1226 height = mLineHeight;
1232 /*end of Virtual methods from parent*/
1234 // Private Internal methods
1236 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1238 switch (gesture.state)
1240 case Gesture::Started:
1241 // fall through so code not duplicated
1242 case Gesture::Continuing:
1244 if (actor == mGrabArea)
1246 SetCursorVisibility( true );
1247 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1248 MoveGrabHandle( gesture.displacement );
1249 HidePopup(); // Do not show popup whilst handle is moving
1251 else if (actor == mHandleOneGrabArea)
1253 // the displacement in PanGesture is affected by the actor's rotation.
1254 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1255 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1257 MoveSelectionHandle( HandleOne, gesture.displacement );
1259 mState = StateDraggingHandle;
1262 else if (actor == mHandleTwoGrabArea)
1264 // the displacement in PanGesture is affected by the actor's rotation.
1265 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1268 MoveSelectionHandle( HandleTwo, gesture.displacement );
1270 mState = StateDraggingHandle;
1276 case Gesture::Finished:
1278 // Revert back to non-pressed selection handle images
1279 if (actor == mGrabArea)
1281 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1282 SetCursorVisibility( true );
1283 SetUpPopupSelection();
1286 if (actor == mHandleOneGrabArea)
1288 // the displacement in PanGesture is affected by the actor's rotation.
1289 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1290 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1292 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1294 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1296 ShowPopupCutCopyPaste();
1298 if (actor == mHandleTwoGrabArea)
1300 // the displacement in PanGesture is affected by the actor's rotation.
1301 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1302 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1304 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1306 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1308 ShowPopupCutCopyPaste();
1317 // Stop the flashing animation so easy to see when moved.
1318 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1320 if (touch.GetPoint(0).state == TouchPoint::Down)
1322 SetCursorVisibility( true );
1323 StopCursorBlinkTimer();
1325 else if (touch.GetPoint(0).state == TouchPoint::Up)
1327 SetCursorVisibility( true );
1328 StartCursorBlinkTimer();
1333 // selection handle one
1334 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1336 if (touch.GetPoint(0).state == TouchPoint::Down)
1338 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1340 else if (touch.GetPoint(0).state == TouchPoint::Up)
1342 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1347 // selection handle two
1348 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1350 if (touch.GetPoint(0).state == TouchPoint::Down)
1352 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1354 else if (touch.GetPoint(0).state == TouchPoint::Up)
1356 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1361 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1363 // If text exists then select nearest word.
1364 if ( !mStyledText.empty())
1368 ShowGrabHandleAndSetVisibility( false );
1373 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1374 // converts the pre-edit word being displayed to a committed word.
1375 if ( !mUnderlinedPriorToPreEdit )
1378 style.SetUnderline( false );
1379 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1381 mPreEditFlag = false;
1382 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1383 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1384 PreEditReset( false );
1386 mCursorPosition = 0;
1388 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1389 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1391 std::size_t start = 0;
1392 std::size_t end = 0;
1393 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1395 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1397 ImfManager imfManager = ImfManager::Get();
1400 imfManager.SetCursorPosition ( mCursorPosition );
1401 imfManager.NotifyCursorPosition();
1404 SelectText( start, end );
1406 // if no text but clipboard has content then show paste option
1407 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1409 ShowPopupCutCopyPaste();
1412 // If no text and clipboard empty then do nothing
1415 // TODO: Change the function name to be more general.
1416 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1418 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1419 , (mEditOnTouch)?"true":"false"
1420 , (mEditModeActive)?"true":"false");
1422 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1427 if( mGrabArea == actor )
1429 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1431 SetUpPopupSelection();
1441 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1443 // Initially don't create the grab handle.
1444 bool createGrabHandle = false;
1446 if ( !mEditModeActive )
1448 // update line height before calculate the actual position.
1451 // Only start edit mode if TextInput configured to edit on touch
1454 // Set the initial cursor position in the tap point.
1455 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1457 // Create the grab handle.
1458 // TODO Make this a re-usable function.
1459 if ( IsGrabHandleEnabled() )
1461 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1465 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1466 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1467 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1468 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1472 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1473 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1474 // otherwise the Grab handle will be shown when selecting.
1481 // Show the keyboard if it was hidden.
1482 if (!VirtualKeyboard::IsVisible())
1484 VirtualKeyboard::Show();
1487 // Reset keyboard as tap event has occurred.
1488 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1489 PreEditReset( true );
1491 GetTextLayoutInfo();
1493 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1495 // 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.
1497 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1499 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1501 // Notify keyboard so it can 're-capture' word for predictive text.
1502 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1503 ImfManager imfManager = ImfManager::Get();
1506 imfManager.SetCursorPosition ( mCursorPosition );
1507 imfManager.NotifyCursorPosition();
1509 const TextStyle oldInputStyle( mInputStyle );
1511 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1515 // Create the grab handle.
1516 // Grab handle is created later.
1517 createGrabHandle = true;
1519 if( oldInputStyle != mInputStyle )
1521 // Updates the line height accordingly with the input style.
1524 EmitStyleChangedSignal();
1529 if ( createGrabHandle && IsGrabHandleEnabled() )
1531 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1535 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1536 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1537 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1538 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1543 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1545 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1547 // Ignore longpress if in selection mode already
1548 if( mHighlightMeshActor )
1553 if(longPress.state == Dali::Gesture::Started)
1555 // Start edit mode on long press
1556 if ( !mEditModeActive )
1561 // If text exists then select nearest word.
1562 if ( !mStyledText.empty())
1566 ShowGrabHandleAndSetVisibility( false );
1571 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1572 // converts the pre-edit word being displayed to a committed word.
1573 if ( !mUnderlinedPriorToPreEdit )
1576 style.SetUnderline( false );
1577 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1579 mPreEditFlag = false;
1580 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1581 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1582 PreEditReset( false );
1584 mCursorPosition = 0;
1586 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1587 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1589 std::size_t start = 0;
1590 std::size_t end = 0;
1591 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1593 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1595 ImfManager imfManager = ImfManager::Get();
1598 imfManager.SetCursorPosition ( mCursorPosition );
1599 imfManager.NotifyCursorPosition();
1602 SelectText( start, end );
1605 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1606 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1608 ShowPopupCutCopyPaste();
1613 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1615 const Text clipboardText( notifier.GetContent() );
1616 PasteText( clipboardText );
1618 SetCursorVisibility( true );
1619 StartCursorBlinkTimer();
1621 ShowGrabHandleAndSetVisibility( false );
1627 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1629 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1631 const std::string& name = button.GetName();
1633 if(name == TextInputPopup::OPTION_SELECT_WORD)
1635 std::size_t start = 0;
1636 std::size_t end = 0;
1637 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1639 SelectText( start, end );
1641 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1643 SetCursorVisibility(false);
1644 StopCursorBlinkTimer();
1646 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1647 std::size_t start = 0;
1649 SelectText( start, end );
1651 else if(name == TextInputPopup::OPTION_CUT)
1653 bool ret = CopySelectedTextToClipboard();
1657 DeleteHighlightedText( true );
1661 SetCursorVisibility( true );
1662 StartCursorBlinkTimer();
1666 else if(name == TextInputPopup::OPTION_COPY)
1668 CopySelectedTextToClipboard();
1672 SetCursorVisibility( true );
1673 StartCursorBlinkTimer();
1677 else if(name == TextInputPopup::OPTION_PASTE)
1679 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1681 PasteText(retrievedString);
1683 SetCursorVisibility( true );
1684 StartCursorBlinkTimer();
1686 ShowGrabHandleAndSetVisibility( false );
1690 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1692 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1693 // Hence pass the false parameter for signalFinished.
1694 HidePopup( true, false );
1695 mClipboard.ShowClipboard();
1701 bool TextInput::OnCursorBlinkTimerTick()
1704 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1705 if ( mCursorRTLEnabled )
1707 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1709 mCursorBlinkStatus = !mCursorBlinkStatus;
1714 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1716 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1718 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1719 if(mHighlightMeshActor && mState == StateEdit)
1721 ShowPopupCutCopyPaste();
1725 //FIXME this routine needs to be re-written as it contains too many branches.
1726 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1728 std::string keyName = event.keyPressedName;
1729 std::string keyString = event.keyPressed;
1731 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1733 // Do not consume "Tab" and "Escape" keys.
1734 if(keyName == "Tab" || keyName == "Escape")
1736 // Escape key to end the edit mode
1742 HidePopup(); // If Pop-up shown then hides it as editing text.
1744 // Update Flag, indicates whether to update the text-input contents or not.
1745 // Any key stroke that results in a visual change of the text-input should
1746 // set this flag to true.
1749 // Whether to scroll text to cursor position.
1750 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1751 bool scroll = false;
1753 if (keyName == "Return")
1755 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1757 bool preEditFlagPreviouslySet( mPreEditFlag );
1759 // replaces highlighted text with new line
1760 DeleteHighlightedText( false );
1762 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1764 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1765 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1768 mCommitByKeyInput = true;
1771 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1772 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1774 mPreEditFlag = true;
1775 mIgnoreCommitFlag = false;
1785 else if ( keyName == "space" )
1787 if ( mHighlightMeshActor )
1789 // Some text is selected so erase it before adding space.
1790 DeleteHighlightedText( true );
1794 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1796 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1797 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1800 mCommitByKeyInput = true;
1805 else if (keyName == "BackSpace")
1807 if ( mHighlightMeshActor )
1809 // Some text is selected so erase it
1810 DeleteHighlightedText( true );
1815 if ( mCursorPosition > 0 )
1817 DeleteCharacter( mCursorPosition );
1823 else if (keyName == "Right")
1828 else if (keyName == "Left")
1830 AdvanceCursor(true);
1833 else // event is a character
1835 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1836 if ( !keyString.empty() )
1838 // replaces highlighted text with new character
1839 DeleteHighlightedText( false );
1841 // Received key String
1842 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1848 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1849 // as this is a costly operation.
1855 if(update || scroll)
1857 if( IsScrollEnabled() )
1859 // Calculates the new cursor position (in actor coordinates)
1860 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1862 ScrollTextViewToMakeCursorVisible( cursorPosition );
1869 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1871 std::string keyName = event.keyPressedName;
1872 std::string keyString = event.keyPressed;
1874 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1876 // The selected text become deselected when the key code is DALI_KEY_BACK.
1877 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1886 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1888 // Updates the stored scroll position.
1889 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1891 const Vector3& controlSize = GetControlSize();
1892 Size cursorSize( CURSOR_THICKNESS, 0.f );
1894 // Updates the cursor and grab handle position and visibility.
1895 if( mGrabHandle || mCursor )
1897 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1898 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1900 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1902 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1906 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1907 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1912 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1913 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1917 // Updates the selection handles and highlighted text position and visibility.
1918 if( mSelectionHandleOne && mSelectionHandleTwo )
1920 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1921 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1922 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1923 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1924 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1925 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1927 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1928 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1930 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1931 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1932 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1933 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1935 if( mHighlightMeshActor )
1937 mHighlightMeshActor.SetVisible( true );
1943 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1945 // Scroll the text to make the cursor visible.
1946 const Size cursorSize( CURSOR_THICKNESS,
1947 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1949 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1951 const Vector3& controlSize = GetControlSize();
1953 // Calculates the new scroll position.
1954 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1955 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1957 scrollOffset.x += cursorPosition.x;
1960 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1962 scrollOffset.y += cursorPosition.y;
1965 // Sets the new scroll position.
1966 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1967 SetScrollPosition( scrollOffset );
1970 void TextInput::StartScrollTimer()
1974 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1975 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1978 if( !mScrollTimer.IsRunning() )
1980 mScrollTimer.Start();
1984 void TextInput::StopScrollTimer()
1988 mScrollTimer.Stop();
1992 bool TextInput::OnScrollTimerTick()
1994 // TODO: need to set the new style accordingly the new handle position.
1996 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1998 // nothing to do if all handles are invisible or doesn't exist.
2004 // Choose between the grab handle or the selection handles.
2005 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2006 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2007 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2009 std::size_t newCursorPosition = 0;
2010 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2012 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2013 // the new selection handle's position needs to be different of the other one.
2014 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2015 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2016 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2018 if( differentSelectionHandles )
2020 handlePosition = newCursorPosition;
2022 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2024 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2026 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2027 scrollPosition += scrollDelta;
2028 SetScrollPosition( scrollPosition );
2030 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2035 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2038 actualHandlePosition.x += mScrollDisplacement.x;
2039 actualHandlePosition.y += mScrollDisplacement.y;
2044 // Public Internal Methods (public for testing purpose)
2046 void TextInput::SetUpTouchEvents()
2048 if ( !mTapDetector )
2050 mTapDetector = TapGestureDetector::New();
2051 // Attach the actors and connect the signal
2052 mTapDetector.Attach(Self());
2054 // As contains children which may register for tap the default control detector is not used.
2055 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2058 if ( !mDoubleTapDetector )
2060 mDoubleTapDetector = TapGestureDetector::New();
2061 mDoubleTapDetector.SetTapsRequired( 2 );
2062 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2064 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2065 // so that we do not, unnecessarily, have a double tap request all the time
2068 if ( !mPanGestureDetector )
2070 mPanGestureDetector = PanGestureDetector::New();
2071 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2074 if ( !mLongPressDetector )
2076 mLongPressDetector = LongPressGestureDetector::New();
2077 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2078 mLongPressDetector.Attach(Self());
2082 void TextInput::CreateTextViewActor()
2084 mDisplayedTextView = Toolkit::TextView::New();
2085 mDisplayedTextView.SetName( "DisplayedTextView ");
2086 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2087 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2088 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2089 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2090 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2091 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2092 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2093 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2094 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2095 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2097 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2099 Self().Add( mDisplayedTextView );
2102 // Start a timer to initiate, used by the cursor to blink.
2103 void TextInput::StartCursorBlinkTimer()
2105 if ( !mCursorBlinkTimer )
2107 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2108 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2111 if ( !mCursorBlinkTimer.IsRunning() )
2113 mCursorBlinkTimer.Start();
2117 // Start a timer to initiate, used by the cursor to blink.
2118 void TextInput::StopCursorBlinkTimer()
2120 if ( mCursorBlinkTimer )
2122 mCursorBlinkTimer.Stop();
2126 void TextInput::StartEditMode()
2128 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2130 if(!mEditModeActive)
2135 if ( mDoubleTapDetector )
2137 mDoubleTapDetector.Attach( Self() );
2141 void TextInput::EndEditMode()
2143 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2145 ClearKeyInputFocus();
2147 if ( mDoubleTapDetector )
2149 mDoubleTapDetector.Detach( Self() );
2153 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2155 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2157 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2159 style.SetUnderline( true );
2160 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2164 void TextInput::RemovePreEditStyle()
2166 if ( !mUnderlinedPriorToPreEdit )
2169 style.SetUnderline( false );
2170 SetActiveStyle( style, TextStyle::UNDERLINE );
2174 // IMF related methods
2177 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2179 bool update( false );
2180 bool preeditResetRequired ( false );
2182 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2184 HidePopup(); // If Pop-up shown then hides it as editing text.
2187 switch ( imfEvent.eventName )
2189 case ImfManager::PREEDIT:
2191 mIgnoreFirstCommitFlag = false;
2193 // 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
2194 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2196 // replaces highlighted text with new character
2197 DeleteHighlightedText( false );
2200 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2202 if( IsScrollEnabled() )
2204 // Calculates the new cursor position (in actor coordinates)
2205 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2206 ScrollTextViewToMakeCursorVisible( cursorPosition );
2213 case ImfManager::COMMIT:
2215 if( mIgnoreFirstCommitFlag )
2217 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2218 mIgnoreFirstCommitFlag = false;
2222 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2224 // 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
2225 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2227 // replaces highlighted text with new character
2228 DeleteHighlightedText( false );
2231 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2232 // not needed, one such scenario is when the pre-edit word is too long to fit.
2233 if ( !mIgnoreCommitFlag )
2235 update = CommitReceived( imfEvent.predictiveString );
2239 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2245 if( IsScrollEnabled() )
2247 // Calculates the new cursor position (in actor coordinates)
2248 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2250 ScrollTextViewToMakeCursorVisible( cursorPosition );
2255 case ImfManager::DELETESURROUNDING:
2257 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2258 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2260 mPreEditFlag = false;
2262 std::size_t toDelete = 0;
2263 std::size_t numberOfCharacters = 0;
2265 if( mHighlightMeshActor )
2267 // delete highlighted text.
2268 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2269 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2273 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2275 toDelete = mCursorPosition + imfEvent.cursorOffset;
2277 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2279 numberOfCharacters = mStyledText.size() - toDelete;
2283 numberOfCharacters = imfEvent.numberOfChars;
2286 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2287 DeleteRange( toDelete, numberOfCharacters );
2289 mCursorPosition = toDelete;
2290 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2294 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2297 case ImfManager::GETSURROUNDING:
2299 // 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
2300 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2301 if (! ( mHighlightMeshActor || mSelectingText ) )
2303 std::string text( GetText() );
2304 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2306 imfManager.SetCursorPosition( mCursorPosition );
2307 imfManager.SetSurroundingText( text );
2310 if( 0 != mNumberOfSurroundingCharactersDeleted )
2312 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2313 mNumberOfSurroundingCharactersDeleted = 0;
2315 if( mStyledText.empty() )
2317 // Styled text is empty, so set the placeholder text.
2318 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2319 mPlaceHolderSet = true;
2324 case ImfManager::VOID:
2326 DALI_ASSERT_DEBUG( false );
2330 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2332 return callbackData;
2335 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2337 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2339 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2340 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2342 bool preeditResetRequest ( false );
2344 if( mPreEditFlag ) // Already in pre-edit state.
2346 if( mStyledText.size() >= mMaxStringLength )
2348 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2349 // Cannot fit these characters into field, clear pre-edit.
2350 if ( !mUnderlinedPriorToPreEdit )
2353 style.SetUnderline( false );
2354 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2356 mIgnoreCommitFlag = true;
2357 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2358 mPreEditFlag = false;
2359 EmitMaxInputCharactersReachedSignal();
2363 // delete existing pre-edit string
2364 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2366 // Store new pre-edit string
2367 mPreEditString.SetText( keyString );
2369 if ( keyString.empty() )
2371 mPreEditFlag = false;
2372 mCursorPosition = mPreEditStartPosition;
2374 if( mStyledText.empty() )
2376 // Styled text is empty, so set the placeholder text.
2377 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2378 mPlaceHolderSet = true;
2382 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2384 GetTextLayoutInfo();
2389 // Insert new pre-edit string. InsertAt updates the size and position table.
2390 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2391 // 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.
2392 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2393 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2394 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2397 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2401 else // mPreEditFlag not set
2403 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2405 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2406 // new pre-edit so move into pre-edit state by setting flag
2407 mPreEditFlag = true;
2408 mPreEditString.SetText( keyString ); // store new pre-edit string
2409 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2410 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2411 // 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.
2412 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2413 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2414 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2415 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2421 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2425 return preeditResetRequest;
2428 bool TextInput::CommitReceived(const std::string& keyString )
2430 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2431 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2433 bool update( false );
2435 RemovePreEditStyle();
2437 const std::size_t styledTextSize( mStyledText.size() );
2438 if( styledTextSize >= mMaxStringLength )
2440 // Cannot fit these characters into field, clear pre-edit.
2443 mIgnoreCommitFlag = true;
2444 mPreEditFlag = false;
2446 EmitMaxInputCharactersReachedSignal();
2452 // delete existing pre-edit string
2453 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2454 mPreEditFlag = false;
2456 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2457 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2459 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2461 // No need to update cursor position as Cursor location given by touch.
2462 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2463 mPreserveCursorPosition = false;
2467 // Cursor not set by touch so needs to be re-positioned to input more text
2468 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2470 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2471 if ( mCommitByKeyInput )
2473 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2474 mCommitByKeyInput = false;
2480 if ( mSelectTextOnCommit )
2482 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2487 else // mPreEditFlag not set
2489 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2491 if( mStyledText.empty() && mPlaceHolderSet )
2493 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2494 mDisplayedTextView.SetText( "" );
2495 mNumberOfSurroundingCharactersDeleted = 0;
2496 mPlaceHolderSet = false;
2498 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2500 mNumberOfSurroundingCharactersDeleted = 0;
2505 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2510 mSelectTextOnCommit = false;
2512 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2513 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2518 // End of IMF related methods
2520 std::size_t TextInput::DeletePreEdit()
2522 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2524 DALI_ASSERT_DEBUG( mPreEditFlag );
2526 const std::size_t preEditStringLength = mPreEditString.GetLength();
2527 const std::size_t styledTextSize = mStyledText.size();
2529 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2531 // Prevents erase items outside mStyledText bounds.
2532 if( mPreEditStartPosition > styledTextSize )
2534 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2535 mPreEditStartPosition = styledTextSize;
2538 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2540 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2541 endPosition = styledTextSize;
2544 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2546 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2547 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2549 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2551 return preEditStringLength;
2554 void TextInput::PreEditReset( bool preserveCursorPosition )
2556 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2557 preserveCursorPosition, mCursorPosition);
2559 // 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.
2560 mPreserveCursorPosition = preserveCursorPosition;
2562 // Reset incase we are in a pre-edit state.
2563 ImfManager imfManager = ImfManager::Get();
2566 imfManager.Reset(); // Will trigger a commit message
2570 void TextInput::CursorUpdate()
2574 ImfManager imfManager = ImfManager::Get();
2577 std::string text( GetText() );
2578 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2579 imfManager.SetCursorPosition ( mCursorPosition );
2580 imfManager.NotifyCursorPosition();
2584 /* Delete highlighted characters redisplay*/
2585 void TextInput::DeleteHighlightedText( bool inheritStyle )
2587 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2589 if( mHighlightMeshActor )
2591 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2593 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2594 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2596 // Get the styled text of the characters to be deleted as it may be needed if
2597 // the "exceed the text-input's boundaries" option is disabled.
2598 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2600 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2602 mStyledText.erase( start, end ); // erase range of characters
2604 // Remove text from TextView.
2606 if( mStyledText.empty() )
2608 // Styled text is empty, so set the placeholder text.
2609 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2610 mPlaceHolderSet = true;
2614 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2616 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2618 // It may happen than after removing a white space or a new line character,
2619 // two words merge, this new word could be big enough to not fit in its
2620 // current line, so moved to the next one, and make some part of the text to
2621 // exceed the text-input's boundary.
2622 if( !mExceedEnabled )
2624 // Get the new text layout after removing some characters.
2625 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2627 // Get text-input's size.
2628 const Vector3& size = GetControlSize();
2630 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2631 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2633 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2635 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2636 styledCharactersToDelete.begin(),
2637 styledCharactersToDelete.end() );
2641 GetTextLayoutInfo();
2649 const TextStyle oldInputStyle( mInputStyle );
2651 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2653 if( oldInputStyle != mInputStyle )
2655 // Updates the line height accordingly with the input style.
2658 EmitStyleChangedSignal();
2664 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2666 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2667 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2669 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2672 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2674 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2675 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2677 mStyledText.erase(itStart, itEnd);
2679 // update the selection handles if they are visible.
2680 if( mHighlightMeshActor )
2682 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2683 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2685 if( minHandle >= start + ncharacters )
2687 minHandle -= ncharacters;
2689 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2694 if( maxHandle >= start + ncharacters )
2696 maxHandle -= ncharacters;
2698 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2704 // 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.
2707 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2709 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2710 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2711 // Mean we do not re-draw the text more than we have too.
2714 /* Delete character at current cursor position and redisplay*/
2715 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2717 // Ensure positionToDelete is not out of bounds.
2718 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2719 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2720 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2722 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2725 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2727 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2729 // Get the styled text of the character to be deleted as it may be needed if
2730 // the "exceed the text-input's boundaries" option is disabled.
2731 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2733 mStyledText.erase(it); // erase the character left of positionToDelete
2735 if( mStyledText.empty() )
2737 // Styled text is empty, so set the placeholder text.
2738 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2739 mPlaceHolderSet = true;
2743 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2745 const Character characterToDelete = styledCharacterToDelete.mText[0];
2747 // It may happen than after removing a white space or a new line character,
2748 // two words merge, this new word could be big enough to not fit in its
2749 // current line, so moved to the next one, and make some part of the text to
2750 // exceed the text-input's boundary.
2751 if( !mExceedEnabled )
2753 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2755 // Get the new text layout after removing one character.
2756 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2758 // Get text-input's size.
2759 const Vector3& size = GetControlSize();
2761 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2762 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2764 MarkupProcessor::StyledTextArray array;
2765 array.push_back( styledCharacterToDelete );
2766 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2768 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2773 GetTextLayoutInfo();
2775 ShowGrabHandleAndSetVisibility( false );
2777 mCursorPosition = positionToDelete -1;
2779 const TextStyle oldInputStyle( mInputStyle );
2781 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2783 if( oldInputStyle != mInputStyle )
2785 // Updates the line height accordingly with the input style.
2788 EmitStyleChangedSignal();
2793 /*Insert new character into the string and (optionally) redisplay text-input*/
2794 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2796 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2798 // Ensure insertionPosition is not out of bounds.
2799 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2801 bool textExceedsMaximunNumberOfCharacters = false;
2802 bool textExceedsBoundary = false;
2803 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2805 ShowGrabHandleAndSetVisibility( false );
2807 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2811 mIgnoreCommitFlag = true;
2812 mPreEditFlag = false;
2813 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2814 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2817 if( textExceedsMaximunNumberOfCharacters )
2819 EmitMaxInputCharactersReachedSignal();
2822 if( textExceedsBoundary )
2824 EmitInputTextExceedsBoundariesSignal();
2825 PreEditReset( false );
2829 return insertedStringLength;
2832 ImageActor TextInput::CreateCursor( const Vector4& color)
2835 cursor = CreateSolidColorActor(color);
2836 cursor.SetName( "Cursor" );
2838 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2839 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2840 cursor.SetVisible(false);
2845 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2847 // As cursor is not moving due to grab handle, handle should be hidden.
2848 ShowGrabHandleAndSetVisibility( false );
2850 bool cursorPositionChanged = false;
2853 if ( mCursorPosition >= places )
2855 mCursorPosition = mCursorPosition - places;
2856 cursorPositionChanged = true;
2861 if ((mCursorPosition + places) <= mStyledText.size())
2863 mCursorPosition = mCursorPosition + places;
2864 cursorPositionChanged = true;
2868 if( cursorPositionChanged )
2870 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2872 const TextStyle oldInputStyle( mInputStyle );
2873 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2877 if( oldInputStyle != mInputStyle )
2879 // Updates the line height accordingly with the input style.
2882 EmitStyleChangedSignal();
2885 ImfManager imfManager = ImfManager::Get();
2888 imfManager.SetCursorPosition ( mCursorPosition );
2889 imfManager.NotifyCursorPosition();
2894 void TextInput::DrawCursor(const std::size_t nthChar)
2896 // Get height of cursor and set its size
2897 Size size( CURSOR_THICKNESS, 0.0f );
2898 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2900 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2904 // Measure Font so know how big text will be if no initial text to measure.
2905 size.height = mLineHeight;
2908 mCursor.SetSize(size);
2910 // If the character is italic then the cursor also tilts.
2911 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2913 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2915 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2917 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2918 bool altPositionValid; // Alternate cursor validity flag.
2919 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2920 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2922 SetAltCursorEnabled( altPositionValid );
2924 if(!altPositionValid)
2926 mCursor.SetPosition( position + UI_OFFSET );
2930 size.height *= 0.5f;
2931 mCursor.SetSize(size);
2932 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2934 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2935 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2936 size.height = rowSize.height * 0.5f;
2937 mCursorRTL.SetSize(size);
2938 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2941 if( IsScrollEnabled() )
2943 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2944 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2949 void TextInput::SetAltCursorEnabled( bool enabled )
2951 mCursorRTLEnabled = enabled;
2952 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2955 void TextInput::SetCursorVisibility( bool visible )
2957 mCursorVisibility = visible;
2958 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2959 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2962 void TextInput::CreateGrabHandle( Dali::Image image )
2968 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2972 mGrabHandleImage = image;
2975 mGrabHandle = ImageActor::New(mGrabHandleImage);
2976 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2977 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2979 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2981 ShowGrabHandleAndSetVisibility( false );
2983 CreateGrabArea( mGrabHandle );
2985 mActiveLayer.Add(mGrabHandle);
2989 void TextInput::CreateGrabArea( Actor& parent )
2991 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2992 mGrabArea.SetName( "GrabArea" );
2993 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2994 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2995 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2996 mTapDetector.Attach( mGrabArea );
2997 mPanGestureDetector.Attach( mGrabArea );
2998 mLongPressDetector.Attach( mGrabArea );
3000 parent.Add(mGrabArea);
3003 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3005 Vector3 actualHandlePosition;
3009 mActualGrabHandlePosition.x += displacement.x;
3010 mActualGrabHandlePosition.y += displacement.y;
3012 // Grab handle should jump to the nearest character and take cursor with it
3013 std::size_t newCursorPosition = 0;
3014 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3016 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3018 bool handleVisible = true;
3020 if( IsScrollEnabled() )
3022 const Vector3 controlSize = GetControlSize();
3023 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3024 // Scrolls the text if the handle is not in a visible position
3025 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3032 mCurrentHandlePosition = actualHandlePosition;
3033 mScrollDisplacement = Vector2::ZERO;
3037 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3039 mScrollDisplacement.x = -SCROLL_SPEED;
3041 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3043 mScrollDisplacement.x&nbs