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);
285 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();
1156 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1157 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1158 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1160 self.Add( mActiveLayer );
1161 mActiveLayer.RaiseToTop();
1164 void TextInput::OnInitialize()
1166 CreateTextViewActor();
1170 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1171 // different positions depending on language)
1172 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1173 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1175 Actor self = Self();
1176 self.Add( mCursor );
1177 self.Add( mCursorRTL );
1179 mCursorVisibility = false;
1181 CreateActiveLayer(); // todo move this so layer only created when needed.
1183 // Assign names to image actors
1184 mCursor.SetName("mainCursor");
1185 mCursorRTL.SetName("rtlCursor");
1188 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1190 mDisplayedTextView.SetSize( targetSize );
1191 GetTextLayoutInfo();
1192 mActiveLayer.SetSize(targetSize);
1195 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1197 Relayout( mDisplayedTextView, size, container );
1198 Relayout( mPopupPanel.GetRootActor(), size, container );
1200 GetTextLayoutInfo();
1205 Vector3 TextInput::GetNaturalSize()
1207 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1209 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1211 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1212 naturalSize.height = mLineHeight;
1218 float TextInput::GetHeightForWidth( float width )
1220 float height = mDisplayedTextView.GetHeightForWidth( width );
1222 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1224 // If the height is zero, it means there is no text. Let's return the cursor height.
1225 height = mLineHeight;
1231 /*end of Virtual methods from parent*/
1233 // Private Internal methods
1235 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1237 switch (gesture.state)
1239 case Gesture::Started:
1240 // fall through so code not duplicated
1241 case Gesture::Continuing:
1243 if (actor == mGrabArea)
1245 SetCursorVisibility( true );
1246 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1247 MoveGrabHandle( gesture.displacement );
1248 HidePopup(); // Do not show popup whilst handle is moving
1250 else if (actor == mHandleOneGrabArea)
1252 // the displacement in PanGesture is affected by the actor's rotation.
1253 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1254 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1256 MoveSelectionHandle( HandleOne, gesture.displacement );
1258 mState = StateDraggingHandle;
1261 else if (actor == mHandleTwoGrabArea)
1263 // the displacement in PanGesture is affected by the actor's rotation.
1264 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1265 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1267 MoveSelectionHandle( HandleTwo, gesture.displacement );
1269 mState = StateDraggingHandle;
1275 case Gesture::Finished:
1277 // Revert back to non-pressed selection handle images
1278 if (actor == mGrabArea)
1280 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1281 SetCursorVisibility( true );
1282 SetUpPopupSelection();
1285 if (actor == mHandleOneGrabArea)
1287 // the displacement in PanGesture is affected by the actor's rotation.
1288 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1289 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1291 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1293 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1295 ShowPopupCutCopyPaste();
1297 if (actor == mHandleTwoGrabArea)
1299 // the displacement in PanGesture is affected by the actor's rotation.
1300 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1301 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1303 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1305 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1307 ShowPopupCutCopyPaste();
1316 // Stop the flashing animation so easy to see when moved.
1317 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1319 if (touch.GetPoint(0).state == TouchPoint::Down)
1321 SetCursorVisibility( true );
1322 StopCursorBlinkTimer();
1324 else if (touch.GetPoint(0).state == TouchPoint::Up)
1326 SetCursorVisibility( true );
1327 StartCursorBlinkTimer();
1332 // selection handle one
1333 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1335 if (touch.GetPoint(0).state == TouchPoint::Down)
1337 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1339 else if (touch.GetPoint(0).state == TouchPoint::Up)
1341 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1346 // selection handle two
1347 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1349 if (touch.GetPoint(0).state == TouchPoint::Down)
1351 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1353 else if (touch.GetPoint(0).state == TouchPoint::Up)
1355 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1360 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1362 // If text exists then select nearest word.
1363 if ( !mStyledText.empty())
1367 ShowGrabHandleAndSetVisibility( false );
1372 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1373 // converts the pre-edit word being displayed to a committed word.
1374 if ( !mUnderlinedPriorToPreEdit )
1377 style.SetUnderline( false );
1378 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1380 mPreEditFlag = false;
1381 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1382 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1383 PreEditReset( false );
1385 mCursorPosition = 0;
1387 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1388 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1390 ImfManager imfManager = ImfManager::Get();
1393 imfManager.SetCursorPosition ( mCursorPosition );
1394 imfManager.NotifyCursorPosition();
1397 std::size_t start = 0;
1398 std::size_t end = 0;
1399 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1401 SelectText( start, end );
1403 // if no text but clipboard has content then show paste option
1404 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1406 ShowPopupCutCopyPaste();
1409 // If no text and clipboard empty then do nothing
1412 // TODO: Change the function name to be more general.
1413 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1415 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1416 , (mEditOnTouch)?"true":"false"
1417 , (mEditModeActive)?"true":"false");
1419 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1424 if( mGrabArea == actor )
1426 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1428 SetUpPopupSelection();
1438 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1440 // Initially don't create the grab handle.
1441 bool createGrabHandle = false;
1443 if ( !mEditModeActive )
1445 // update line height before calculate the actual position.
1448 // Only start edit mode if TextInput configured to edit on touch
1451 // Set the initial cursor position in the tap point.
1452 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1454 // Create the grab handle.
1455 // TODO Make this a re-usable function.
1456 if ( IsGrabHandleEnabled() )
1458 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1462 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1463 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1464 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1465 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1469 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1470 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1471 // otherwise the Grab handle will be shown when selecting.
1478 // Show the keyboard if it was hidden.
1479 if (!VirtualKeyboard::IsVisible())
1481 VirtualKeyboard::Show();
1484 // Reset keyboard as tap event has occurred.
1485 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1486 PreEditReset( true );
1488 GetTextLayoutInfo();
1490 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1492 // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1494 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1496 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1498 // Notify keyboard so it can 're-capture' word for predictive text.
1499 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1500 ImfManager imfManager = ImfManager::Get();
1503 imfManager.SetCursorPosition ( mCursorPosition );
1504 imfManager.NotifyCursorPosition();
1506 const TextStyle oldInputStyle( mInputStyle );
1508 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1512 // Create the grab handle.
1513 // Grab handle is created later.
1514 createGrabHandle = true;
1516 if( oldInputStyle != mInputStyle )
1518 // Updates the line height accordingly with the input style.
1521 EmitStyleChangedSignal();
1526 if ( createGrabHandle && IsGrabHandleEnabled() )
1528 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1532 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1533 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1534 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1535 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1540 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1542 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1544 if(longPress.state == Dali::Gesture::Started)
1546 // Start edit mode on long press
1547 if ( !mEditModeActive )
1552 // If text exists then select nearest word.
1553 if ( !mStyledText.empty())
1557 ShowGrabHandleAndSetVisibility( false );
1562 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1563 // converts the pre-edit word being displayed to a committed word.
1564 if ( !mUnderlinedPriorToPreEdit )
1567 style.SetUnderline( false );
1568 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1570 mPreEditFlag = false;
1571 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1572 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1573 PreEditReset( false );
1575 mCursorPosition = 0;
1577 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1578 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1580 ImfManager imfManager = ImfManager::Get();
1583 imfManager.SetCursorPosition ( mCursorPosition );
1584 imfManager.NotifyCursorPosition();
1586 std::size_t start = 0;
1587 std::size_t end = 0;
1588 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1590 SelectText( start, end );
1593 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1594 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1596 ShowPopupCutCopyPaste();
1601 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1603 const Text clipboardText( notifier.GetContent() );
1604 PasteText( clipboardText );
1606 SetCursorVisibility( true );
1607 StartCursorBlinkTimer();
1609 ShowGrabHandleAndSetVisibility( false );
1615 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1617 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1619 const std::string& name = button.GetName();
1621 if(name == TextInputPopup::OPTION_SELECT_WORD)
1623 std::size_t start = 0;
1624 std::size_t end = 0;
1625 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1627 SelectText( start, end );
1629 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1631 SetCursorVisibility(false);
1632 StopCursorBlinkTimer();
1634 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1635 std::size_t start = 0;
1637 SelectText( start, end );
1639 else if(name == TextInputPopup::OPTION_CUT)
1641 bool ret = CopySelectedTextToClipboard();
1645 DeleteHighlightedText( true );
1649 SetCursorVisibility( true );
1650 StartCursorBlinkTimer();
1654 else if(name == TextInputPopup::OPTION_COPY)
1656 CopySelectedTextToClipboard();
1660 SetCursorVisibility( true );
1661 StartCursorBlinkTimer();
1665 else if(name == TextInputPopup::OPTION_PASTE)
1667 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1669 PasteText(retrievedString);
1671 SetCursorVisibility( true );
1672 StartCursorBlinkTimer();
1674 ShowGrabHandleAndSetVisibility( false );
1678 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1680 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1681 // Hence pass the false parameter for signalFinished.
1682 HidePopup( true, false );
1683 mClipboard.ShowClipboard();
1689 bool TextInput::OnCursorBlinkTimerTick()
1692 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1693 if ( mCursorRTLEnabled )
1695 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1697 mCursorBlinkStatus = !mCursorBlinkStatus;
1702 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1704 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1706 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1707 if(mHighlightMeshActor && mState == StateEdit)
1709 ShowPopupCutCopyPaste();
1713 //FIXME this routine needs to be re-written as it contains too many branches.
1714 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1716 std::string keyName = event.keyPressedName;
1717 std::string keyString = event.keyPressed;
1719 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1721 // Do not consume "Tab" and "Escape" keys.
1722 if(keyName == "Tab" || keyName == "Escape")
1724 // Escape key to end the edit mode
1730 HidePopup(); // If Pop-up shown then hides it as editing text.
1732 // Update Flag, indicates whether to update the text-input contents or not.
1733 // Any key stroke that results in a visual change of the text-input should
1734 // set this flag to true.
1737 // Whether to scroll text to cursor position.
1738 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1739 bool scroll = false;
1741 if (keyName == "Return")
1743 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1745 bool preEditFlagPreviouslySet( mPreEditFlag );
1747 if (mHighlightMeshActor)
1749 // replaces highlighted text with new line
1750 DeleteHighlightedText( false );
1752 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1754 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1755 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1758 mCommitByKeyInput = true;
1761 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1762 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1764 mPreEditFlag = true;
1765 mIgnoreCommitFlag = false;
1775 else if ( keyName == "space" )
1777 if ( mHighlightMeshActor )
1779 // Some text is selected so erase it before adding space.
1780 DeleteHighlightedText( true );
1784 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1786 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1787 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1790 mCommitByKeyInput = true;
1795 else if (keyName == "BackSpace")
1797 if ( mHighlightMeshActor )
1799 // Some text is selected so erase it
1800 DeleteHighlightedText( true );
1805 if ( mCursorPosition > 0 )
1807 DeleteCharacter( mCursorPosition );
1813 else if (keyName == "Right")
1818 else if (keyName == "Left")
1820 AdvanceCursor(true);
1823 else // event is a character
1825 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1826 if ( !keyString.empty() )
1828 if ( mHighlightMeshActor )
1830 // replaces highlighted text with new character
1831 DeleteHighlightedText( false );
1835 // Received key String
1836 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1842 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1843 // as this is a costly operation.
1849 if(update || scroll)
1851 if( IsScrollEnabled() )
1853 // Calculates the new cursor position (in actor coordinates)
1854 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1856 ScrollTextViewToMakeCursorVisible( cursorPosition );
1863 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1865 std::string keyName = event.keyPressedName;
1866 std::string keyString = event.keyPressed;
1868 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1870 // The selected text become deselected when the key code is DALI_KEY_BACK.
1871 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1880 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1882 // Updates the stored scroll position.
1883 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1885 const Vector3& controlSize = GetControlSize();
1886 Size cursorSize( CURSOR_THICKNESS, 0.f );
1888 // Updates the cursor and grab handle position and visibility.
1889 if( mGrabHandle || mCursor )
1891 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1892 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1894 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1896 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1900 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1901 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1906 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1907 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1911 // Updates the selection handles and highlighted text position and visibility.
1912 if( mSelectionHandleOne && mSelectionHandleTwo )
1914 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1915 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1916 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1917 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1918 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1919 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1921 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1922 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1924 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1925 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1926 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1927 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1929 if( mHighlightMeshActor )
1931 mHighlightMeshActor.SetVisible( true );
1937 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1939 // Scroll the text to make the cursor visible.
1940 const Size cursorSize( CURSOR_THICKNESS,
1941 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1943 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1945 const Vector3& controlSize = GetControlSize();
1947 // Calculates the new scroll position.
1948 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1949 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1951 scrollOffset.x += cursorPosition.x;
1954 if( cursorPosition.y - cursorSize.height < 0.f )
1956 scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1958 else if( cursorPosition.y > controlSize.height )
1960 scrollOffset.y += cursorPosition.y;
1963 // Sets the new scroll position.
1964 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1965 SetScrollPosition( scrollOffset );
1968 void TextInput::StartScrollTimer()
1972 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1973 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1976 if( !mScrollTimer.IsRunning() )
1978 mScrollTimer.Start();
1982 void TextInput::StopScrollTimer()
1986 mScrollTimer.Stop();
1990 bool TextInput::OnScrollTimerTick()
1992 // TODO: need to set the new style accordingly the new handle position.
1994 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1996 // nothing to do if all handles are invisible or doesn't exist.
2002 // Choose between the grab handle or the selection handles.
2003 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2004 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2005 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2007 std::size_t newCursorPosition = 0;
2008 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2010 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2011 // the new selection handle's position needs to be different of the other one.
2012 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2013 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2014 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2016 if( differentSelectionHandles )
2018 handlePosition = newCursorPosition;
2020 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2022 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2024 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2025 scrollPosition += scrollDelta;
2026 SetScrollPosition( scrollPosition );
2028 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2033 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2036 actualHandlePosition.x += mScrollDisplacement.x;
2037 actualHandlePosition.y += mScrollDisplacement.y;
2042 // Public Internal Methods (public for testing purpose)
2044 void TextInput::SetUpTouchEvents()
2046 if ( !mTapDetector )
2048 mTapDetector = TapGestureDetector::New();
2049 // Attach the actors and connect the signal
2050 mTapDetector.Attach(Self());
2052 // As contains children which may register for tap the default control detector is not used.
2053 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2056 if ( !mDoubleTapDetector )
2058 mDoubleTapDetector = TapGestureDetector::New();
2059 mDoubleTapDetector.SetTapsRequired( 2 );
2060 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2062 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2063 // so that we do not, unnecessarily, have a double tap request all the time
2066 if ( !mPanGestureDetector )
2068 mPanGestureDetector = PanGestureDetector::New();
2069 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2072 if ( !mLongPressDetector )
2074 mLongPressDetector = LongPressGestureDetector::New();
2075 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2076 mLongPressDetector.Attach(Self());
2080 void TextInput::CreateTextViewActor()
2082 mDisplayedTextView = Toolkit::TextView::New();
2083 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2084 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2085 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2086 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2087 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2088 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2089 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2090 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2091 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2092 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2094 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2096 Self().Add( mDisplayedTextView );
2099 // Start a timer to initiate, used by the cursor to blink.
2100 void TextInput::StartCursorBlinkTimer()
2102 if ( !mCursorBlinkTimer )
2104 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2105 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2108 if ( !mCursorBlinkTimer.IsRunning() )
2110 mCursorBlinkTimer.Start();
2114 // Start a timer to initiate, used by the cursor to blink.
2115 void TextInput::StopCursorBlinkTimer()
2117 if ( mCursorBlinkTimer )
2119 mCursorBlinkTimer.Stop();
2123 void TextInput::StartEditMode()
2125 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2127 if(!mEditModeActive)
2132 if ( mDoubleTapDetector )
2134 mDoubleTapDetector.Attach( Self() );
2138 void TextInput::EndEditMode()
2140 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2142 ClearKeyInputFocus();
2144 if ( mDoubleTapDetector )
2146 mDoubleTapDetector.Detach( Self() );
2150 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2152 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2154 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2156 style.SetUnderline( true );
2157 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2161 void TextInput::RemovePreEditStyle()
2163 if ( !mUnderlinedPriorToPreEdit )
2166 style.SetUnderline( false );
2167 SetActiveStyle( style, TextStyle::UNDERLINE );
2171 // IMF related methods
2174 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2176 bool update( false );
2177 bool preeditResetRequired ( false );
2179 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2181 HidePopup(); // If Pop-up shown then hides it as editing text.
2184 switch ( imfEvent.eventName )
2186 case ImfManager::PREEDIT:
2188 mIgnoreFirstCommitFlag = false;
2190 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2191 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2193 // replaces highlighted text with new character
2194 DeleteHighlightedText( false );
2197 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2199 if( IsScrollEnabled() )
2201 // Calculates the new cursor position (in actor coordinates)
2202 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2203 ScrollTextViewToMakeCursorVisible( cursorPosition );
2210 case ImfManager::COMMIT:
2212 if( mIgnoreFirstCommitFlag )
2214 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2215 mIgnoreFirstCommitFlag = false;
2219 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2221 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2222 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2224 // replaces highlighted text with new character
2225 DeleteHighlightedText( false );
2228 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2229 // not needed, one such scenario is when the pre-edit word is too long to fit.
2230 if ( !mIgnoreCommitFlag )
2232 update = CommitReceived( imfEvent.predictiveString );
2236 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2242 if( IsScrollEnabled() )
2244 // Calculates the new cursor position (in actor coordinates)
2245 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2247 ScrollTextViewToMakeCursorVisible( cursorPosition );
2252 case ImfManager::DELETESURROUNDING:
2254 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2255 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2257 mPreEditFlag = false;
2259 std::size_t toDelete = 0;
2260 std::size_t numberOfCharacters = 0;
2262 if( mHighlightMeshActor )
2264 // delete highlighted text.
2265 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2266 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2270 if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2272 toDelete = mCursorPosition + imfEvent.cursorOffset;
2274 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2276 numberOfCharacters = mStyledText.size() - toDelete;
2280 numberOfCharacters = imfEvent.numberOfChars;
2283 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2284 DeleteRange( toDelete, numberOfCharacters );
2286 mCursorPosition = toDelete;
2287 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2291 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2294 case ImfManager::GETSURROUNDING:
2296 // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2297 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2298 if (! ( mHighlightMeshActor || mSelectingText ) )
2300 std::string text( GetText() );
2301 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2303 imfManager.SetCursorPosition( mCursorPosition );
2304 imfManager.SetSurroundingText( text );
2307 if( 0 != mNumberOfSurroundingCharactersDeleted )
2309 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2310 mNumberOfSurroundingCharactersDeleted = 0;
2312 if( mStyledText.empty() )
2314 // Styled text is empty, so set the placeholder text.
2315 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2316 mPlaceHolderSet = true;
2321 case ImfManager::VOID:
2323 DALI_ASSERT_DEBUG( false );
2327 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2329 return callbackData;
2332 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2334 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2336 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2337 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2339 bool preeditResetRequest ( false );
2341 if( mPreEditFlag ) // Already in pre-edit state.
2343 if( mStyledText.size() >= mMaxStringLength )
2345 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2346 // Cannot fit these characters into field, clear pre-edit.
2347 if ( !mUnderlinedPriorToPreEdit )
2350 style.SetUnderline( false );
2351 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2353 mIgnoreCommitFlag = true;
2354 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2355 mPreEditFlag = false;
2356 EmitMaxInputCharactersReachedSignal();
2360 // delete existing pre-edit string
2361 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2363 // Store new pre-edit string
2364 mPreEditString.SetText( keyString );
2366 if ( keyString.empty() )
2368 mPreEditFlag = false;
2369 mCursorPosition = mPreEditStartPosition;
2371 if( mStyledText.empty() )
2373 // Styled text is empty, so set the placeholder text.
2374 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2375 mPlaceHolderSet = true;
2379 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2381 GetTextLayoutInfo();
2386 // Insert new pre-edit string. InsertAt updates the size and position table.
2387 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2388 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2389 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2390 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2391 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2394 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2398 else // mPreEditFlag not set
2400 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2402 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2403 // new pre-edit so move into pre-edit state by setting flag
2404 mPreEditFlag = true;
2405 mPreEditString.SetText( keyString ); // store new pre-edit string
2406 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2407 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2408 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2409 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2410 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2411 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2412 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2418 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2422 return preeditResetRequest;
2425 bool TextInput::CommitReceived(const std::string& keyString )
2427 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2428 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2430 bool update( false );
2432 RemovePreEditStyle();
2434 const std::size_t styledTextSize( mStyledText.size() );
2435 if( styledTextSize >= mMaxStringLength )
2437 // Cannot fit these characters into field, clear pre-edit.
2440 mIgnoreCommitFlag = true;
2441 mPreEditFlag = false;
2443 EmitMaxInputCharactersReachedSignal();
2449 // delete existing pre-edit string
2450 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2451 mPreEditFlag = false;
2453 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2454 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2456 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2458 // No need to update cursor position as Cursor location given by touch.
2459 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2460 mPreserveCursorPosition = false;
2464 // Cursor not set by touch so needs to be re-positioned to input more text
2465 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2467 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2468 if ( mCommitByKeyInput )
2470 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2471 mCommitByKeyInput = false;
2477 if ( mSelectTextOnCommit )
2479 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2484 else // mPreEditFlag not set
2486 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2488 if( mStyledText.empty() && mPlaceHolderSet )
2490 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2491 mDisplayedTextView.SetText( "" );
2492 mNumberOfSurroundingCharactersDeleted = 0;
2493 mPlaceHolderSet = false;
2495 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2497 mNumberOfSurroundingCharactersDeleted = 0;
2502 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2507 mSelectTextOnCommit = false;
2509 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2510 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2515 // End of IMF related methods
2517 std::size_t TextInput::DeletePreEdit()
2519 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2521 DALI_ASSERT_DEBUG( mPreEditFlag );
2523 const std::size_t preEditStringLength = mPreEditString.GetLength();
2524 const std::size_t styledTextSize = mStyledText.size();
2526 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2528 // Prevents erase items outside mStyledText bounds.
2529 if( mPreEditStartPosition > styledTextSize )
2531 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2532 mPreEditStartPosition = styledTextSize;
2535 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2537 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2538 endPosition = styledTextSize;
2541 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2543 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2544 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2546 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2548 return preEditStringLength;
2551 void TextInput::PreEditReset( bool preserveCursorPosition )
2553 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2554 preserveCursorPosition, mCursorPosition);
2556 // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2557 mPreserveCursorPosition = preserveCursorPosition;
2559 // Reset incase we are in a pre-edit state.
2560 ImfManager imfManager = ImfManager::Get();
2563 imfManager.Reset(); // Will trigger a commit message
2567 void TextInput::CursorUpdate()
2571 ImfManager imfManager = ImfManager::Get();
2574 std::string text( GetText() );
2575 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2576 imfManager.SetCursorPosition ( mCursorPosition );
2577 imfManager.NotifyCursorPosition();
2581 /* Delete highlighted characters redisplay*/
2582 void TextInput::DeleteHighlightedText( bool inheritStyle )
2584 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2586 if(mHighlightMeshActor)
2588 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2590 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2591 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2593 // Get the styled text of the characters to be deleted as it may be needed if
2594 // the "exceed the text-input's boundaries" option is disabled.
2595 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2597 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2599 mStyledText.erase( start, end ); // erase range of characters
2601 // Remove text from TextView.
2603 if( mStyledText.empty() )
2605 // Styled text is empty, so set the placeholder text.
2606 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2607 mPlaceHolderSet = true;
2611 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2613 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2615 // It may happen than after removing a white space or a new line character,
2616 // two words merge, this new word could be big enough to not fit in its
2617 // current line, so moved to the next one, and make some part of the text to
2618 // exceed the text-input's boundary.
2619 if( !mExceedEnabled )
2621 // Get the new text layout after removing some characters.
2622 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2624 // Get text-input's size.
2625 const Vector3& size = GetControlSize();
2627 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2628 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2630 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2632 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2633 styledCharactersToDelete.begin(),
2634 styledCharactersToDelete.end() );
2638 GetTextLayoutInfo();
2644 const TextStyle oldInputStyle( mInputStyle );
2646 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2648 if( oldInputStyle != mInputStyle )
2650 // Updates the line height accordingly with the input style.
2653 EmitStyleChangedSignal();
2659 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2661 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2662 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2664 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2667 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2669 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2670 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2672 mStyledText.erase(itStart, itEnd);
2674 // update the selection handles if they are visible.
2675 if( mHighlightMeshActor )
2677 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2678 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2680 if( minHandle >= start + ncharacters )
2682 minHandle -= ncharacters;
2684 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2689 if( maxHandle >= start + ncharacters )
2691 maxHandle -= ncharacters;
2693 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2699 // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2702 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2704 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2705 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2706 // Mean we do not re-draw the text more than we have too.
2709 /* Delete character at current cursor position and redisplay*/
2710 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2712 // Ensure positionToDelete is not out of bounds.
2713 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2714 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2715 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2717 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2720 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2722 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2724 // Get the styled text of the character to be deleted as it may be needed if
2725 // the "exceed the text-input's boundaries" option is disabled.
2726 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2728 mStyledText.erase(it); // erase the character left of positionToDelete
2730 if( mStyledText.empty() )
2732 // Styled text is empty, so set the placeholder text.
2733 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2734 mPlaceHolderSet = true;
2738 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2740 const Character characterToDelete = styledCharacterToDelete.mText[0];
2742 // It may happen than after removing a white space or a new line character,
2743 // two words merge, this new word could be big enough to not fit in its
2744 // current line, so moved to the next one, and make some part of the text to
2745 // exceed the text-input's boundary.
2746 if( !mExceedEnabled )
2748 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2750 // Get the new text layout after removing one character.
2751 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2753 // Get text-input's size.
2754 const Vector3& size = GetControlSize();
2756 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2757 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2759 MarkupProcessor::StyledTextArray array;
2760 array.push_back( styledCharacterToDelete );
2761 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2763 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2768 GetTextLayoutInfo();
2770 ShowGrabHandleAndSetVisibility( false );
2772 mCursorPosition = positionToDelete -1;
2774 const TextStyle oldInputStyle( mInputStyle );
2776 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2778 if( oldInputStyle != mInputStyle )
2780 // Updates the line height accordingly with the input style.
2783 EmitStyleChangedSignal();
2788 /*Insert new character into the string and (optionally) redisplay text-input*/
2789 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2791 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2793 // Ensure insertionPosition is not out of bounds.
2794 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2796 bool textExceedsMaximunNumberOfCharacters = false;
2797 bool textExceedsBoundary = false;
2798 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2800 ShowGrabHandleAndSetVisibility( false );
2802 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2806 mIgnoreCommitFlag = true;
2807 mPreEditFlag = false;
2808 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2809 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2812 if( textExceedsMaximunNumberOfCharacters )
2814 EmitMaxInputCharactersReachedSignal();
2817 if( textExceedsBoundary )
2819 EmitInputTextExceedsBoundariesSignal();
2820 PreEditReset( false );
2824 return insertedStringLength;
2827 ImageActor TextInput::CreateCursor( const Vector4& color)
2830 cursor = CreateSolidColorActor(color);
2832 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2833 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2834 cursor.SetVisible(false);
2839 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2841 // As cursor is not moving due to grab handle, handle should be hidden.
2842 ShowGrabHandleAndSetVisibility( false );
2844 bool cursorPositionChanged = false;
2847 if ( mCursorPosition >= places )
2849 mCursorPosition = mCursorPosition - places;
2850 cursorPositionChanged = true;
2855 if ((mCursorPosition + places) <= mStyledText.size())
2857 mCursorPosition = mCursorPosition + places;
2858 cursorPositionChanged = true;
2862 if( cursorPositionChanged )
2864 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2866 const TextStyle oldInputStyle( mInputStyle );
2867 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2871 if( oldInputStyle != mInputStyle )
2873 // Updates the line height accordingly with the input style.
2876 EmitStyleChangedSignal();
2879 ImfManager imfManager = ImfManager::Get();
2882 imfManager.SetCursorPosition ( mCursorPosition );
2883 imfManager.NotifyCursorPosition();
2888 void TextInput::DrawCursor(const std::size_t nthChar)
2890 // Get height of cursor and set its size
2891 Size size( CURSOR_THICKNESS, 0.0f );
2892 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2894 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2898 // Measure Font so know how big text will be if no initial text to measure.
2899 size.height = mLineHeight;
2902 mCursor.SetSize(size);
2904 // If the character is italic then the cursor also tilts.
2905 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2907 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2909 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2911 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2912 bool altPositionValid; // Alternate cursor validity flag.
2913 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2914 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2916 SetAltCursorEnabled( altPositionValid );
2918 if(!altPositionValid)
2920 mCursor.SetPosition( position + UI_OFFSET );
2924 size.height *= 0.5f;
2925 mCursor.SetSize(size);
2926 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2928 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2929 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2930 size.height = rowSize.height * 0.5f;
2931 mCursorRTL.SetSize(size);
2932 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2935 if( IsScrollEnabled() )
2937 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2938 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2943 void TextInput::SetAltCursorEnabled( bool enabled )
2945 mCursorRTLEnabled = enabled;
2946 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2949 void TextInput::SetCursorVisibility( bool visible )
2951 mCursorVisibility = visible;
2952 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2953 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2956 void TextInput::CreateGrabHandle( Dali::Image image )
2962 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2966 mGrabHandleImage = image;
2969 mGrabHandle = ImageActor::New(mGrabHandleImage);
2970 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2971 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2973 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2975 ShowGrabHandleAndSetVisibility( false );
2977 CreateGrabArea( mGrabHandle );
2979 mActiveLayer.Add(mGrabHandle);
2983 void TextInput::CreateGrabArea( Actor& parent )
2985 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2986 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2987 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2988 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2989 mTapDetector.Attach( mGrabArea );
2990 mPanGestureDetector.Attach( mGrabArea );
2992 parent.Add(mGrabArea);
2995 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2997 Vector3 actualHandlePosition;
3001 mActualGrabHandlePosition.x += displacement.x;
3002 mActualGrabHandlePosition.y += displacement.y;
3004 // Grab handle should jump to the nearest character and take cursor with it
3005 std::size_t newCursorPosition = 0;
3006 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3008 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3010 bool handleVisible = true;
3012 if( IsScrollEnabled() )
3014 const Vector3 controlSize = GetControlSize();
3015 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3016 // Scrolls the text if the handle is not in a visible position
3017 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3024 mCurrentHandlePosition = actualHandlePosition;
3025 mScrollDisplacement = Vector2::ZERO;
3029 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3031 mScrollDisplacement.x = -SCROLL_SPEED;
3033 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3035 mScrollDisplacement.x = SCROLL_SPEED;
3037 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3039 mScrollDisplacement.y = -SCROLL_SPEED;
3041 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3043 mScrollDisplacement.y = SCROLL_SPEED;
3049 if( handleVisible && // Only redraw cursor and do updates if position changed
3050 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3052 mCursorPosition = newCursorPosition;
3054 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3056 const TextStyle oldInputStyle( mInputStyle );
3058 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3060 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3062 if( oldInputStyle != mInputStyle )
3064 // Updates the line height accordingly with the input style.
3067 EmitStyleChangedSignal();
3072 return actualHandlePosition;
3075 void TextInput::ShowGrabHandle( bool visible )
3077 if ( IsGrabHandleEnabled() )
3081 mGrabHandle.SetVisible( mGrabHandleVisibility );
3083 StartMonitoringStageForTouch();
3087 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3089 mGrabHandleVisibility = visible;
3090 ShowGrabHandle( visible );
3093 // Callbacks connected to be Property notifications for Boundary checking.
3095 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3097 mIsSelectionHandleOneFlipped = true;
3098 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3099 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3102 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3104 mIsSelectionHandleOneFlipped = false;
3105 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3106 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3109 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3111 mIsSelectionHandleTwoFlipped = true;
3112 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3113 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3116 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3118 mIsSelectionHandleTwoFlipped = false;
3119 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3120 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3123 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3124 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3126 mSelectionHandleOne.SetOpacity(0.0f);
3129 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3131 mSelectionHandleOne.SetOpacity(1.0f);
3134 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3136 mSelectionHandleTwo.SetOpacity(0.0f);
3139 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3141 mSelectionHandleTwo.SetOpacity(1.0f);
3144 // End of Callbacks connected to be Property notifications for Boundary checking.
3146 void TextInput::SetUpHandlePropertyNotifications()
3148 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3150 Vector3 handlesize = GetSelectionHandleSize();
3152 // Exceeding horizontal boundary
3153 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3154 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3156 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3157 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3159 // Within horizontal boundary
3160 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3161 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3163 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3164 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3166 // Exceeding vertical boundary
3167 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3168 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3169 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3170 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3172 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3173 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3174 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3175 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3177 // Within vertical boundary
3178 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3179 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3180 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3181 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3183 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3184 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3185 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3186 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3189 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3191 mSelectionHandleOnePosition = start;
3192 mSelectionHandleTwoPosition = end;
3194 if ( !mSelectionHandleOne )
3196 // create normal and pressed images
3197 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3198 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3200 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3201 mSelectionHandleOne.SetName("SelectionHandleOne");
3202 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3203 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3204 mIsSelectionHandleOneFlipped = false;
3205 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3207 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3208 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3210 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3211 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3213 mTapDetector.Attach( mHandleOneGrabArea );
3214 mPanGestureDetector.Attach( mHandleOneGrabArea );
3216 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3218 mSelectionHandleOne.Add( mHandleOneGrabArea );
3219 mActiveLayer.Add( mSelectionHandleOne );
3222 if ( !mSelectionHandleTwo )
3224 // create normal and pressed images
3225 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3226 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3228 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3229 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3230 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3231 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3232 mIsSelectionHandleTwoFlipped = false;
3233 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3235 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3236 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3237 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3238 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3240 mTapDetector.Attach( mHandleTwoGrabArea );
3241 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3243 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3245 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3247 mActiveLayer.Add( mSelectionHandleTwo );
3250 SetUpHandlePropertyNotifications();
3252 // update table as text may have changed.
3253 GetTextLayoutInfo();
3255 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3256 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3258 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3259 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3261 // Calculates and set the visibility if the scroll mode is enabled.
3262 bool isSelectionHandleOneVisible = true;
3263 bool isSelectionHandleTwoVisible = true;
3264 if( IsScrollEnabled() )
3266 const Vector3& controlSize( GetControlSize() );
3267 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3268 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3269 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3270 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3273 CreateHighlight(); // function will only create highlight if not already created.
3276 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3278 Vector3 actualHandlePosition;
3280 if ( mSelectionHandleOne && mSelectionHandleTwo )
3282 const Vector3& controlSize = GetControlSize();
3284 Size cursorSize( CURSOR_THICKNESS, 0.f );
3286 // Get a reference of the wanted selection handle (handle one or two).
3287 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3289 // Get a reference for the current position of the handle and a copy of its pair
3290 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3291 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3293 // Get a handle of the selection handle actor
3294 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3296 // Selection handles should jump to the nearest character
3297 std::size_t newHandlePosition = 0;
3298 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3300 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3302 bool handleVisible = true;
3304 if( IsScrollEnabled() )
3306 mCurrentSelectionId = handleId;
3308 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3309 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3310 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3317 mCurrentSelectionHandlePosition = actualHandlePosition;
3318 mScrollDisplacement = Vector2::ZERO;
3322 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3324 mScrollDisplacement.x = -SCROLL_SPEED;
3326 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3328 mScrollDisplacement.x = SCROLL_SPEED;
3330 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3332 mScrollDisplacement.y = -SCROLL_SPEED;
3334 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3336 mScrollDisplacement.y = SCROLL_SPEED;
3342 if ( handleVisible && // Ensure the handle is visible.
3343 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3344 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3346 currentSelectionHandlePosition = newHandlePosition;
3348 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3349 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3353 if ( handleId == HandleOne )
3355 const TextStyle oldInputStyle( mInputStyle );
3357 // Set Active Style to that of first character in selection
3358 if( mSelectionHandleOnePosition < mStyledText.size() )
3360 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3363 if( oldInputStyle != mInputStyle )
3365 // Updates the line height accordingly with the input style.
3368 EmitStyleChangedSignal();
3374 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3377 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3380 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3381 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3383 if ( selectionHandleActor )
3385 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3386 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3387 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3389 if( IsScrollEnabled() )
3391 const Size cursorSize( CURSOR_THICKNESS,
3392 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3393 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3395 GetControlSize() ) );
3400 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3402 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3403 // For now the visual position of end of logical string will be end of visual string.
3404 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3406 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3409 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3411 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3412 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3413 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3414 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3416 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3418 // Deselect text prior to startSelectionIt
3419 for(;it!=startSelectionIt;++it)
3421 selectedVisualText[*it] = false;
3424 // Select text from startSelectionIt -> endSelectionIt
3425 for(;it!=endSelectionIt;++it)
3427 selectedVisualText[*it] = true;
3430 // Deselect text after endSelection
3433 selectedVisualText[*it] = false;
3436 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3439 // Calculate the dimensions of the quads they will make the highlight mesh
3440 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3442 // At the moment there is no public API to modify the block alignment option.
3443 const bool blockAlignEnabled = true;
3445 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3447 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3449 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3450 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3452 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3453 std::vector<bool> selectedVisualText;
3454 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3455 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3456 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3458 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3459 float rowLeft = 0.0f;
3460 float rowRight = 0.0f;
3461 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3462 float maxRowLeft = std::numeric_limits<float>::max();
3463 float maxRowRight = 0.0f;
3465 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3467 // Scan through entire text.
3470 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3472 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3473 bool charSelected( false );
3474 if( selectedIt != selectedEndIt )
3476 charSelected = *selectedIt++;
3479 if(selectionState == SelectionNone)
3483 selectionState = SelectionStarted;
3484 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3485 rowRight = rowLeft + charInfo.mSize.width;
3488 else if(selectionState == SelectionStarted)
3490 // break selection on:
3491 // 1. new line causing selection break. (\n or wordwrap)
3492 // 2. character not selected.
3493 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3496 // finished selection.
3497 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3498 // that it resides on. That way this enumeration is not necessary.
3500 if(lastIt->mIsNewLineChar)
3502 // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line.
3503 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3505 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3506 maxRowLeft = std::min(maxRowLeft, min.x);
3507 maxRowRight = std::max(maxRowRight, max.x);
3508 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3509 float rowTop = rowBottom - rowSize.height;
3511 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3512 if(charSelected && blockAlignEnabled)
3514 rowRight = std::numeric_limits<float>::max();
3516 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3518 selectionState = SelectionNone;
3520 // Still selected? start a new selection
3523 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3524 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3525 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3526 selectionState = SelectionStarted;
3531 // build up highlight(s) with this selection data.
3532 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3533 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3540 // If reached end, and still on selection, then close selection.
3543 if(selectionState == SelectionStarted)
3545 // finished selection.
3547 if(lastIt->mIsNewLineChar)
3549 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3551 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3552 maxRowLeft = std::min(maxRowLeft, min.x);
3553 maxRowRight = std::max(maxRowRight, max.x);
3554 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3555 float rowTop = rowBottom - rowSize.height;
3556 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3560 // Get the top left and bottom right corners.
3561 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3562 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3563 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3565 // Clamp quads so they appear to clip to borders of the whole text.
3566 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3568 // For block-align align Further Clamp quads to max left and right extents
3569 if(blockAlignEnabled)
3571 // BlockAlign: Will adjust highlight to block:
3573 // H[ello] (top row right = max of all rows right)
3574 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3575 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3576 // [text] (bottom row left = min of all rows left)
3577 // (common in SMS messaging selection)
3579 // As opposed to the default which is tight text highlighting.
3584 // (common in regular text editors/web browser selection)
3586 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3589 // Finally clamp quads again so they don't exceed the boundry of the control.
3590 const Vector3& controlSize = GetControlSize();
3591 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3594 return mNewHighlightInfo;
3597 void TextInput::UpdateHighlight()
3599 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3601 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3603 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3604 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3605 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3606 // [BOTTOM] [ MIDDLE ]
3609 // Each quad is created as 2 triangles.
3610 // Middle is just 1 quad regardless of its size.
3624 if ( mHighlightMeshActor )
3626 // vertex and triangle buffers should always be present if MeshActor is alive.
3627 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3628 MeshData::VertexContainer vertices;
3629 Dali::MeshData::FaceIndices faceIndices;
3631 if( !newHighlightInfo.mQuadList.empty() )
3633 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3634 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3636 // vertex position defaults to (0 0 0)
3637 MeshData::Vertex vertex;
3638 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3641 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3643 // Add each quad geometry (a sub-selection) to the mesh data.
3653 QuadCoordinates& quad = *iter;
3655 vertex.x = quad.min.x;
3656 vertex.y = quad.min.y;
3657 vertices.push_back( vertex );
3660 vertex.x = quad.max.x;
3661 vertex.y = quad.min.y;
3662 vertices.push_back( vertex );
3664 // bottom-left (v+2)
3665 vertex.x = quad.min.x;
3666 vertex.y = quad.max.y;
3667 vertices.push_back( vertex );
3669 // bottom-right (v+3)
3670 vertex.x = quad.max.x;
3671 vertex.y = quad.max.y;
3672 vertices.push_back( vertex );
3674 // triangle A (3, 1, 0)
3675 faceIndices.push_back( v + 3 );
3676 faceIndices.push_back( v + 1 );
3677 faceIndices.push_back( v );
3679 // triangle B (0, 2, 3)
3680 faceIndices.push_back( v );
3681 faceIndices.push_back( v + 2 );
3682 faceIndices.push_back( v + 3 );
3684 mMeshData.SetFaceIndices( faceIndices );
3687 BoneContainer bones(0); // passed empty as bones not required
3688 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3689 mHighlightMesh.UpdateMeshData(mMeshData);
3694 void TextInput::ClearPopup()
3696 mPopupPanel.Clear();
3699 void TextInput::AddPopupOptions()
3701 mPopupPanel.AddPopupOptions();
3704 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3706 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3708 Vector3 clampedPosition ( position );
3709 Vector3 tailOffsetPosition ( position );
3711 float xOffSet( 0.0f );
3713 Actor self = Self();
3714 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3716 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3717 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3719 // Clamp to left or right or of boundary
3720 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3722 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3724 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3726 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3729 clampedPosition.x = position.x + xOffSet;
3730 tailOffsetPosition.x = -xOffSet;
3732 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3733 bool flipTail( false );
3735 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3737 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3741 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3742 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3745 void TextInput::HidePopup(bool animate, bool signalFinished )
3747 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3749 mPopupPanel.Hide( animate );
3751 if( animate && signalFinished )
3753 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3758 void TextInput::ShowPopup( bool animate )
3761 Vector2 alternativePopupPosition;
3763 if(mHighlightMeshActor && mState == StateEdit)
3766 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3768 // When text is selected, show popup above top handle (and text), or below bottom handle.
3769 // topHandle: referring to the top most point of the handle or the top line of selection.
3770 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3772 topHandle = mSelectionHandleOneActualPosition;
3773 bottomHandle = mSelectionHandleTwoActualPosition;
3774 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3778 topHandle = mSelectionHandleTwoActualPosition;
3779 bottomHandle = mSelectionHandleOneActualPosition;
3780 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3782 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3783 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3785 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3787 position.x = xPosition;
3789 // Alternative position if no upper space
3790 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3791 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3795 // When no text is selected, show popup at world position of grab handle or cursor
3796 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3797 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3798 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3799 // if can't be positioned above, then position below row.
3800 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3803 // If grab handle enabled then position pop-up below the grab handle.
3804 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3808 SetPopupPosition( position, alternativePopupPosition );
3811 mPopupPanel.Show( Self(), animate );
3812 StartMonitoringStageForTouch();
3814 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3817 void TextInput::ShowPopupCutCopyPaste()
3821 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3822 // Check the selected text is whole text or not.
3823 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3825 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3828 if ( !mStyledText.empty() )
3830 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3831 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3834 if( mClipboard && mClipboard.NumberOfItems() )
3836 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3837 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3842 mPopupPanel.Hide(false);
3846 void TextInput::SetUpPopupSelection()
3849 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3850 // If no text exists then don't offer to select
3851 if ( !mStyledText.empty() )
3853 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3854 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3855 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3857 // if clipboard has valid contents then offer paste option
3858 if( mClipboard && mClipboard.NumberOfItems() )
3860 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3861 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3866 mPopupPanel.Hide(false);
3869 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3874 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3875 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3876 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3877 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3879 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3881 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3883 float closestYdifference = std::numeric_limits<float>::max();
3884 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3885 std::size_t numberOfMatchedCharacters = 0;
3887 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3888 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3890 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3892 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3893 float baselinePosition = info.mPosition.y - info.mDescender;
3895 if( info.mIsVisible )
3897 // store difference between source y point and the y position of the current character
3898 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3900 if( currentYdifference < closestYdifference )
3902 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3903 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3904 closestYdifference = currentYdifference;
3905 matchedCharacters.clear();
3906 numberOfMatchedCharacters = 0; // reset count
3909 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3910 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3912 // ignore new line character.
3913 if( !info.mIsNewLineChar )
3915 matchedCharacters.push_back( info );
3916 numberOfMatchedCharacters++;
3920 } // End of loop checking each character's y position in the character layout table
3922 // Check if last character is a newline, if it is
3923 // then need pretend there is an imaginary line afterwards,
3924 // and check if user is touching below previous line.
3925 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3927 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3929 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3933 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3934 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3936 bool matched( false );
3938 // 2 Iterate through matching list of y positions and find closest matching X position.
3939 for( ; it != endIt; ++it )
3941 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3943 if( info.mIsVisible )
3945 // stop when on left side of character's center.
3946 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3947 if( sourceScrollOffset.x < characterMidPointPosition )
3949 if(info.mIsRightToLeftCharacter)
3951 rightToLeftChar = true;
3953 glyphIntersection = info.mPosition.x;
3958 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3964 rightToLeftChar = lastRightToLeftChar;
3967 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3968 closestIndex = lineOffset + matchCharacterIndex;
3970 mClosestCursorPositionEOL = false; // reset
3971 if ( it == endIt && !matched )
3973 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3976 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3977 if( rightToLeftChar && lastRightToLeftChar )
3979 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3984 // closestIndex is the visual index, need to convert it to the logical index
3985 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3987 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3989 // Checks for situations where user is touching between LTR and RTL
3990 // characters. To identify if the user means the end of a LTR string
3991 // or the beginning of an RTL string, and vice versa.
3992 if( closestIndex > 0 )
3994 if( rightToLeftChar && !lastRightToLeftChar )
3999 // A: In this touch range, the user is indicating that they wish to place
4000 // the cursor at the end of the LTR text.
4001 // B: In this touch range, the user is indicating that they wish to place
4002 // the cursor at the end of the RTL text.
4004 // Result of touching A area:
4005 // [.....LTR]|[RTL......]+
4007 // |: primary cursor (for typing LTR chars)
4008 // +: secondary cursor (for typing RTL chars)
4010 // Result of touching B area:
4011 // [.....LTR]+[RTL......]|
4013 // |: primary cursor (for typing RTL chars)
4014 // +: secondary cursor (for typing LTR chars)
4016 if( sourceScrollOffset.x < glyphIntersection )
4021 else if( !rightToLeftChar && lastRightToLeftChar )
4023 if( sourceScrollOffset.x < glyphIntersection )
4030 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4031 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4032 // one further ahead
4033 if( rightToLeftChar && !lastRightToLeftChar )
4038 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4040 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4042 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4051 float TextInput::GetLineJustificationPosition() const
4053 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4054 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4055 float alignmentOffset = 0.f;
4057 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4058 if( alignment & Toolkit::Alignment::HorizontalLeft )
4060 alignmentOffset = 0.f;
4062 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4064 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4066 else if( alignment & Toolkit::Alignment::HorizontalRight )
4068 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4071 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4072 float justificationOffset = 0.f;
4074 switch( justification )
4076 case Toolkit::TextView::Left:
4078 justificationOffset = 0.f;
4081 case Toolkit::TextView::Center:
4083 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4086 case Toolkit::TextView::Right:
4088 justificationOffset = mTextLayoutInfo.mTextSize.width;
4091 case Toolkit::TextView::Justified:
4093 justificationOffset = 0.f;
4098 DALI_ASSERT_ALWAYS( false );
4102 return alignmentOffset + justificationOffset;
4105 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4107 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4108 A newline character is not inserted in this case */
4110 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4112 Vector3 cursorPosition;
4114 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4116 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4118 // end character so use
4119 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4120 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4124 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4127 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4129 // If previous character on a different line then use current characters position
4130 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4132 if ( mClosestCursorPositionEOL )
4134 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4138 cursorPosition = Vector3(currentCharInfo.mPosition);
4143 // Previous character is on same line so use position of previous character plus it's width.
4144 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4147 return cursorPosition;
4150 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4152 bool direction(false);
4153 Vector3 alternatePosition;
4154 bool alternatePositionValid(false);
4156 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4159 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4161 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4163 alternatePositionValid = false;
4164 directionRTL = false;
4166 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4168 std::size_t visualCharacterPosition;
4170 // When cursor is not at beginning, consider possibility of
4171 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4172 if(characterPosition > 0)
4174 // Cursor position should be the end of the last character.
4175 // If the last character is LTR, then the end is on the right side of the glyph.
4176 // If the last character is RTL, then the end is on the left side of the glyph.
4177 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4179 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4181 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4184 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4185 if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4187 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4188 const Vector3& size = GetControlSize();
4190 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4192 --visualCharacterPosition;
4194 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4197 if(!info.mIsNewLineChar)
4199 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4203 // When cursor points to first character on new line, position cursor at the start of this glyph.
4204 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4206 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4207 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4208 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4210 cursorPosition.x = infoNext.mPosition.x + start;
4211 cursorPosition.y = infoNext.mPosition.y;
4215 // If cursor points to the end of text, then can only position
4216 // cursor where the new line starts based on the line-justification position.
4217 cursorPosition.x = GetLineJustificationPosition();
4219 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4221 // If this is after the last character, then we can assume that the new cursor
4222 // should be exactly one row below the current row.
4224 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4225 cursorPosition.y = info.mPosition.y + rowRect.height;
4229 // If this is not after last character, then we can use this row's height.
4230 // should be exactly one row below the current row.
4232 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4233 cursorPosition.y = info.mPosition.y + rowRect.height;
4238 directionRTL = info.mIsRightToLeftCharacter;
4240 // 1. When the cursor is neither at the beginning or the end,
4241 // we can show multiple cursors under situations when the cursor is
4242 // between RTL and LTR text...
4243 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4245 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4247 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4248 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4250 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4252 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4253 // Text: [...LTR...]|[...RTL...]
4255 // Alternate cursor pos: ^
4256 // In which case we need to display an alternate cursor for the RTL text.
4258 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4259 alternatePosition.y = infoAlt.mPosition.y;
4260 alternatePositionValid = true;
4262 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4264 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4265 // Text: |[...RTL...] [...LTR....]
4267 // Alternate cursor pos: ^
4268 // In which case we need to display an alternate cursor for the RTL text.
4270 alternatePosition.x = infoAlt.mPosition.x;
4271 alternatePosition.y = infoAlt.mPosition.y;
4272 alternatePositionValid = true;
4277 // 2. When the cursor is at the end of the text,
4278 // and we have multi-directional text,
4279 // we can also consider showing mulitple cursors.
4280 // The rule here is:
4281 // If first and last characters on row are different
4282 // Directions, then two cursors need to be displayed.
4284 // Get first logical glyph on row
4285 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4287 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4288 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4290 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4292 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4293 // Text: [...LTR...]|[...RTL...]
4295 // Alternate cursor pos: ^
4296 // In which case we need to display an alternate cursor for the RTL text, this cursor
4297 // should be at the end of the given line.
4299 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4300 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4301 alternatePosition.y = infoAlt.mPosition.y;
4302 alternatePositionValid = true;
4304 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4306 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4307 // Text: |[...RTL...] [...LTR....]
4309 // Alternate cursor pos: ^
4310 // In which case we need to display an alternate cursor for the RTL text.
4312 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4313 alternatePosition.x = infoAlt.mPosition.x;
4314 alternatePosition.y = infoAlt.mPosition.y;
4315 alternatePositionValid = true;
4318 } // characterPosition > 0
4319 else if(characterPosition == 0)
4321 // When the cursor position is at the beginning, it should be at the start of the current character.
4322 // If the current character is LTR, then the start is on the right side of the glyph.
4323 // If the current character is RTL, then the start is on the left side of the glyph.
4324 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4326 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4328 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4331 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4332 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4334 cursorPosition.x = info.mPosition.x + start;
4335 cursorPosition.y = info.mPosition.y;
4336 directionRTL = info.mIsRightToLeftCharacter;
4341 // If the character table is void, place the cursor accordingly the text alignment.
4342 const Vector3& size = GetControlSize();
4344 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4345 float alignmentOffset = 0.f;
4347 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4348 if( alignment & Toolkit::Alignment::HorizontalLeft )
4350 alignmentOffset = 0.f;
4352 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4354 alignmentOffset = 0.5f * ( size.width );
4356 else if( alignment & Toolkit::Alignment::HorizontalRight )
4358 alignmentOffset = size.width;
4361 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4362 cursorPosition.x = alignmentOffset;
4364 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4365 if( alignment & Toolkit::Alignment::VerticalTop )
4367 cursorPosition.y = mLineHeight;
4369 else if( alignment & Toolkit::Alignment::VerticalCenter )
4371 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4373 else if( alignment & Toolkit::Alignment::VerticalBottom )
4375 cursorPosition.y = size.height;
4379 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4380 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4381 if( alternatePositionValid )
4383 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4384 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4387 return cursorPosition;
4390 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4392 // scan string from current position to beginning of current line to note direction of line
4393 while(logicalPosition)
4396 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4397 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4404 return logicalPosition;
4407 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4411 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4414 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4416 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4417 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4419 min = Vector2::ZERO;
4420 max = Vector2(0.0f, mLineHeight);
4424 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4425 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4426 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4428 // If cursor is pointing to end of line, then start from last character.
4429 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4431 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4433 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4434 if( !it->mIsVisible )
4436 characterPosition = FindVisibleCharacter( Left, characterPosition );
4437 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4440 // Scan characters left and right of cursor, stopping when end of line/string reached or
4441 // y position greater than threshold of reference line.
4443 // 1. scan left until we reach the beginning or a different line.
4444 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4445 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4446 // min-x position is the left-most char's left (x)
4447 // max-x position is the right-most char's right (x)
4448 // min-y position is the minimum of all character's top (y)
4449 // max-y position is the maximum of all character's bottom (y+height)
4450 min.y = validCharIt->mPosition.y;
4451 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4456 min.y = std::min(min.y, validCharIt->mPosition.y);
4457 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4466 if( (it->mPosition.y < referenceLine) ||
4467 (it->mIsNewLineChar) ||
4474 // info refers to the first character on this line.
4475 min.x = validCharIt->mPosition.x;
4477 // 2. scan right until we reach end or a different line
4478 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4479 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4483 if( (it->mPosition.y > referenceLine) ||
4484 (it->mIsNewLineChar) ||
4491 min.y = std::min(min.y, validCharIt->mPosition.y);
4492 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4497 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4499 if ( validCharIt != end )
4501 // info refers to the last character on this line.
4502 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4505 return Size( max.x - min.x, max.y - min.y );
4508 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4510 Actor popUpPanel = mPopupPanel.GetRootActor();
4512 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4518 Dali::Actor parent( touchedActor.GetParent() );
4522 return WasTouchedCheck( parent );
4529 void TextInput::StartMonitoringStageForTouch()
4531 Stage stage = Stage::GetCurrent();
4532 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4535 void TextInput::EndMonitoringStageForTouch()
4537 Stage stage = Stage::GetCurrent();
4538 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4541 void TextInput::OnStageTouched(const TouchEvent& event)
4543 if( event.GetPointCount() > 0 )
4545 if ( TouchPoint::Down == event.GetPoint(0).state )
4547 const Actor touchedActor(event.GetPoint(0).hitActor);
4549 bool popUpShown( false );
4551 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4556 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4558 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4560 EndMonitoringStageForTouch();
4561 HidePopup( true, false );
4564 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4566 EndMonitoringStageForTouch();
4567 ShowGrabHandleAndSetVisibility( false );
4573 void TextInput::SelectText(std::size_t start, std::size_t end)
4575 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4576 IsGrabHandleEnabled()?"true":"false",
4577 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4578 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4579 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4581 StartMonitoringStageForTouch();
4583 if ( mEditModeActive ) // Only allow text selection when in edit mode
4585 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4586 mSelectingText = true;
4588 mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4590 ImfManager imfManager = ImfManager::Get();
4593 imfManager.SetCursorPosition ( mCursorPosition );
4594 imfManager.SetSurroundingText( GetText() );
4595 imfManager.NotifyCursorPosition();
4597 // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead.
4599 // Hide grab handle when selecting.
4600 ShowGrabHandleAndSetVisibility( false );
4602 if( start != end ) // something to select
4604 SetCursorVisibility( false );
4605 StopCursorBlinkTimer();
4607 CreateSelectionHandles(start, end);
4610 const TextStyle oldInputStyle( mInputStyle );
4611 mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4613 if( oldInputStyle != mInputStyle )
4615 // Updates the line height accordingly with the input style.
4618 EmitStyleChangedSignal();
4624 mSelectingText = false;
4628 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4630 MarkupProcessor::StyledTextArray currentSelectedText;
4632 if ( IsTextSelected() )
4634 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4635 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4637 for(; it != end; ++it)
4639 MarkupProcessor::StyledText& styledText( *it );
4640 currentSelectedText.push_back( styledText );
4643 return currentSelectedText;
4646 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4648 const std::size_t beginIndex = std::min( begin, end );
4649 const std::size_t endIndex = std::max( begin, end );
4652 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4654 // Create a styled text array used to replace the text into the text-view.
4655 MarkupProcessor::StyledTextArray text;
4656 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4658 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4659 GetTextLayoutInfo();
4661 if( IsScrollEnabled() )
4663 // Need to set the scroll position as the text's size may have changed.
4664 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4667 ShowGrabHandleAndSetVisibility( false );
4673 // Set Handle positioning as the new style may have repositioned the characters.
4674 SetSelectionHandlePosition(HandleOne);
4675 SetSelectionHandlePosition(HandleTwo);
4678 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4680 // Just hide the grab handle when keyboard is hidden.
4681 if (!keyboardShown )
4683 ShowGrabHandleAndSetVisibility( false );
4685 // If the keyboard is not now being shown, then hide the popup panel
4686 mPopupPanel.Hide( true );
4690 // Removes highlight and resumes edit mode state
4691 void TextInput::RemoveHighlight()
4693 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4695 if ( mHighlightMeshActor )
4697 if ( mSelectionHandleOne )
4699 mActiveLayer.Remove( mSelectionHandleOne );
4700 mSelectionHandleOne.Reset();
4701 mSelectionHandleOneOffset.x = 0.0f;
4703 if ( mSelectionHandleTwo )
4705 mActiveLayer.Remove( mSelectionHandleTwo );
4706 mSelectionHandleTwo.Reset();
4707 mSelectionHandleTwoOffset.x = 0.0f;
4710 mNewHighlightInfo.mQuadList.clear();
4712 Self().Remove( mHighlightMeshActor );
4714 SetCursorVisibility( true );
4715 StartCursorBlinkTimer();
4717 mHighlightMeshActor.Reset();
4718 // NOTE: We cannot dereference mHighlightMesh, due
4719 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4724 mSelectionHandleOnePosition = 0;
4725 mSelectionHandleTwoPosition = 0;
4728 void TextInput::CreateHighlight()
4730 if ( !mHighlightMeshActor )
4732 mMeshData = MeshData( );
4733 mMeshData.SetHasNormals( true );
4735 mCustomMaterial = Material::New("CustomMaterial");
4736 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4738 mMeshData.SetMaterial( mCustomMaterial );
4740 mHighlightMesh = Mesh::New( mMeshData );
4742 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4743 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4744 mHighlightMeshActor.SetInheritShaderEffect( false );
4745 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4746 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4747 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4748 mHighlightMeshActor.SetAffectedByLighting(false);
4750 Self().Add(mHighlightMeshActor);
4755 bool TextInput::CopySelectedTextToClipboard()
4757 mCurrentCopySelecton.clear();
4759 mCurrentCopySelecton = GetSelectedText();
4761 std::string stringToStore;
4763 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4764 * a marked up string.
4766 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4767 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4768 bool success = mClipboard.SetItem( stringToStore );
4772 void TextInput::PasteText( const Text& text )
4774 // Update Flag, indicates whether to update the text-input contents or not.
4775 // Any key stroke that results in a visual change of the text-input should
4776 // set this flag to true.
4777 bool update = false;
4778 if( mHighlightMeshActor )
4780 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4781 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4783 ImfManager imfManager = ImfManager::Get();
4786 imfManager.SetCursorPosition( mCursorPosition );
4787 imfManager.NotifyCursorPosition();
4789 DeleteHighlightedText( true );
4793 bool textExceedsMaximunNumberOfCharacters = false;
4794 bool textExceedsBoundary = false;
4796 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4798 mCursorPosition += insertedStringLength;
4799 ImfManager imfManager = ImfManager::Get();
4802 imfManager.SetCursorPosition ( mCursorPosition );
4803 imfManager.NotifyCursorPosition();
4806 update = update || ( insertedStringLength > 0 );
4812 if( insertedStringLength < text.GetLength() )
4814 EmitMaxInputCharactersReachedSignal();
4817 if( textExceedsBoundary )
4819 EmitInputTextExceedsBoundariesSignal();
4823 void TextInput::SetTextDirection()
4825 // Put the cursor to the right if we are empty and an RTL language is being used.
4826 if ( mStyledText.empty() )
4828 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4830 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4831 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4833 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4834 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4836 int alignment( mDisplayedTextView.GetTextAlignment() &
4837 ( Toolkit::Alignment::VerticalTop |
4838 Toolkit::Alignment::VerticalCenter |
4839 Toolkit::Alignment::VerticalBottom |
4840 Toolkit::Alignment::HorizontalCenter ) );
4841 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4843 // If our alignment is in the center, then do not change.
4844 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4846 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4849 // If our justification is in the center, then do not change.
4850 if ( justification != Toolkit::TextView::Center )
4852 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4855 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4856 mDisplayedTextView.SetLineJustification( justification );
4860 void TextInput::UpdateLineHeight()
4862 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4863 mLineHeight = font.GetLineHeight();
4865 // If the height exceed policy is shrink or exceed the boundaries of the text-input is not allowed, then modify the line height is needed.
4867 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4869 if( !mExceedEnabled || shrink )
4871 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4875 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4877 std::size_t position = 0;
4879 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4885 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4887 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4889 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4895 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4896 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4898 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4904 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4909 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4916 void TextInput::SetSortModifier( float depthOffset )
4918 if(mDisplayedTextView)
4920 mDisplayedTextView.SetSortModifier(depthOffset);
4924 void TextInput::SetSnapshotModeEnabled( bool enable )
4926 if(mDisplayedTextView)
4928 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4932 bool TextInput::IsSnapshotModeEnabled() const
4934 bool snapshotEnabled = false;
4936 if(mDisplayedTextView)
4938 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4941 return snapshotEnabled;
4944 void TextInput::SetMarkupProcessingEnabled( bool enable )
4946 mMarkUpEnabled = enable;
4949 bool TextInput::IsMarkupProcessingEnabled() const
4951 return mMarkUpEnabled;
4954 void TextInput::SetScrollEnabled( bool enable )
4956 if( mDisplayedTextView )
4958 mDisplayedTextView.SetScrollEnabled( enable );
4963 // Don't set cursor's and handle's visibility to false if they are outside the
4964 // boundaries of the text-input.
4965 mIsCursorInScrollArea = true;
4966 mIsGrabHandleInScrollArea = true;
4967 if( mSelectionHandleOne && mSelectionHandleTwo )
4969 mSelectionHandleOne.SetVisible( true );
4970 mSelectionHandleTwo.SetVisible( true );
4972 if( mHighlightMeshActor )
4974 mHighlightMeshActor.SetVisible( true );
4980 bool TextInput::IsScrollEnabled() const
4982 bool scrollEnabled = false;
4984 if( mDisplayedTextView )
4986 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4989 return scrollEnabled;
4992 void TextInput::SetScrollPosition( const Vector2& position )
4994 if( mDisplayedTextView )
4996 mDisplayedTextView.SetScrollPosition( position );
5000 Vector2 TextInput::GetScrollPosition() const
5002 Vector2 scrollPosition;
5004 if( mDisplayedTextView )
5006 scrollPosition = mDisplayedTextView.GetScrollPosition();
5009 return scrollPosition;
5012 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5014 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5015 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5016 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5018 // Add style to the new input text.
5019 MarkupProcessor::StyledTextArray textToInsert;
5020 for( std::size_t i = 0; i < insertedStringLength; ++i )
5022 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5023 textToInsert.push_back( newStyledCharacter );
5026 //Insert text to the TextView.
5027 const bool emptyTextView = mStyledText.empty();
5028 if( emptyTextView && mPlaceHolderSet )
5030 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5031 mDisplayedTextView.SetText( textToInsert );
5035 if( 0 == numberOfCharactersToReplace )
5037 mDisplayedTextView.InsertTextAt( position, textToInsert );
5041 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5044 mPlaceHolderSet = false;
5046 if( textToInsert.empty() )
5048 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5049 GetTextLayoutInfo();
5053 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5054 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5057 textExceedsBoundary = false;
5059 if( !mExceedEnabled )
5061 const Vector3& size = GetControlSize();
5063 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5065 // If new text does not fit within TextView
5066 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5067 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5068 GetTextLayoutInfo();
5069 textExceedsBoundary = true;
5070 insertedStringLength = 0;
5073 if( textExceedsBoundary )
5075 // Add the part of the text which fits on the text-input.
5077 // Split the text which doesn't fit in two halves.
5078 MarkupProcessor::StyledTextArray firstHalf;
5079 MarkupProcessor::StyledTextArray secondHalf;
5080 SplitText( textToInsert, firstHalf, secondHalf );
5082 // Clear text. This text will be filled with the text inserted.
5083 textToInsert.clear();
5085 // Where to insert the text.
5086 std::size_t positionToInsert = position;
5088 bool end = text.GetLength() <= 1;
5091 // Insert text and check ...
5092 const std::size_t textLength = firstHalf.size();
5093 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5094 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5096 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5098 // Inserted text doesn't fit.
5100 // Remove inserted text
5101 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5102 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5104 // The iteration finishes when only one character doesn't fit.
5105 end = textLength <= 1;
5109 // Prepare next two halves for next iteration.
5110 MarkupProcessor::StyledTextArray copyText = firstHalf;
5111 SplitText( copyText, firstHalf, secondHalf );
5118 // store text to be inserted in mStyledText.
5119 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5121 // Increase the inserted characters counter.
5122 insertedStringLength += textLength;
5124 // Prepare next two halves for next iteration.
5125 MarkupProcessor::StyledTextArray copyText = secondHalf;
5126 SplitText( copyText, firstHalf, secondHalf );
5128 // Update where next text has to be inserted
5129 positionToInsert += textLength;
5135 if( textToInsert.empty() && emptyTextView )
5137 // No character has been added and the text-view was empty.
5138 // Set the placeholder text.
5139 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5140 mPlaceHolderSet = true;
5144 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5145 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5146 mPlaceHolderSet = false;
5149 return insertedStringLength;
5152 void TextInput::GetTextLayoutInfo()
5154 if( mStyledText.empty() )
5156 // The text-input has no text, clear the text-view's layout info.
5157 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5161 if( mDisplayedTextView )
5163 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5167 // There is no text-view.
5168 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5173 void TextInput::SetOffsetFromText( const Vector4& offset )
5175 mPopupOffsetFromText = offset;
5178 const Vector4& TextInput::GetOffsetFromText() const
5180 return mPopupOffsetFromText;
5183 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5185 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5189 TextInput& textInputImpl( GetImpl( textInput ) );
5191 switch ( propertyIndex )
5193 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5195 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5198 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5200 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5203 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5205 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5208 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5210 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5213 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5215 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5218 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5220 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5223 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5225 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5228 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5230 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5233 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5235 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5238 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5240 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5243 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5245 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5248 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5250 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5253 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5255 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5258 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5260 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5263 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5265 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5268 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5270 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5276 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5278 Property::Value value;
5280 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5284 TextInput& textInputImpl( GetImpl( textInput ) );
5286 switch ( propertyIndex )
5288 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5290 value = textInputImpl.GetMaterialDiffuseColor();
5293 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5295 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5298 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5300 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5303 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5305 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5308 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5310 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5313 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5315 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5318 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5320 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5323 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5325 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5328 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5330 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5333 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5335 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5338 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5340 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5343 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5345 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5348 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5350 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5353 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5355 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5358 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5360 value = textInputImpl.GetOffsetFromText();
5363 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5365 value = textInputImpl.mCursor.GetCurrentColor();
5372 void TextInput::EmitStyleChangedSignal()
5374 // emit signal if input style changes.
5375 Toolkit::TextInput handle( GetOwner() );
5376 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5379 void TextInput::EmitTextModified()
5381 // emit signal when text changes.
5382 Toolkit::TextInput handle( GetOwner() );
5383 mTextModifiedSignal.Emit( handle );
5387 void TextInput::EmitMaxInputCharactersReachedSignal()
5389 // emit signal if max characters is reached during text input.
5390 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5392 Toolkit::TextInput handle( GetOwner() );
5393 mMaxInputCharactersReachedSignalV2.Emit( handle );
5396 void TextInput::EmitInputTextExceedsBoundariesSignal()
5398 // Emit a signal when the input text exceeds the boundaries of the text input.
5400 Toolkit::TextInput handle( GetOwner() );
5401 mInputTextExceedBoundariesSignalV2.Emit( handle );
5404 } // namespace Internal
5406 } // namespace Toolkit