2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
25 #include <dali/integration-api/debug.h>
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
58 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
72 const std::string NEWLINE( "\n" );
74 const TextStyle DEFAULT_TEXT_STYLE;
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
81 * Selection state enumeration (FSM)
85 SelectionNone, ///< Currently not encountered selected section.
86 SelectionStarted, ///< Encountered selected section
87 SelectionFinished ///< Finished selected section
91 * Whether the given style is the default style or not.
92 * @param[in] style The given style.
93 * @return \e true if the given style is the default. Otherwise it returns \e false.
95 bool IsDefaultStyle( const TextStyle& style )
97 return DEFAULT_TEXT_STYLE == style;
101 * Whether the given styled text is using the default style or not.
102 * @param[in] textArray The given text.
103 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
107 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
109 const TextStyle& style( (*it).mStyle );
111 if( !IsDefaultStyle( style ) )
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
122 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
126 if( ( *it ).mIsVisible )
128 return --cursorPosition;
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
139 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
141 if( ( *it ).mIsVisible )
143 return cursorPosition;
149 return cursorPosition;
153 * Whether the given position plus the cursor size offset is inside the given boundary.
155 * @param[in] position The given position.
156 * @param[in] cursorSize The cursor size.
157 * @param[in] controlSize The given boundary.
159 * @return whether the given position is inside the given boundary.
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
163 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
170 * Splits a text in two halves.
172 * If the text's number of characters is odd, firstHalf has one more character.
174 * @param[in] text The text to be split.
175 * @param[out] firstHalf The first half of the text.
176 * @param[out] secondHalf The second half of the text.
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
185 const std::size_t textLength = text.size();
186 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
188 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
192 } // end of namespace
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
205 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
207 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
208 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
209 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
210 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
211 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
212 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
213 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
215 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
255 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
257 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
259 QuadCoordinates quad(x1, y1, x2, y2);
260 mQuadList.push_back( quad );
263 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
265 for(std::size_t i = 0;i < mQuadList.size(); i++)
267 QuadCoordinates& quad = mQuadList[i];
269 quad.min.Clamp(min, max);
270 quad.max.Clamp(min, max);
274 // [TextInput] ////////////////////////////////////////////////////////////////
276 Dali::Toolkit::TextInput TextInput::New()
278 // Create the implementation
279 TextInputPtr textInput(new TextInput());
280 // Pass ownership to CustomActor via derived handle
281 Dali::Toolkit::TextInput handle(*textInput);
283 textInput->Initialize();
288 TextInput::TextInput()
289 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
294 mDisplayedTextView(),
295 mStyledPlaceHolderText(),
296 mMaxStringLength( DEFAULT_MAX_SIZE ),
297 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
298 mCursorPosition( 0 ),
299 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
300 mIsSelectionHandleOneFlipped( false ),
301 mIsSelectionHandleTwoFlipped( false ),
302 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
303 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
304 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
305 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
306 mSelectionHandleOnePosition( 0 ),
307 mSelectionHandleTwoPosition( 0 ),
309 mPreEditStartPosition( 0 ),
310 mPreEditLength ( 0 ),
311 mNumberOfSurroundingCharactersDeleted( 0 ),
312 mTouchStartTime( 0 ),
314 mCurrentCopySelecton(),
317 mScrollDisplacement(),
318 mCurrentHandlePosition(),
319 mCurrentSelectionId(),
320 mCurrentSelectionHandlePosition(),
321 mRequestedSelection( 0, 0 ),
322 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
323 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mMaterialColor( LIGHTBLUE ),
326 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
327 mOverrideAutomaticAlignment( false ),
328 mCursorRTLEnabled( false ),
329 mClosestCursorPositionEOL ( false ),
330 mCursorBlinkStatus( true ),
331 mCursorVisibility( false ),
332 mGrabHandleVisibility( false ),
333 mIsCursorInScrollArea( true ),
334 mIsGrabHandleInScrollArea( true ),
335 mEditModeActive( false ),
336 mEditOnTouch( true ),
337 mTextSelection( true ),
338 mExceedEnabled( true ),
339 mGrabHandleEnabled( true ),
340 mIsSelectionHandleFlipEnabled( true ),
341 mPreEditFlag( false ),
342 mIgnoreCommitFlag( false ),
343 mIgnoreFirstCommitFlag( false ),
344 mSelectingText( false ),
345 mPreserveCursorPosition( false ),
346 mSelectTextOnCommit( false ),
347 mUnderlinedPriorToPreEdit ( false ),
348 mCommitByKeyInput( false ),
349 mPlaceHolderSet( false ),
350 mMarkUpEnabled( false )
352 // Updates the line height accordingly with the input style.
356 TextInput::~TextInput()
358 StopCursorBlinkTimer();
363 std::string TextInput::GetText() const
367 // Return text-view's text only if the text-input's text is not empty
368 // in order to not to return the placeholder text.
369 if( !mStyledText.empty() )
371 text = mDisplayedTextView.GetText();
377 std::string TextInput::GetMarkupText() const
379 std::string markupString;
380 MarkupProcessor::GetMarkupString( mStyledText, markupString );
385 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
387 // Get the placeholder styled text array from the markup string.
388 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
390 if( mStyledText.empty() )
392 // Set the placeholder text only if the styled text is empty.
393 mDisplayedTextView.SetText( mStyledPlaceHolderText );
394 mPlaceHolderSet = true;
398 std::string TextInput::GetPlaceholderText()
400 // Traverses the styled placeholder array getting only the text.
401 // Note that for some languages a 'character' could be represented by more than one 'char'
403 std::string placeholderText;
404 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
406 placeholderText.append( (*it).mText.GetText() );
409 return placeholderText ;
412 void TextInput::SetInitialText(const std::string& initialText)
414 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
416 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
418 mPreEditFlag = false;
419 mIgnoreCommitFlag = true;
422 SetText( initialText );
423 PreEditReset( false ); // Reset keyboard as text changed
426 void TextInput::SetText(const std::string& initialText)
428 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
430 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
432 if( mStyledText.empty() )
434 // If the initial text is empty, set the placeholder text.
435 mDisplayedTextView.SetText( mStyledPlaceHolderText );
436 mPlaceHolderSet = true;
440 mDisplayedTextView.SetText( mStyledText );
441 mPlaceHolderSet = false;
446 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
448 ImfManager imfManager = ImfManager::Get();
451 imfManager.SetCursorPosition( mCursorPosition );
452 imfManager.SetSurroundingText( initialText );
453 imfManager.NotifyCursorPosition();
456 if( IsScrollEnabled() )
458 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
461 ShowGrabHandleAndSetVisibility( false );
470 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
472 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
474 mDisplayedTextView.SetText( styleText );
475 mPlaceHolderSet = false;
477 // If text alignment hasn't been manually set by application developer, then we
478 // automatically determine the alignment based on the content of the text i.e. what
479 // language the text begins with.
480 // TODO: This should determine different alignments for each line (broken by '\n') of text.
481 if(!mOverrideAutomaticAlignment)
483 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
484 bool leftToRight(true);
486 if( !styleText.empty() )
488 bool breakOut(false);
490 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
492 const Text& text = textIter->mText;
494 for( std::size_t i = 0; i < text.GetLength(); ++i )
496 Character character( text[i] );
497 if( character.GetCharacterDirection() != Character::Neutral )
499 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
507 // Based on this direction, either left or right align text if not manually set by application developer.
508 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
509 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
510 Toolkit::Alignment::VerticalTop ) );
511 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
517 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
519 mMaxStringLength = maxChars;
522 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
524 DALI_ASSERT_DEBUG( maxLines > 0 )
528 mNumberOflinesLimit = maxLines;
532 std::size_t TextInput::GetNumberOfLinesLimit() const
534 return mNumberOflinesLimit;
537 std::size_t TextInput::GetNumberOfCharacters() const
539 return mStyledText.size();
543 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
545 mMaterialColor = color;
546 if ( mCustomMaterial )
548 mCustomMaterial.SetDiffuseColor( mMaterialColor );
549 mMeshData.SetMaterial( mCustomMaterial );
553 const Vector4& TextInput::GetMaterialDiffuseColor() const
555 return mMaterialColor;
560 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
562 return mInputStartedSignalV2;
565 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
567 return mInputFinishedSignalV2;
570 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
572 return mCutAndPasteToolBarDisplayedV2;
575 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
577 return mStyleChangedSignalV2;
580 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
582 return mTextModifiedSignal;
585 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
587 return mMaxInputCharactersReachedSignalV2;
590 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
592 return mInputTextExceedBoundariesSignalV2;
595 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
597 Dali::BaseHandle handle( object );
599 bool connected( true );
600 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
602 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
604 textInput.InputStartedSignal().Connect( tracker, functor );
606 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
608 textInput.InputFinishedSignal().Connect( tracker, functor );
610 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
612 textInput.StyleChangedSignal().Connect( tracker, functor );
614 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
616 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
618 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
620 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
624 // signalName does not match any signal
631 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
635 // update line height before calculate the actual position.
640 if( setCursorOnTouchPoint )
642 // Sets the cursor position for the given touch point.
643 ReturnClosestIndex( touchPoint, mCursorPosition );
645 // Creates the grab handle.
646 if( IsGrabHandleEnabled() )
648 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
652 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
653 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
654 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
655 ShowGrabHandleAndSetVisibility( true );
657 // Scrolls the text-view if needed.
658 if( IsScrollEnabled() )
660 ScrollTextViewToMakeCursorVisible( cursorPosition );
666 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
678 bool TextInput::IsEditable() const
680 return mEditModeActive;
683 void TextInput::SetEditOnTouch( bool editOnTouch )
685 mEditOnTouch = editOnTouch;
688 bool TextInput::IsEditOnTouch() const
693 void TextInput::SetTextSelectable( bool textSelectable )
695 mTextSelection = textSelectable;
698 bool TextInput::IsTextSelectable() const
700 return mTextSelection;
703 bool TextInput::IsTextSelected() const
705 return mHighlightMeshActor;
708 void TextInput::DeSelectText()
715 void TextInput::SetGrabHandleImage(Dali::Image image )
719 CreateGrabHandle(image);
723 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
725 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
729 mCursor.SetImage( image );
730 mCursor.SetNinePatchBorder( border );
734 Vector3 TextInput::GetSelectionHandleSize()
736 return DEFAULT_SELECTION_HANDLE_SIZE;
739 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
741 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
745 mCursorRTL.SetImage( image);
746 mCursorRTL.SetNinePatchBorder( border );
750 void TextInput::EnableGrabHandle(bool toggle)
752 // enables grab handle with will in turn de-activate magnifier
753 mGrabHandleEnabled = toggle;
756 bool TextInput::IsGrabHandleEnabled()
758 // if false then magnifier will be shown instead.
759 return mGrabHandleEnabled;
762 void TextInput::EnableSelectionHandleFlip( bool toggle )
764 // Deprecated function. To be removed.
765 mIsSelectionHandleFlipEnabled = toggle;
768 bool TextInput::IsSelectionHandleFlipEnabled()
770 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
774 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
776 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
777 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
778 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
780 mSelectionHandleFlipMargin = margin;
783 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
785 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
786 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
788 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
789 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
791 const Vector4 boundary( originX,
793 originX + boundingRectangle.width,
794 originY + boundingRectangle.height );
796 mBoundingRectangleWorldCoordinates = boundary;
798 // Set Boundary for Popup so it keeps the Pop-up within the area also.
799 mPopupPanel.SetPopupBoundary( boundingRectangle );
802 const Rect<float> TextInput::GetBoundingRectangle() const
804 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
806 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
807 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
809 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
814 const Vector4& TextInput::GetSelectionHandleFlipMargin()
816 return mSelectionHandleFlipMargin;
819 void TextInput::SetTextColor( const Vector4& color )
821 mDisplayedTextView.SetColor( color );
824 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
826 if( style != mInputStyle )
829 bool emitSignal = false;
831 // mask: modify style according to mask, if different emit signal.
832 const TextStyle oldInputStyle( mInputStyle );
834 // Copy the new style.
835 mInputStyle.Copy( style, mask );
837 // if style has changed, emit signal.
838 if( oldInputStyle != mInputStyle )
843 // Updates the line height accordingly with the input style.
846 // Changing font point size will require the cursor to be re-sized
851 EmitStyleChangedSignal();
856 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
858 if ( IsTextSelected() )
860 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
861 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
863 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
865 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
868 // Keeps the old style to be compared with the new one.
869 const TextStyle oldInputStyle( mInputStyle );
871 // Copy only those parameters from the style which are set in the mask.
872 mInputStyle.Copy( style, mask );
874 if( mInputStyle != oldInputStyle )
876 // Updates the line height accordingly with the input style.
879 EmitStyleChangedSignal();
884 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
886 if( !mStyledText.empty() )
888 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
892 TextStyle TextInput::GetStyleAtCursor() const
896 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
898 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
899 style = mStyledText.at( mCursorPosition-1 ).mStyle;
905 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
907 Dali::Font defaultFont = Dali::Font::New();
908 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
915 TextStyle TextInput::GetStyleAt( std::size_t position ) const
917 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
919 if( position >= mStyledText.size() )
921 position = mStyledText.size() - 1;
924 return mStyledText.at( position ).mStyle;
927 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
929 mDisplayedTextView.SetTextAlignment( align );
930 mOverrideAutomaticAlignment = true;
933 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
935 mDisplayedTextView.SetLineJustification( justification );
936 mOverrideAutomaticAlignment = true;
939 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
941 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
944 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
946 return mDisplayedTextView.GetFadeBoundary();
949 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
951 return mDisplayedTextView.GetTextAlignment();
954 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
956 mDisplayedTextView.SetMultilinePolicy( policy );
959 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
961 return mDisplayedTextView.GetMultilinePolicy();
964 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
966 mDisplayedTextView.SetWidthExceedPolicy( policy );
969 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
971 return mDisplayedTextView.GetWidthExceedPolicy();
974 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
976 mDisplayedTextView.SetHeightExceedPolicy( policy );
979 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
981 return mDisplayedTextView.GetHeightExceedPolicy();
984 void TextInput::SetExceedEnabled( bool enable )
986 mExceedEnabled = enable;
989 bool TextInput::GetExceedEnabled() const
991 return mExceedEnabled;
994 void TextInput::SetBackground(Dali::Image image )
996 // TODO Should add this function and add public api to match.
999 bool TextInput::OnTouchEvent(const TouchEvent& event)
1004 bool TextInput::OnKeyEvent(const KeyEvent& event)
1006 switch( event.state )
1008 case KeyEvent::Down:
1010 return OnKeyDownEvent(event);
1016 return OnKeyUpEvent(event);
1028 void TextInput::OnKeyInputFocusGained()
1030 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1032 mEditModeActive = true;
1034 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1036 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1038 // Updates the line height accordingly with the input style.
1041 // Connect the signals to use in text input.
1042 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1043 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1045 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1048 GetTextLayoutInfo();
1051 SetCursorVisibility( true );
1052 StartCursorBlinkTimer();
1054 Toolkit::TextInput handle( GetOwner() );
1055 mInputStartedSignalV2.Emit( handle );
1057 ImfManager imfManager = ImfManager::Get();
1061 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1063 // Notify that the text editing start.
1064 imfManager.Activate();
1066 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1067 imfManager.SetRestoreAferFocusLost( true );
1069 imfManager.SetCursorPosition( mCursorPosition );
1070 imfManager.NotifyCursorPosition();
1073 mClipboard = Clipboard::Get(); // Store handle to clipboard
1075 // Now in edit mode we can accept string to paste from clipboard
1076 if( Adaptor::IsAvailable() )
1078 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1081 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1086 void TextInput::OnKeyInputFocusLost()
1088 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1092 // If key input focus is lost, it removes the
1093 // underline from the last pre-edit text.
1094 RemovePreEditStyle();
1095 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1096 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1100 ImfManager imfManager = ImfManager::Get();
1103 // The text editing is finished. Therefore the imf manager don't have restore activation.
1104 imfManager.SetRestoreAferFocusLost( false );
1106 // Notify that the text editing finish.
1107 imfManager.Deactivate();
1109 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1111 // Disconnect signal used the text input.
1112 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1114 Toolkit::TextInput handle( GetOwner() );
1115 mInputFinishedSignalV2.Emit( handle );
1116 mEditModeActive = false;
1117 mPreEditFlag = false;
1119 SetCursorVisibility( false );
1120 StopCursorBlinkTimer();
1122 ShowGrabHandleAndSetVisibility( false );
1125 // No longer in edit mode so do not want to receive string from clipboard
1126 if( Adaptor::IsAvailable() )
1128 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1131 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1133 Clipboard clipboard = Clipboard::Get();
1137 clipboard.HideClipboard();
1142 void TextInput::OnControlStageConnection()
1144 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1146 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1148 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1152 void TextInput::CreateActiveLayer()
1154 Actor self = Self();
1155 mActiveLayer = Layer::New();
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 Image mCursorImage = Image::New( DEFAULT_CURSOR );
1174 mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1175 mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1177 Actor self = Self();
1178 self.Add( mCursor );
1179 self.Add( mCursorRTL );
1181 mCursorVisibility = false;
1183 CreateActiveLayer(); // todo move this so layer only created when needed.
1185 // Assign names to image actors
1186 mCursor.SetName("mainCursor");
1187 mCursorRTL.SetName("rtlCursor");
1190 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1192 mDisplayedTextView.SetSize( targetSize );
1193 GetTextLayoutInfo();
1194 mActiveLayer.SetSize(targetSize);
1197 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1199 Relayout( mDisplayedTextView, size, container );
1200 Relayout( mPopupPanel.GetRootActor(), size, container );
1202 GetTextLayoutInfo();
1207 Vector3 TextInput::GetNaturalSize()
1209 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1211 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1213 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1214 naturalSize.height = mLineHeight;
1220 float TextInput::GetHeightForWidth( float width )
1222 float height = mDisplayedTextView.GetHeightForWidth( width );
1224 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1226 // If the height is zero, it means there is no text. Let's return the cursor height.
1227 height = mLineHeight;
1233 /*end of Virtual methods from parent*/
1235 // Private Internal methods
1237 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1239 switch (gesture.state)
1241 case Gesture::Started:
1242 // fall through so code not duplicated
1243 case Gesture::Continuing:
1245 if (actor == mGrabArea)
1247 SetCursorVisibility( true );
1248 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1249 MoveGrabHandle( gesture.displacement );
1250 HidePopup(); // Do not show popup whilst handle is moving
1252 else if (actor == mHandleOneGrabArea)
1254 // the displacement in PanGesture is affected by the actor's rotation.
1255 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1256 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1258 MoveSelectionHandle( HandleOne, gesture.displacement );
1260 mState = StateDraggingHandle;
1263 else if (actor == mHandleTwoGrabArea)
1265 // the displacement in PanGesture is affected by the actor's rotation.
1266 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1267 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1269 MoveSelectionHandle( HandleTwo, gesture.displacement );
1271 mState = StateDraggingHandle;
1277 case Gesture::Finished:
1279 // Revert back to non-pressed selection handle images
1280 if (actor == mGrabArea)
1282 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1283 SetCursorVisibility( true );
1284 SetUpPopupSelection();
1287 if (actor == mHandleOneGrabArea)
1289 // the displacement in PanGesture is affected by the actor's rotation.
1290 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1291 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1293 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1295 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1297 ShowPopupCutCopyPaste();
1299 if (actor == mHandleTwoGrabArea)
1301 // the displacement in PanGesture is affected by the actor's rotation.
1302 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1303 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1305 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1307 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1309 ShowPopupCutCopyPaste();
1318 // Stop the flashing animation so easy to see when moved.
1319 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1321 if (touch.GetPoint(0).state == TouchPoint::Down)
1323 SetCursorVisibility( true );
1324 StopCursorBlinkTimer();
1326 else if (touch.GetPoint(0).state == TouchPoint::Up)
1328 SetCursorVisibility( true );
1329 StartCursorBlinkTimer();
1334 // selection handle one
1335 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1337 if (touch.GetPoint(0).state == TouchPoint::Down)
1339 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1341 else if (touch.GetPoint(0).state == TouchPoint::Up)
1343 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1348 // selection handle two
1349 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1351 if (touch.GetPoint(0).state == TouchPoint::Down)
1353 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1355 else if (touch.GetPoint(0).state == TouchPoint::Up)
1357 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1362 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1364 // If text exists then select nearest word.
1365 if ( !mStyledText.empty())
1369 ShowGrabHandleAndSetVisibility( false );
1374 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1375 // converts the pre-edit word being displayed to a committed word.
1376 if ( !mUnderlinedPriorToPreEdit )
1379 style.SetUnderline( false );
1380 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1382 mPreEditFlag = false;
1383 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1384 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1385 PreEditReset( false );
1387 mCursorPosition = 0;
1389 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1390 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1392 ImfManager imfManager = ImfManager::Get();
1395 imfManager.SetCursorPosition ( mCursorPosition );
1396 imfManager.NotifyCursorPosition();
1399 std::size_t start = 0;
1400 std::size_t end = 0;
1401 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1403 SelectText( start, end );
1405 // if no text but clipboard has content then show paste option
1406 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1408 ShowPopupCutCopyPaste();
1411 // If no text and clipboard empty then do nothing
1414 // TODO: Change the function name to be more general.
1415 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1417 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1418 , (mEditOnTouch)?"true":"false"
1419 , (mEditModeActive)?"true":"false");
1421 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1426 if( mGrabArea == actor )
1428 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1430 SetUpPopupSelection();
1440 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1442 // Initially don't create the grab handle.
1443 bool createGrabHandle = false;
1445 if ( !mEditModeActive )
1447 // update line height before calculate the actual position.
1450 // Only start edit mode if TextInput configured to edit on touch
1453 // Set the initial cursor position in the tap point.
1454 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1456 // Create the grab handle.
1457 // TODO Make this a re-usable function.
1458 if ( IsGrabHandleEnabled() )
1460 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1464 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1465 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1466 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1467 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1471 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1472 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1473 // otherwise the Grab handle will be shown when selecting.
1480 // Show the keyboard if it was hidden.
1481 if (!VirtualKeyboard::IsVisible())
1483 VirtualKeyboard::Show();
1486 // Reset keyboard as tap event has occurred.
1487 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1488 PreEditReset( true );
1490 GetTextLayoutInfo();
1492 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1494 // 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.
1496 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1498 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1500 // Notify keyboard so it can 're-capture' word for predictive text.
1501 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1502 ImfManager imfManager = ImfManager::Get();
1505 imfManager.SetCursorPosition ( mCursorPosition );
1506 imfManager.NotifyCursorPosition();
1508 const TextStyle oldInputStyle( mInputStyle );
1510 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1514 // Create the grab handle.
1515 // Grab handle is created later.
1516 createGrabHandle = true;
1518 if( oldInputStyle != mInputStyle )
1520 // Updates the line height accordingly with the input style.
1523 EmitStyleChangedSignal();
1528 if ( createGrabHandle && IsGrabHandleEnabled() )
1530 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1534 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1535 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1536 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1537 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1542 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1544 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1546 if(longPress.state == Dali::Gesture::Started)
1548 // Start edit mode on long press
1549 if ( !mEditModeActive )
1554 // If text exists then select nearest word.
1555 if ( !mStyledText.empty())
1559 ShowGrabHandleAndSetVisibility( false );
1564 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1565 // converts the pre-edit word being displayed to a committed word.
1566 if ( !mUnderlinedPriorToPreEdit )
1569 style.SetUnderline( false );
1570 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1572 mPreEditFlag = false;
1573 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1574 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1575 PreEditReset( false );
1577 mCursorPosition = 0;
1579 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1580 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1582 ImfManager imfManager = ImfManager::Get();
1585 imfManager.SetCursorPosition ( mCursorPosition );
1586 imfManager.NotifyCursorPosition();
1588 std::size_t start = 0;
1589 std::size_t end = 0;
1590 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1592 SelectText( start, end );
1595 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1596 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1598 ShowPopupCutCopyPaste();
1603 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1605 const Text clipboardText( notifier.GetContent() );
1606 PasteText( clipboardText );
1608 SetCursorVisibility( true );
1609 StartCursorBlinkTimer();
1611 ShowGrabHandleAndSetVisibility( false );
1617 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1619 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1621 const std::string& name = button.GetName();
1623 if(name == TextInputPopup::OPTION_SELECT_WORD)
1625 std::size_t start = 0;
1626 std::size_t end = 0;
1627 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1629 SelectText( start, end );
1631 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1633 SetCursorVisibility(false);
1634 StopCursorBlinkTimer();
1636 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1637 std::size_t start = 0;
1639 SelectText( start, end );
1641 else if(name == TextInputPopup::OPTION_CUT)
1643 bool ret = CopySelectedTextToClipboard();
1647 DeleteHighlightedText( true );
1651 SetCursorVisibility( true );
1652 StartCursorBlinkTimer();
1656 else if(name == TextInputPopup::OPTION_COPY)
1658 CopySelectedTextToClipboard();
1662 SetCursorVisibility( true );
1663 StartCursorBlinkTimer();
1667 else if(name == TextInputPopup::OPTION_PASTE)
1669 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1671 PasteText(retrievedString);
1673 SetCursorVisibility( true );
1674 StartCursorBlinkTimer();
1676 ShowGrabHandleAndSetVisibility( false );
1680 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1682 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1683 // Hence pass the false parameter for signalFinished.
1684 HidePopup( true, false );
1685 mClipboard.ShowClipboard();
1691 bool TextInput::OnCursorBlinkTimerTick()
1694 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1695 if ( mCursorRTLEnabled )
1697 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1699 mCursorBlinkStatus = !mCursorBlinkStatus;
1704 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1706 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1708 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1709 if(mHighlightMeshActor && mState == StateEdit)
1711 ShowPopupCutCopyPaste();
1715 //FIXME this routine needs to be re-written as it contains too many branches.
1716 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1718 std::string keyName = event.keyPressedName;
1719 std::string keyString = event.keyPressed;
1721 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1723 // Do not consume "Tab" and "Escape" keys.
1724 if(keyName == "Tab" || keyName == "Escape")
1726 // Escape key to end the edit mode
1732 HidePopup(); // If Pop-up shown then hides it as editing text.
1734 // Update Flag, indicates whether to update the text-input contents or not.
1735 // Any key stroke that results in a visual change of the text-input should
1736 // set this flag to true.
1739 // Whether to scroll text to cursor position.
1740 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1741 bool scroll = false;
1743 if (keyName == "Return")
1745 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1747 bool preEditFlagPreviouslySet( mPreEditFlag );
1749 if (mHighlightMeshActor)
1751 // replaces highlighted text with new line
1752 DeleteHighlightedText( false );
1754 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1756 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1757 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1760 mCommitByKeyInput = true;
1763 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1764 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1766 mPreEditFlag = true;
1767 mIgnoreCommitFlag = false;
1777 else if ( keyName == "space" )
1779 if ( mHighlightMeshActor )
1781 // Some text is selected so erase it before adding space.
1782 DeleteHighlightedText( true );
1786 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1788 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1789 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1792 mCommitByKeyInput = true;
1797 else if (keyName == "BackSpace")
1799 if ( mHighlightMeshActor )
1801 // Some text is selected so erase it
1802 DeleteHighlightedText( true );
1807 if ( mCursorPosition > 0 )
1809 DeleteCharacter( mCursorPosition );
1815 else if (keyName == "Right")
1820 else if (keyName == "Left")
1822 AdvanceCursor(true);
1825 else // event is a character
1827 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1828 if ( !keyString.empty() )
1830 if ( mHighlightMeshActor )
1832 // replaces highlighted text with new character
1833 DeleteHighlightedText( false );
1837 // Received key String
1838 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1844 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1845 // as this is a costly operation.
1851 if(update || scroll)
1853 if( IsScrollEnabled() )
1855 // Calculates the new cursor position (in actor coordinates)
1856 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1858 ScrollTextViewToMakeCursorVisible( cursorPosition );
1865 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1867 std::string keyName = event.keyPressedName;
1868 std::string keyString = event.keyPressed;
1870 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1872 // The selected text become deselected when the key code is DALI_KEY_BACK.
1873 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1882 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1884 // Updates the stored scroll position.
1885 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1887 const Vector3& controlSize = GetControlSize();
1888 Size cursorSize( CURSOR_THICKNESS, 0.f );
1890 // Updates the cursor and grab handle position and visibility.
1891 if( mGrabHandle || mCursor )
1893 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1894 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1896 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1898 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1902 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1903 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1908 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1909 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1913 // Updates the selection handles and highlighted text position and visibility.
1914 if( mSelectionHandleOne && mSelectionHandleTwo )
1916 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1917 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1918 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1919 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1920 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1921 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1923 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1924 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1926 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1927 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1928 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1929 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1931 if( mHighlightMeshActor )
1933 mHighlightMeshActor.SetVisible( true );
1939 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1941 // Scroll the text to make the cursor visible.
1942 const Size cursorSize( CURSOR_THICKNESS,
1943 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1945 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1947 const Vector3& controlSize = GetControlSize();
1949 // Calculates the new scroll position.
1950 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1951 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1953 scrollOffset.x += cursorPosition.x;
1956 if( cursorPosition.y - cursorSize.height < 0.f )
1958 scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1960 else if( cursorPosition.y > controlSize.height )
1962 scrollOffset.y += cursorPosition.y;
1965 // Sets the new scroll position.
1966 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1967 SetScrollPosition( scrollOffset );
1970 void TextInput::StartScrollTimer()
1974 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1975 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1978 if( !mScrollTimer.IsRunning() )
1980 mScrollTimer.Start();
1984 void TextInput::StopScrollTimer()
1988 mScrollTimer.Stop();
1992 bool TextInput::OnScrollTimerTick()
1994 // TODO: need to set the new style accordingly the new handle position.
1996 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1998 // nothing to do if all handles are invisible or doesn't exist.
2004 // Choose between the grab handle or the selection handles.
2005 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2006 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2007 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2009 std::size_t newCursorPosition = 0;
2010 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2012 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2013 // the new selection handle's position needs to be different of the other one.
2014 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2015 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2016 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2018 if( differentSelectionHandles )
2020 handlePosition = newCursorPosition;
2022 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2024 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2026 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2027 scrollPosition += scrollDelta;
2028 SetScrollPosition( scrollPosition );
2030 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2035 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2038 actualHandlePosition.x += mScrollDisplacement.x;
2039 actualHandlePosition.y += mScrollDisplacement.y;
2044 // Public Internal Methods (public for testing purpose)
2046 void TextInput::SetUpTouchEvents()
2048 if ( !mTapDetector )
2050 mTapDetector = TapGestureDetector::New();
2051 // Attach the actors and connect the signal
2052 mTapDetector.Attach(Self());
2054 // As contains children which may register for tap the default control detector is not used.
2055 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2058 if ( !mDoubleTapDetector )
2060 mDoubleTapDetector = TapGestureDetector::New();
2061 mDoubleTapDetector.SetTapsRequired( 2 );
2062 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2064 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2065 // so that we do not, unnecessarily, have a double tap request all the time
2068 if ( !mPanGestureDetector )
2070 mPanGestureDetector = PanGestureDetector::New();
2071 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2074 if ( !mLongPressDetector )
2076 mLongPressDetector = LongPressGestureDetector::New();
2077 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2078 mLongPressDetector.Attach(Self());
2082 void TextInput::CreateTextViewActor()
2084 mDisplayedTextView = Toolkit::TextView::New();
2085 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2086 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2087 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2088 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2089 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2090 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2091 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2092 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2093 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2094 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2096 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2098 Self().Add( mDisplayedTextView );
2101 // Start a timer to initiate, used by the cursor to blink.
2102 void TextInput::StartCursorBlinkTimer()
2104 if ( !mCursorBlinkTimer )
2106 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2107 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2110 if ( !mCursorBlinkTimer.IsRunning() )
2112 mCursorBlinkTimer.Start();
2116 // Start a timer to initiate, used by the cursor to blink.
2117 void TextInput::StopCursorBlinkTimer()
2119 if ( mCursorBlinkTimer )
2121 mCursorBlinkTimer.Stop();
2125 void TextInput::StartEditMode()
2127 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2129 if(!mEditModeActive)
2134 if ( mDoubleTapDetector )
2136 mDoubleTapDetector.Attach( Self() );
2140 void TextInput::EndEditMode()
2142 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2144 ClearKeyInputFocus();
2146 if ( mDoubleTapDetector )
2148 mDoubleTapDetector.Detach( Self() );
2152 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2154 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2156 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2158 style.SetUnderline( true );
2159 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2163 void TextInput::RemovePreEditStyle()
2165 if ( !mUnderlinedPriorToPreEdit )
2168 style.SetUnderline( false );
2169 SetActiveStyle( style, TextStyle::UNDERLINE );
2173 // IMF related methods
2176 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2178 bool update( false );
2179 bool preeditResetRequired ( false );
2181 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2183 HidePopup(); // If Pop-up shown then hides it as editing text.
2186 switch ( imfEvent.eventName )
2188 case ImfManager::PREEDIT:
2190 mIgnoreFirstCommitFlag = false;
2192 // 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
2193 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2195 // replaces highlighted text with new character
2196 DeleteHighlightedText( false );
2199 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2201 if( IsScrollEnabled() )
2203 // Calculates the new cursor position (in actor coordinates)
2204 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2205 ScrollTextViewToMakeCursorVisible( cursorPosition );
2212 case ImfManager::COMMIT:
2214 if( mIgnoreFirstCommitFlag )
2216 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2217 mIgnoreFirstCommitFlag = false;
2221 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2223 // 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
2224 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2226 // replaces highlighted text with new character
2227 DeleteHighlightedText( false );
2230 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2231 // not needed, one such scenario is when the pre-edit word is too long to fit.
2232 if ( !mIgnoreCommitFlag )
2234 update = CommitReceived( imfEvent.predictiveString );
2238 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2244 if( IsScrollEnabled() )
2246 // Calculates the new cursor position (in actor coordinates)
2247 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2249 ScrollTextViewToMakeCursorVisible( cursorPosition );
2254 case ImfManager::DELETESURROUNDING:
2256 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2257 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2259 mPreEditFlag = false;
2261 std::size_t toDelete = 0;
2262 std::size_t numberOfCharacters = 0;
2264 if( mHighlightMeshActor )
2266 // delete highlighted text.
2267 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2268 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2272 if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2274 toDelete = mCursorPosition + imfEvent.cursorOffset;
2276 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2278 numberOfCharacters = mStyledText.size() - toDelete;
2282 numberOfCharacters = imfEvent.numberOfChars;
2285 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2286 DeleteRange( toDelete, numberOfCharacters );
2288 mCursorPosition = toDelete;
2289 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2293 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2296 case ImfManager::GETSURROUNDING:
2298 // 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
2299 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2300 if (! ( mHighlightMeshActor || mSelectingText ) )
2302 std::string text( GetText() );
2303 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2305 imfManager.SetCursorPosition( mCursorPosition );
2306 imfManager.SetSurroundingText( text );
2309 if( 0 != mNumberOfSurroundingCharactersDeleted )
2311 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2312 mNumberOfSurroundingCharactersDeleted = 0;
2314 if( mStyledText.empty() )
2316 // Styled text is empty, so set the placeholder text.
2317 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2318 mPlaceHolderSet = true;
2323 case ImfManager::VOID:
2325 DALI_ASSERT_DEBUG( false );
2329 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2331 return callbackData;
2334 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2336 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2338 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2339 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2341 bool preeditResetRequest ( false );
2343 if( mPreEditFlag ) // Already in pre-edit state.
2345 if( mStyledText.size() >= mMaxStringLength )
2347 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2348 // Cannot fit these characters into field, clear pre-edit.
2349 if ( !mUnderlinedPriorToPreEdit )
2352 style.SetUnderline( false );
2353 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2355 mIgnoreCommitFlag = true;
2356 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2357 mPreEditFlag = false;
2358 EmitMaxInputCharactersReachedSignal();
2362 // delete existing pre-edit string
2363 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2365 // Store new pre-edit string
2366 mPreEditString.SetText( keyString );
2368 if ( keyString.empty() )
2370 mPreEditFlag = false;
2371 mCursorPosition = mPreEditStartPosition;
2373 if( mStyledText.empty() )
2375 // Styled text is empty, so set the placeholder text.
2376 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2377 mPlaceHolderSet = true;
2381 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2383 GetTextLayoutInfo();
2388 // Insert new pre-edit string. InsertAt updates the size and position table.
2389 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2390 // 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.
2391 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2392 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2393 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2396 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2400 else // mPreEditFlag not set
2402 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2404 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2405 // new pre-edit so move into pre-edit state by setting flag
2406 mPreEditFlag = true;
2407 mPreEditString.SetText( keyString ); // store new pre-edit string
2408 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2409 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2410 // 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.
2411 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2412 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2413 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2414 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2420 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2424 return preeditResetRequest;
2427 bool TextInput::CommitReceived(const std::string& keyString )
2429 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2430 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2432 bool update( false );
2434 RemovePreEditStyle();
2436 const std::size_t styledTextSize( mStyledText.size() );
2437 if( styledTextSize >= mMaxStringLength )
2439 // Cannot fit these characters into field, clear pre-edit.
2442 mIgnoreCommitFlag = true;
2443 mPreEditFlag = false;
2445 EmitMaxInputCharactersReachedSignal();
2451 // delete existing pre-edit string
2452 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2453 mPreEditFlag = false;
2455 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2456 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2458 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2460 // No need to update cursor position as Cursor location given by touch.
2461 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2462 mPreserveCursorPosition = false;
2466 // Cursor not set by touch so needs to be re-positioned to input more text
2467 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2469 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2470 if ( mCommitByKeyInput )
2472 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2473 mCommitByKeyInput = false;
2479 if ( mSelectTextOnCommit )
2481 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2486 else // mPreEditFlag not set
2488 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2490 if( mStyledText.empty() && mPlaceHolderSet )
2492 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2493 mDisplayedTextView.SetText( "" );
2494 mNumberOfSurroundingCharactersDeleted = 0;
2495 mPlaceHolderSet = false;
2497 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2499 mNumberOfSurroundingCharactersDeleted = 0;
2504 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2509 mSelectTextOnCommit = false;
2511 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2512 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2517 // End of IMF related methods
2519 std::size_t TextInput::DeletePreEdit()
2521 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2523 DALI_ASSERT_DEBUG( mPreEditFlag );
2525 const std::size_t preEditStringLength = mPreEditString.GetLength();
2526 const std::size_t styledTextSize = mStyledText.size();
2528 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2530 // Prevents erase items outside mStyledText bounds.
2531 if( mPreEditStartPosition > styledTextSize )
2533 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2534 mPreEditStartPosition = styledTextSize;
2537 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2539 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2540 endPosition = styledTextSize;
2543 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2545 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2546 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2548 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2550 return preEditStringLength;
2553 void TextInput::PreEditReset( bool preserveCursorPosition )
2555 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2556 preserveCursorPosition, mCursorPosition);
2558 // 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.
2559 mPreserveCursorPosition = preserveCursorPosition;
2561 // Reset incase we are in a pre-edit state.
2562 ImfManager imfManager = ImfManager::Get();
2565 imfManager.Reset(); // Will trigger a commit message
2569 void TextInput::CursorUpdate()
2573 ImfManager imfManager = ImfManager::Get();
2576 std::string text( GetText() );
2577 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2578 imfManager.SetCursorPosition ( mCursorPosition );
2579 imfManager.NotifyCursorPosition();
2583 /* Delete highlighted characters redisplay*/
2584 void TextInput::DeleteHighlightedText( bool inheritStyle )
2586 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2588 if(mHighlightMeshActor)
2590 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2592 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2593 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2595 // Get the styled text of the characters to be deleted as it may be needed if
2596 // the "exceed the text-input's boundaries" option is disabled.
2597 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2599 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2601 mStyledText.erase( start, end ); // erase range of characters
2603 // Remove text from TextView.
2605 if( mStyledText.empty() )
2607 // Styled text is empty, so set the placeholder text.
2608 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2609 mPlaceHolderSet = true;
2613 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2615 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2617 // It may happen than after removing a white space or a new line character,
2618 // two words merge, this new word could be big enough to not fit in its
2619 // current line, so moved to the next one, and make some part of the text to
2620 // exceed the text-input's boundary.
2621 if( !mExceedEnabled )
2623 // Get the new text layout after removing some characters.
2624 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2626 // Get text-input's size.
2627 const Vector3& size = GetControlSize();
2629 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2630 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2632 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2634 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2635 styledCharactersToDelete.begin(),
2636 styledCharactersToDelete.end() );
2640 GetTextLayoutInfo();
2646 const TextStyle oldInputStyle( mInputStyle );
2648 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2650 if( oldInputStyle != mInputStyle )
2652 // Updates the line height accordingly with the input style.
2655 EmitStyleChangedSignal();
2661 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2663 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2664 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2666 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2669 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2671 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2672 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2674 mStyledText.erase(itStart, itEnd);
2676 // update the selection handles if they are visible.
2677 if( mHighlightMeshActor )
2679 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2680 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2682 if( minHandle >= start + ncharacters )
2684 minHandle -= ncharacters;
2686 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2691 if( maxHandle >= start + ncharacters )
2693 maxHandle -= ncharacters;
2695 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2701 // 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.
2704 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2706 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2707 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2708 // Mean we do not re-draw the text more than we have too.
2711 /* Delete character at current cursor position and redisplay*/
2712 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2714 // Ensure positionToDelete is not out of bounds.
2715 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2716 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2717 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2719 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2722 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2724 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2726 // Get the styled text of the character to be deleted as it may be needed if
2727 // the "exceed the text-input's boundaries" option is disabled.
2728 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2730 mStyledText.erase(it); // erase the character left of positionToDelete
2732 if( mStyledText.empty() )
2734 // Styled text is empty, so set the placeholder text.
2735 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2736 mPlaceHolderSet = true;
2740 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2742 const Character characterToDelete = styledCharacterToDelete.mText[0];
2744 // It may happen than after removing a white space or a new line character,
2745 // two words merge, this new word could be big enough to not fit in its
2746 // current line, so moved to the next one, and make some part of the text to
2747 // exceed the text-input's boundary.
2748 if( !mExceedEnabled )
2750 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2752 // Get the new text layout after removing one character.
2753 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2755 // Get text-input's size.
2756 const Vector3& size = GetControlSize();
2758 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2759 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2761 MarkupProcessor::StyledTextArray array;
2762 array.push_back( styledCharacterToDelete );
2763 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2765 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2770 GetTextLayoutInfo();
2772 ShowGrabHandleAndSetVisibility( false );
2774 mCursorPosition = positionToDelete -1;
2776 const TextStyle oldInputStyle( mInputStyle );
2778 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2780 if( oldInputStyle != mInputStyle )
2782 // Updates the line height accordingly with the input style.
2785 EmitStyleChangedSignal();
2790 /*Insert new character into the string and (optionally) redisplay text-input*/
2791 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2793 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2795 // Ensure insertionPosition is not out of bounds.
2796 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2798 bool textExceedsMaximunNumberOfCharacters = false;
2799 bool textExceedsBoundary = false;
2800 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2802 ShowGrabHandleAndSetVisibility( false );
2804 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2808 mIgnoreCommitFlag = true;
2809 mPreEditFlag = false;
2810 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2811 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2814 if( textExceedsMaximunNumberOfCharacters )
2816 EmitMaxInputCharactersReachedSignal();
2819 if( textExceedsBoundary )
2821 EmitInputTextExceedsBoundariesSignal();
2822 PreEditReset( false );
2826 return insertedStringLength;
2829 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2835 cursor = ImageActor::New( cursorImage );
2839 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2842 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2843 cursor.SetNinePatchBorder( border );
2845 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2846 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2847 cursor.SetVisible(false);
2852 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2854 // As cursor is not moving due to grab handle, handle should be hidden.
2855 ShowGrabHandleAndSetVisibility( false );
2857 bool cursorPositionChanged = false;
2860 if ( mCursorPosition >= places )
2862 mCursorPosition = mCursorPosition - places;
2863 cursorPositionChanged = true;
2868 if ((mCursorPosition + places) <= mStyledText.size())
2870 mCursorPosition = mCursorPosition + places;
2871 cursorPositionChanged = true;
2875 if( cursorPositionChanged )
2877 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2879 const TextStyle oldInputStyle( mInputStyle );
2880 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2884 if( oldInputStyle != mInputStyle )
2886 // Updates the line height accordingly with the input style.
2889 EmitStyleChangedSignal();
2892 ImfManager imfManager = ImfManager::Get();
2895 imfManager.SetCursorPosition ( mCursorPosition );
2896 imfManager.NotifyCursorPosition();
2901 void TextInput::DrawCursor(const std::size_t nthChar)
2903 // Get height of cursor and set its size
2904 Size size( CURSOR_THICKNESS, 0.0f );
2905 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2907 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2911 // Measure Font so know how big text will be if no initial text to measure.
2912 size.height = mLineHeight;
2915 mCursor.SetSize(size);
2917 // If the character is italic then the cursor also tilts.
2918 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2920 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2922 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2924 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2925 bool altPositionValid; // Alternate cursor validity flag.
2926 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2927 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2929 SetAltCursorEnabled( altPositionValid );
2931 if(!altPositionValid)
2933 mCursor.SetPosition( position + UI_OFFSET );
2937 size.height *= 0.5f;
2938 mCursor.SetSize(size);
2939 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2941 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2942 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2943 size.height = rowSize.height * 0.5f;
2944 mCursorRTL.SetSize(size);
2945 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2948 if( IsScrollEnabled() )
2950 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2951 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2956 void TextInput::SetAltCursorEnabled( bool enabled )
2958 mCursorRTLEnabled = enabled;
2959 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2962 void TextInput::SetCursorVisibility( bool visible )
2964 mCursorVisibility = visible;
2965 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2966 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2969 void TextInput::CreateGrabHandle( Dali::Image image )
2975 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2979 mGrabHandleImage = image;
2982 mGrabHandle = ImageActor::New(mGrabHandleImage);
2983 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2984 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2986 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2988 ShowGrabHandleAndSetVisibility( false );
2990 CreateGrabArea( mGrabHandle );
2992 mActiveLayer.Add(mGrabHandle);
2996 void TextInput::CreateGrabArea( Actor& parent )
2998 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2999 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3000 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3001 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3002 mTapDetector.Attach( mGrabArea );
3003 mPanGestureDetector.Attach( mGrabArea );
3005 parent.Add(mGrabArea);
3008 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3010 Vector3 actualHandlePosition;
3014 mActualGrabHandlePosition.x += displacement.x;
3015 mActualGrabHandlePosition.y += displacement.y;
3017 // Grab handle should jump to the nearest character and take cursor with it
3018 std::size_t newCursorPosition = 0;
3019 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3021 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3023 bool handleVisible = true;
3025 if( IsScrollEnabled() )
3027 const Vector3 controlSize = GetControlSize();
3028 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3029 // Scrolls the text if the handle is not in a visible position
3030 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3037 mCurrentHandlePosition = actualHandlePosition;
3038 mScrollDisplacement = Vector2::ZERO;
3042 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3044 mScrollDisplacement.x = -SCROLL_SPEED;
3046 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3048 mScrollDisplacement.x = SCROLL_SPEED;
3050 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3052 mScrollDisplacement.y = -SCROLL_SPEED;
3054 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3056 mScrollDisplacement.y = SCROLL_SPEED;
3062 if( handleVisible && // Only redraw cursor and do updates if position changed
3063 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3065 mCursorPosition = newCursorPosition;
3067 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3069 const TextStyle oldInputStyle( mInputStyle );
3071 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3073 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3075 if( oldInputStyle != mInputStyle )
3077 // Updates the line height accordingly with the input style.
3080 EmitStyleChangedSignal();
3085 return actualHandlePosition;
3088 void TextInput::ShowGrabHandle( bool visible )
3090 if ( IsGrabHandleEnabled() )
3094 mGrabHandle.SetVisible( mGrabHandleVisibility );
3096 StartMonitoringStageForTouch();
3100 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3102 mGrabHandleVisibility = visible;
3103 ShowGrabHandle( visible );
3106 // Callbacks connected to be Property notifications for Boundary checking.
3108 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3110 mIsSelectionHandleOneFlipped = true;
3111 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3112 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3115 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3117 mIsSelectionHandleOneFlipped = false;
3118 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3119 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3122 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3124 mIsSelectionHandleTwoFlipped = true;
3125 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3126 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3129 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3131 mIsSelectionHandleTwoFlipped = false;
3132 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3133 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3136 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3137 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3139 mSelectionHandleOne.SetOpacity(0.0f);
3142 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3144 mSelectionHandleOne.SetOpacity(1.0f);
3147 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3149 mSelectionHandleTwo.SetOpacity(0.0f);
3152 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3154 mSelectionHandleTwo.SetOpacity(1.0f);
3157 // End of Callbacks connected to be Property notifications for Boundary checking.
3159 void TextInput::SetUpHandlePropertyNotifications()
3161 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3163 Vector3 handlesize = GetSelectionHandleSize();
3165 // Exceeding horizontal boundary
3166 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3167 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3169 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3170 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3172 // Within horizontal boundary
3173 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3174 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3176 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3177 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3179 // Exceeding vertical boundary
3180 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3181 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3182 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3183 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3185 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3186 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3187 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3188 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3190 // Within vertical boundary
3191 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3192 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3193 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3194 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3196 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3197 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3198 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3199 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3202 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3204 mSelectionHandleOnePosition = start;
3205 mSelectionHandleTwoPosition = end;
3207 if ( !mSelectionHandleOne )
3209 // create normal and pressed images
3210 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3211 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3213 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3214 mSelectionHandleOne.SetName("SelectionHandleOne");
3215 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3216 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3217 mIsSelectionHandleOneFlipped = false;
3218 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3220 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3221 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3223 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3224 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3226 mTapDetector.Attach( mHandleOneGrabArea );
3227 mPanGestureDetector.Attach( mHandleOneGrabArea );
3229 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3231 mSelectionHandleOne.Add( mHandleOneGrabArea );
3232 mActiveLayer.Add( mSelectionHandleOne );
3235 if ( !mSelectionHandleTwo )
3237 // create normal and pressed images
3238 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3239 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3241 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3242 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3243 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3244 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3245 mIsSelectionHandleTwoFlipped = false;
3246 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3248 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3249 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3250 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3251 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3253 mTapDetector.Attach( mHandleTwoGrabArea );
3254 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3256 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3258 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3260 mActiveLayer.Add( mSelectionHandleTwo );
3263 SetUpHandlePropertyNotifications();
3265 // update table as text may have changed.
3266 GetTextLayoutInfo();
3268 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3269 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3271 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3272 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3274 // Calculates and set the visibility if the scroll mode is enabled.
3275 bool isSelectionHandleOneVisible = true;
3276 bool isSelectionHandleTwoVisible = true;
3277 if( IsScrollEnabled() )
3279 const Vector3& controlSize( GetControlSize() );
3280 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3281 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3282 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3283 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3286 CreateHighlight(); // function will only create highlight if not already created.
3289 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3291 Vector3 actualHandlePosition;
3293 if ( mSelectionHandleOne && mSelectionHandleTwo )
3295 const Vector3& controlSize = GetControlSize();
3297 Size cursorSize( CURSOR_THICKNESS, 0.f );
3299 // Get a reference of the wanted selection handle (handle one or two).
3300 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3302 // Get a reference for the current position of the handle and a copy of its pair
3303 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3304 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3306 // Get a handle of the selection handle actor
3307 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3309 // Selection handles should jump to the nearest character
3310 std::size_t newHandlePosition = 0;
3311 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3313 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3315 bool handleVisible = true;
3317 if( IsScrollEnabled() )
3319 mCurrentSelectionId = handleId;
3321 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3322 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3323 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3330 mCurrentSelectionHandlePosition = actualHandlePosition;
3331 mScrollDisplacement = Vector2::ZERO;
3335 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3337 mScrollDisplacement.x = -SCROLL_SPEED;
3339 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3341 mScrollDisplacement.x = SCROLL_SPEED;
3343 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3345 mScrollDisplacement.y = -SCROLL_SPEED;
3347 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3349 mScrollDisplacement.y = SCROLL_SPEED;
3355 if ( handleVisible && // Ensure the handle is visible.
3356 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3357 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3359 currentSelectionHandlePosition = newHandlePosition;
3361 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3362 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3366 if ( handleId == HandleOne )
3368 const TextStyle oldInputStyle( mInputStyle );
3370 // Set Active Style to that of first character in selection
3371 if( mSelectionHandleOnePosition < mStyledText.size() )
3373 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3376 if( oldInputStyle != mInputStyle )
3378 // Updates the line height accordingly with the input style.
3381 EmitStyleChangedSignal();
3387 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3390 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3393 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3394 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3396 if ( selectionHandleActor )
3398 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3399 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3400 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3402 if( IsScrollEnabled() )
3404 const Size cursorSize( CURSOR_THICKNESS,
3405 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3406 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3408 GetControlSize() ) );
3413 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3415 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3416 // For now the visual position of end of logical string will be end of visual string.
3417 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3419 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3422 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3424 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3425 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3426 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3427 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3429 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3431 // Deselect text prior to startSelectionIt
3432 for(;it!=startSelectionIt;++it)
3434 selectedVisualText[*it] = false;
3437 // Select text from startSelectionIt -> endSelectionIt
3438 for(;it!=endSelectionIt;++it)
3440 selectedVisualText[*it] = true;
3443 // Deselect text after endSelection
3446 selectedVisualText[*it] = false;
3449 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3452 // Calculate the dimensions of the quads they will make the highlight mesh
3453 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3455 // At the moment there is no public API to modify the block alignment option.
3456 const bool blockAlignEnabled = true;
3458 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3460 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3462 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3463 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3465 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3466 std::vector<bool> selectedVisualText;
3467 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3468 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3469 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3471 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3472 float rowLeft = 0.0f;
3473 float rowRight = 0.0f;
3474 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3475 float maxRowLeft = std::numeric_limits<float>::max();
3476 float maxRowRight = 0.0f;
3478 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3480 // Scan through entire text.
3483 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3485 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3486 bool charSelected( false );
3487 if( selectedIt != selectedEndIt )
3489 charSelected = *selectedIt++;
3492 if(selectionState == SelectionNone)
3496 selectionState = SelectionStarted;
3497 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3498 rowRight = rowLeft + charInfo.mSize.width;
3501 else if(selectionState == SelectionStarted)
3503 // break selection on:
3504 // 1. new line causing selection break. (\n or wordwrap)
3505 // 2. character not selected.
3506 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3509 // finished selection.
3510 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3511 // that it resides on. That way this enumeration is not necessary.
3513 if(lastIt->mIsNewLineChar)
3515 // 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.
3516 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3518 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3519 maxRowLeft = std::min(maxRowLeft, min.x);
3520 maxRowRight = std::max(maxRowRight, max.x);
3521 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3522 float rowTop = rowBottom - rowSize.height;
3524 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3525 if(charSelected && blockAlignEnabled)
3527 rowRight = std::numeric_limits<float>::max();
3529 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3531 selectionState = SelectionNone;
3533 // Still selected? start a new selection
3536 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3537 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3538 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3539 selectionState = SelectionStarted;
3544 // build up highlight(s) with this selection data.
3545 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3546 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3553 // If reached end, and still on selection, then close selection.
3556 if(selectionState == SelectionStarted)
3558 // finished selection.
3560 if(lastIt->mIsNewLineChar)
3562 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3564 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3565 maxRowLeft = std::min(maxRowLeft, min.x);
3566 maxRowRight = std::max(maxRowRight, max.x);
3567 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3568 float rowTop = rowBottom - rowSize.height;
3569 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3573 // Get the top left and bottom right corners.
3574 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3575 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3576 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3578 // Clamp quads so they appear to clip to borders of the whole text.
3579 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3581 // For block-align align Further Clamp quads to max left and right extents
3582 if(blockAlignEnabled)
3584 // BlockAlign: Will adjust highlight to block:
3586 // H[ello] (top row right = max of all rows right)
3587 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3588 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3589 // [text] (bottom row left = min of all rows left)
3590 // (common in SMS messaging selection)
3592 // As opposed to the default which is tight text highlighting.
3597 // (common in regular text editors/web browser selection)
3599 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3602 // Finally clamp quads again so they don't exceed the boundry of the control.
3603 const Vector3& controlSize = GetControlSize();
3604 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3607 return mNewHighlightInfo;
3610 void TextInput::UpdateHighlight()
3612 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3614 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3616 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3617 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3618 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3619 // [BOTTOM] [ MIDDLE ]
3622 // Each quad is created as 2 triangles.
3623 // Middle is just 1 quad regardless of its size.
3637 if ( mHighlightMeshActor )
3639 // vertex and triangle buffers should always be present if MeshActor is alive.
3640 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3641 MeshData::VertexContainer vertices;
3642 Dali::MeshData::FaceIndices faceIndices;
3644 if( !newHighlightInfo.mQuadList.empty() )
3646 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3647 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3649 // vertex position defaults to (0 0 0)
3650 MeshData::Vertex vertex;
3651 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3654 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3656 // Add each quad geometry (a sub-selection) to the mesh data.
3666 QuadCoordinates& quad = *iter;
3668 vertex.x = quad.min.x;
3669 vertex.y = quad.min.y;
3670 vertices.push_back( vertex );
3673 vertex.x = quad.max.x;
3674 vertex.y = quad.min.y;
3675 vertices.push_back( vertex );
3677 // bottom-left (v+2)
3678 vertex.x = quad.min.x;
3679 vertex.y = quad.max.y;
3680 vertices.push_back( vertex );
3682 // bottom-right (v+3)
3683 vertex.x = quad.max.x;
3684 vertex.y = quad.max.y;
3685 vertices.push_back( vertex );
3687 // triangle A (3, 1, 0)
3688 faceIndices.push_back( v + 3 );
3689 faceIndices.push_back( v + 1 );
3690 faceIndices.push_back( v );
3692 // triangle B (0, 2, 3)
3693 faceIndices.push_back( v );
3694 faceIndices.push_back( v + 2 );
3695 faceIndices.push_back( v + 3 );
3697 mMeshData.SetFaceIndices( faceIndices );
3700 BoneContainer bones(0); // passed empty as bones not required
3701 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3702 mHighlightMesh.UpdateMeshData(mMeshData);
3707 void TextInput::ClearPopup()
3709 mPopupPanel.Clear();
3712 void TextInput::AddPopupOptions()
3714 mPopupPanel.AddPopupOptions();
3717 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3719 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3721 Vector3 clampedPosition ( position );
3722 Vector3 tailOffsetPosition ( position );
3724 float xOffSet( 0.0f );
3726 Actor self = Self();
3727 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3729 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3730 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3732 // Clamp to left or right or of boundary
3733 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3735 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3737 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3739 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3742 clampedPosition.x = position.x + xOffSet;
3743 tailOffsetPosition.x = -xOffSet;
3745 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3746 bool flipTail( false );
3748 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3750 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3754 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3755 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3758 void TextInput::HidePopup(bool animate, bool signalFinished )
3760 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3762 mPopupPanel.Hide( animate );
3764 if( animate && signalFinished )
3766 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3771 void TextInput::ShowPopup( bool animate )
3774 Vector2 alternativePopupPosition;
3776 if(mHighlightMeshActor && mState == StateEdit)
3779 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3781 // When text is selected, show popup above top handle (and text), or below bottom handle.
3782 // topHandle: referring to the top most point of the handle or the top line of selection.
3783 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3785 topHandle = mSelectionHandleOneActualPosition;
3786 bottomHandle = mSelectionHandleTwoActualPosition;
3787 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3791 topHandle = mSelectionHandleTwoActualPosition;
3792 bottomHandle = mSelectionHandleOneActualPosition;
3793 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3795 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3796 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3798 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3800 position.x = xPosition;
3802 // Alternative position if no upper space
3803 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3804 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3808 // When no text is selected, show popup at world position of grab handle or cursor
3809 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3810 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3811 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3812 // if can't be positioned above, then position below row.
3813 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3816 // If grab handle enabled then position pop-up below the grab handle.
3817 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3821 SetPopupPosition( position, alternativePopupPosition );
3824 mPopupPanel.Show( Self(), animate );
3825 StartMonitoringStageForTouch();
3827 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3830 void TextInput::ShowPopupCutCopyPaste()
3834 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3835 // Check the selected text is whole text or not.
3836 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3838 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3841 if ( !mStyledText.empty() )
3843 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3844 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3847 if( mClipboard && mClipboard.NumberOfItems() )
3849 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3850 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3855 mPopupPanel.Hide(false);
3859 void TextInput::SetUpPopupSelection()
3862 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3863 // If no text exists then don't offer to select
3864 if ( !mStyledText.empty() )
3866 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3867 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3868 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3870 // if clipboard has valid contents then offer paste option
3871 if( mClipboard && mClipboard.NumberOfItems() )
3873 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3874 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3879 mPopupPanel.Hide(false);
3882 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3887 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3888 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3889 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3890 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3892 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3894 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3896 float closestYdifference = std::numeric_limits<float>::max();
3897 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3898 std::size_t numberOfMatchedCharacters = 0;
3900 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3901 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3903 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3905 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3906 float baselinePosition = info.mPosition.y - info.mDescender;
3908 if( info.mIsVisible )
3910 // store difference between source y point and the y position of the current character
3911 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3913 if( currentYdifference < closestYdifference )
3915 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3916 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3917 closestYdifference = currentYdifference;
3918 matchedCharacters.clear();
3919 numberOfMatchedCharacters = 0; // reset count
3922 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3923 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3925 // ignore new line character.
3926 if( !info.mIsNewLineChar )
3928 matchedCharacters.push_back( info );
3929 numberOfMatchedCharacters++;
3933 } // End of loop checking each character's y position in the character layout table
3935 // Check if last character is a newline, if it is
3936 // then need pretend there is an imaginary line afterwards,
3937 // and check if user is touching below previous line.
3938 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3940 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3942 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3946 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3947 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3949 bool matched( false );
3951 // 2 Iterate through matching list of y positions and find closest matching X position.
3952 for( ; it != endIt; ++it )
3954 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3956 if( info.mIsVisible )
3958 // stop when on left side of character's center.
3959 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3960 if( sourceScrollOffset.x < characterMidPointPosition )
3962 if(info.mIsRightToLeftCharacter)
3964 rightToLeftChar = true;
3966 glyphIntersection = info.mPosition.x;
3971 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3977 rightToLeftChar = lastRightToLeftChar;
3980 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3981 closestIndex = lineOffset + matchCharacterIndex;
3983 mClosestCursorPositionEOL = false; // reset
3984 if ( it == endIt && !matched )
3986 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3989 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3990 if( rightToLeftChar && lastRightToLeftChar )
3992 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3997 // closestIndex is the visual index, need to convert it to the logical index
3998 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4000 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4002 // Checks for situations where user is touching between LTR and RTL
4003 // characters. To identify if the user means the end of a LTR string
4004 // or the beginning of an RTL string, and vice versa.
4005 if( closestIndex > 0 )
4007 if( rightToLeftChar && !lastRightToLeftChar )
4012 // A: In this touch range, the user is indicating that they wish to place
4013 // the cursor at the end of the LTR text.
4014 // B: In this touch range, the user is indicating that they wish to place
4015 // the cursor at the end of the RTL text.
4017 // Result of touching A area:
4018 // [.....LTR]|[RTL......]+
4020 // |: primary cursor (for typing LTR chars)
4021 // +: secondary cursor (for typing RTL chars)
4023 // Result of touching B area:
4024 // [.....LTR]+[RTL......]|
4026 // |: primary cursor (for typing RTL chars)
4027 // +: secondary cursor (for typing LTR chars)
4029 if( sourceScrollOffset.x < glyphIntersection )
4034 else if( !rightToLeftChar && lastRightToLeftChar )
4036 if( sourceScrollOffset.x < glyphIntersection )
4043 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4044 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4045 // one further ahead
4046 if( rightToLeftChar && !lastRightToLeftChar )
4051 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4053 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4055 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4064 float TextInput::GetLineJustificationPosition() const
4066 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4067 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4068 float alignmentOffset = 0.f;
4070 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4071 if( alignment & Toolkit::Alignment::HorizontalLeft )
4073 alignmentOffset = 0.f;
4075 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4077 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4079 else if( alignment & Toolkit::Alignment::HorizontalRight )
4081 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4084 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4085 float justificationOffset = 0.f;
4087 switch( justification )
4089 case Toolkit::TextView::Left:
4091 justificationOffset = 0.f;
4094 case Toolkit::TextView::Center:
4096 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4099 case Toolkit::TextView::Right:
4101 justificationOffset = mTextLayoutInfo.mTextSize.width;
4104 case Toolkit::TextView::Justified:
4106 justificationOffset = 0.f;
4111 DALI_ASSERT_ALWAYS( false );
4115 return alignmentOffset + justificationOffset;
4118 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4120 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4121 A newline character is not inserted in this case */
4123 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4125 Vector3 cursorPosition;
4127 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4129 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4131 // end character so use
4132 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4133 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4137 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4140 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4142 // If previous character on a different line then use current characters position
4143 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4145 if ( mClosestCursorPositionEOL )
4147 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4151 cursorPosition = Vector3(currentCharInfo.mPosition);
4156 // Previous character is on same line so use position of previous character plus it's width.
4157 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4160 return cursorPosition;
4163 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4165 bool direction(false);
4166 Vector3 alternatePosition;
4167 bool alternatePositionValid(false);
4169 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4172 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4174 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4176 alternatePositionValid = false;
4177 directionRTL = false;
4179 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4181 std::size_t visualCharacterPosition;
4183 // When cursor is not at beginning, consider possibility of
4184 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4185 if(characterPosition > 0)
4187 // Cursor position should be the end of the last character.
4188 // If the last character is LTR, then the end is on the right side of the glyph.
4189 // If the last character is RTL, then the end is on the left side of the glyph.
4190 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4192 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4194 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4197 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4198 if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4200 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4201 const Vector3& size = GetControlSize();
4203 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4205 --visualCharacterPosition;
4207 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4210 if(!info.mIsNewLineChar)
4212 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4216 // When cursor points to first character on new line, position cursor at the start of this glyph.
4217 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4219 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4220 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4221 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4223 cursorPosition.x = infoNext.mPosition.x + start;
4224 cursorPosition.y = infoNext.mPosition.y;
4228 // If cursor points to the end of text, then can only position
4229 // cursor where the new line starts based on the line-justification position.
4230 cursorPosition.x = GetLineJustificationPosition();
4232 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4234 // If this is after the last character, then we can assume that the new cursor
4235 // should be exactly one row below the current row.
4237 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4238 cursorPosition.y = info.mPosition.y + rowRect.height;
4242 // If this is not after last character, then we can use this row's height.
4243 // should be exactly one row below the current row.
4245 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4246 cursorPosition.y = info.mPosition.y + rowRect.height;
4251 directionRTL = info.mIsRightToLeftCharacter;
4253 // 1. When the cursor is neither at the beginning or the end,
4254 // we can show multiple cursors under situations when the cursor is
4255 // between RTL and LTR text...
4256 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4258 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4260 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4261 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4263 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4265 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4266 // Text: [...LTR...]|[...RTL...]
4268 // Alternate cursor pos: ^
4269 // In which case we need to display an alternate cursor for the RTL text.
4271 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4272 alternatePosition.y = infoAlt.mPosition.y;
4273 alternatePositionValid = true;
4275 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4277 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4278 // Text: |[...RTL...] [...LTR....]
4280 // Alternate cursor pos: ^
4281 // In which case we need to display an alternate cursor for the RTL text.
4283 alternatePosition.x = infoAlt.mPosition.x;
4284 alternatePosition.y = infoAlt.mPosition.y;
4285 alternatePositionValid = true;
4290 // 2. When the cursor is at the end of the text,
4291 // and we have multi-directional text,
4292 // we can also consider showing mulitple cursors.
4293 // The rule here is:
4294 // If first and last characters on row are different
4295 // Directions, then two cursors need to be displayed.
4297 // Get first logical glyph on row
4298 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4300 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4301 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4303 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4305 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4306 // Text: [...LTR...]|[...RTL...]
4308 // Alternate cursor pos: ^
4309 // In which case we need to display an alternate cursor for the RTL text, this cursor
4310 // should be at the end of the given line.
4312 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4313 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4314 alternatePosition.y = infoAlt.mPosition.y;
4315 alternatePositionValid = true;
4317 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4319 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4320 // Text: |[...RTL...] [...LTR....]
4322 // Alternate cursor pos: ^
4323 // In which case we need to display an alternate cursor for the RTL text.
4325 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4326 alternatePosition.x = infoAlt.mPosition.x;
4327 alternatePosition.y = infoAlt.mPosition.y;
4328 alternatePositionValid = true;
4331 } // characterPosition > 0
4332 else if(characterPosition == 0)
4334 // When the cursor position is at the beginning, it should be at the start of the current character.
4335 // If the current character is LTR, then the start is on the right side of the glyph.
4336 // If the current character is RTL, then the start is on the left side of the glyph.
4337 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4339 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4341 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4344 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4345 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4347 cursorPosition.x = info.mPosition.x + start;
4348 cursorPosition.y = info.mPosition.y;
4349 directionRTL = info.mIsRightToLeftCharacter;
4354 // If the character table is void, place the cursor accordingly the text alignment.
4355 const Vector3& size = GetControlSize();
4357 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4358 float alignmentOffset = 0.f;
4360 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4361 if( alignment & Toolkit::Alignment::HorizontalLeft )
4363 alignmentOffset = 0.f;
4365 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4367 alignmentOffset = 0.5f * ( size.width );
4369 else if( alignment & Toolkit::Alignment::HorizontalRight )
4371 alignmentOffset = size.width;
4374 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4375 cursorPosition.x = alignmentOffset;
4377 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4378 if( alignment & Toolkit::Alignment::VerticalTop )
4380 cursorPosition.y = mLineHeight;
4382 else if( alignment & Toolkit::Alignment::VerticalCenter )
4384 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4386 else if( alignment & Toolkit::Alignment::VerticalBottom )
4388 cursorPosition.y = size.height;
4392 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4393 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4394 if( alternatePositionValid )
4396 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4397 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4400 return cursorPosition;
4403 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4405 // scan string from current position to beginning of current line to note direction of line
4406 while(logicalPosition)
4409 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4410 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4417 return logicalPosition;
4420 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4424 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4427 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4429 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4430 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4432 min = Vector2::ZERO;
4433 max = Vector2(0.0f, mLineHeight);
4437 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4438 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4439 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4441 // If cursor is pointing to end of line, then start from last character.
4442 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4444 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4446 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4447 if( !it->mIsVisible )
4449 characterPosition = FindVisibleCharacter( Left, characterPosition );
4450 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4453 // Scan characters left and right of cursor, stopping when end of line/string reached or
4454 // y position greater than threshold of reference line.
4456 // 1. scan left until we reach the beginning or a different line.
4457 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4458 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4459 // min-x position is the left-most char's left (x)
4460 // max-x position is the right-most char's right (x)
4461 // min-y position is the minimum of all character's top (y)
4462 // max-y position is the maximum of all character's bottom (y+height)
4463 min.y = validCharIt->mPosition.y;
4464 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4469 min.y = std::min(min.y, validCharIt->mPosition.y);
4470 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4479 if( (it->mPosition.y < referenceLine) ||
4480 (it->mIsNewLineChar) ||
4487 // info refers to the first character on this line.
4488 min.x = validCharIt->mPosition.x;
4490 // 2. scan right until we reach end or a different line
4491 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4492 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4496 if( (it->mPosition.y > referenceLine) ||
4497 (it->mIsNewLineChar) ||
4504 min.y = std::min(min.y, validCharIt->mPosition.y);
4505 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4510 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4512 if ( validCharIt != end )
4514 // info refers to the last character on this line.
4515 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4518 return Size( max.x - min.x, max.y - min.y );
4521 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4523 Actor popUpPanel = mPopupPanel.GetRootActor();
4525 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4531 Dali::Actor parent( touchedActor.GetParent() );
4535 return WasTouchedCheck( parent );
4542 void TextInput::StartMonitoringStageForTouch()
4544 Stage stage = Stage::GetCurrent();
4545 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4548 void TextInput::EndMonitoringStageForTouch()
4550 Stage stage = Stage::GetCurrent();
4551 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4554 void TextInput::OnStageTouched(const TouchEvent& event)
4556 if( event.GetPointCount() > 0 )
4558 if ( TouchPoint::Down == event.GetPoint(0).state )
4560 const Actor touchedActor(event.GetPoint(0).hitActor);
4562 bool popUpShown( false );
4564 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4569 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4571 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4573 EndMonitoringStageForTouch();
4574 HidePopup( true, false );
4577 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4579 EndMonitoringStageForTouch();
4580 ShowGrabHandleAndSetVisibility( false );
4586 void TextInput::SelectText(std::size_t start, std::size_t end)
4588 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4589 IsGrabHandleEnabled()?"true":"false",
4590 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4591 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4592 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4594 StartMonitoringStageForTouch();
4596 if ( mEditModeActive ) // Only allow text selection when in edit mode
4598 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4599 mSelectingText = true;
4601 mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4603 ImfManager imfManager = ImfManager::Get();
4606 imfManager.SetCursorPosition ( mCursorPosition );
4607 imfManager.SetSurroundingText( GetText() );
4608 imfManager.NotifyCursorPosition();
4610 // 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.
4612 // Hide grab handle when selecting.
4613 ShowGrabHandleAndSetVisibility( false );
4615 if( start != end ) // something to select
4617 SetCursorVisibility( false );
4618 StopCursorBlinkTimer();
4620 CreateSelectionHandles(start, end);
4623 const TextStyle oldInputStyle( mInputStyle );
4624 mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4626 if( oldInputStyle != mInputStyle )
4628 // Updates the line height accordingly with the input style.
4631 EmitStyleChangedSignal();
4637 mSelectingText = false;
4641 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4643 MarkupProcessor::StyledTextArray currentSelectedText;
4645 if ( IsTextSelected() )
4647 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4648 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4650 for(; it != end; ++it)
4652 MarkupProcessor::StyledText& styledText( *it );
4653 currentSelectedText.push_back( styledText );
4656 return currentSelectedText;
4659 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4661 const std::size_t beginIndex = std::min( begin, end );
4662 const std::size_t endIndex = std::max( begin, end );
4665 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4667 // Create a styled text array used to replace the text into the text-view.
4668 MarkupProcessor::StyledTextArray text;
4669 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4671 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4672 GetTextLayoutInfo();
4674 if( IsScrollEnabled() )
4676 // Need to set the scroll position as the text's size may have changed.
4677 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4680 ShowGrabHandleAndSetVisibility( false );
4686 // Set Handle positioning as the new style may have repositioned the characters.
4687 SetSelectionHandlePosition(HandleOne);
4688 SetSelectionHandlePosition(HandleTwo);
4691 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4693 // Just hide the grab handle when keyboard is hidden.
4694 if (!keyboardShown )
4696 ShowGrabHandleAndSetVisibility( false );
4698 // If the keyboard is not now being shown, then hide the popup panel
4699 mPopupPanel.Hide( true );
4703 // Removes highlight and resumes edit mode state
4704 void TextInput::RemoveHighlight()
4706 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4708 if ( mHighlightMeshActor )
4710 if ( mSelectionHandleOne )
4712 mActiveLayer.Remove( mSelectionHandleOne );
4713 mSelectionHandleOne.Reset();
4714 mSelectionHandleOneOffset.x = 0.0f;
4716 if ( mSelectionHandleTwo )
4718 mActiveLayer.Remove( mSelectionHandleTwo );
4719 mSelectionHandleTwo.Reset();
4720 mSelectionHandleTwoOffset.x = 0.0f;
4723 mNewHighlightInfo.mQuadList.clear();
4725 Self().Remove( mHighlightMeshActor );
4727 SetCursorVisibility( true );
4728 StartCursorBlinkTimer();
4730 mHighlightMeshActor.Reset();
4731 // NOTE: We cannot dereference mHighlightMesh, due
4732 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4737 mSelectionHandleOnePosition = 0;
4738 mSelectionHandleTwoPosition = 0;
4741 void TextInput::CreateHighlight()
4743 if ( !mHighlightMeshActor )
4745 mMeshData = MeshData( );
4746 mMeshData.SetHasNormals( true );
4748 mCustomMaterial = Material::New("CustomMaterial");
4749 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4751 mMeshData.SetMaterial( mCustomMaterial );
4753 mHighlightMesh = Mesh::New( mMeshData );
4755 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4756 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4757 mHighlightMeshActor.SetInheritShaderEffect( false );
4758 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4759 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4760 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4761 mHighlightMeshActor.SetAffectedByLighting(false);
4763 Self().Add(mHighlightMeshActor);
4768 bool TextInput::CopySelectedTextToClipboard()
4770 mCurrentCopySelecton.clear();
4772 mCurrentCopySelecton = GetSelectedText();
4774 std::string stringToStore;
4776 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4777 * a marked up string.
4779 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4780 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4781 bool success = mClipboard.SetItem( stringToStore );
4785 void TextInput::PasteText( const Text& text )
4787 // Update Flag, indicates whether to update the text-input contents or not.
4788 // Any key stroke that results in a visual change of the text-input should
4789 // set this flag to true.
4790 bool update = false;
4791 if( mHighlightMeshActor )
4793 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4794 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4796 ImfManager imfManager = ImfManager::Get();
4799 imfManager.SetCursorPosition( mCursorPosition );
4800 imfManager.NotifyCursorPosition();
4802 DeleteHighlightedText( true );
4806 bool textExceedsMaximunNumberOfCharacters = false;
4807 bool textExceedsBoundary = false;
4809 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4811 mCursorPosition += insertedStringLength;
4812 ImfManager imfManager = ImfManager::Get();
4815 imfManager.SetCursorPosition ( mCursorPosition );
4816 imfManager.NotifyCursorPosition();
4819 update = update || ( insertedStringLength > 0 );
4825 if( insertedStringLength < text.GetLength() )
4827 EmitMaxInputCharactersReachedSignal();
4830 if( textExceedsBoundary )
4832 EmitInputTextExceedsBoundariesSignal();
4836 void TextInput::SetTextDirection()
4838 // Put the cursor to the right if we are empty and an RTL language is being used.
4839 if ( mStyledText.empty() )
4841 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4843 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4844 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4846 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4847 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4849 int alignment( mDisplayedTextView.GetTextAlignment() &
4850 ( Toolkit::Alignment::VerticalTop |
4851 Toolkit::Alignment::VerticalCenter |
4852 Toolkit::Alignment::VerticalBottom |
4853 Toolkit::Alignment::HorizontalCenter ) );
4854 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4856 // If our alignment is in the center, then do not change.
4857 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4859 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4862 // If our justification is in the center, then do not change.
4863 if ( justification != Toolkit::TextView::Center )
4865 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4868 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4869 mDisplayedTextView.SetLineJustification( justification );
4873 void TextInput::UpdateLineHeight()
4875 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4876 mLineHeight = font.GetLineHeight();
4878 // 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.
4880 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4882 if( !mExceedEnabled || shrink )
4884 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4888 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4890 std::size_t position = 0;
4892 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4898 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4900 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4902 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4908 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4909 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4911 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4917 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4922 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4929 void TextInput::SetSortModifier( float depthOffset )
4931 if(mDisplayedTextView)
4933 mDisplayedTextView.SetSortModifier(depthOffset);
4937 void TextInput::SetSnapshotModeEnabled( bool enable )
4939 if(mDisplayedTextView)
4941 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4945 bool TextInput::IsSnapshotModeEnabled() const
4947 bool snapshotEnabled = false;
4949 if(mDisplayedTextView)
4951 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4954 return snapshotEnabled;
4957 void TextInput::SetMarkupProcessingEnabled( bool enable )
4959 mMarkUpEnabled = enable;
4962 bool TextInput::IsMarkupProcessingEnabled() const
4964 return mMarkUpEnabled;
4967 void TextInput::SetScrollEnabled( bool enable )
4969 if( mDisplayedTextView )
4971 mDisplayedTextView.SetScrollEnabled( enable );
4976 // Don't set cursor's and handle's visibility to false if they are outside the
4977 // boundaries of the text-input.
4978 mIsCursorInScrollArea = true;
4979 mIsGrabHandleInScrollArea = true;
4980 if( mSelectionHandleOne && mSelectionHandleTwo )
4982 mSelectionHandleOne.SetVisible( true );
4983 mSelectionHandleTwo.SetVisible( true );
4985 if( mHighlightMeshActor )
4987 mHighlightMeshActor.SetVisible( true );
4993 bool TextInput::IsScrollEnabled() const
4995 bool scrollEnabled = false;
4997 if( mDisplayedTextView )
4999 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5002 return scrollEnabled;
5005 void TextInput::SetScrollPosition( const Vector2& position )
5007 if( mDisplayedTextView )
5009 mDisplayedTextView.SetScrollPosition( position );
5013 Vector2 TextInput::GetScrollPosition() const
5015 Vector2 scrollPosition;
5017 if( mDisplayedTextView )
5019 scrollPosition = mDisplayedTextView.GetScrollPosition();
5022 return scrollPosition;
5025 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5027 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5028 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5029 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5031 // Add style to the new input text.
5032 MarkupProcessor::StyledTextArray textToInsert;
5033 for( std::size_t i = 0; i < insertedStringLength; ++i )
5035 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5036 textToInsert.push_back( newStyledCharacter );
5039 //Insert text to the TextView.
5040 const bool emptyTextView = mStyledText.empty();
5041 if( emptyTextView && mPlaceHolderSet )
5043 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5044 mDisplayedTextView.SetText( textToInsert );
5048 if( 0 == numberOfCharactersToReplace )
5050 mDisplayedTextView.InsertTextAt( position, textToInsert );
5054 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5057 mPlaceHolderSet = false;
5059 if( textToInsert.empty() )
5061 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5062 GetTextLayoutInfo();
5066 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5067 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5070 textExceedsBoundary = false;
5072 if( !mExceedEnabled )
5074 const Vector3& size = GetControlSize();
5076 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5078 // If new text does not fit within TextView
5079 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5080 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5081 GetTextLayoutInfo();
5082 textExceedsBoundary = true;
5083 insertedStringLength = 0;
5086 if( textExceedsBoundary )
5088 // Add the part of the text which fits on the text-input.
5090 // Split the text which doesn't fit in two halves.
5091 MarkupProcessor::StyledTextArray firstHalf;
5092 MarkupProcessor::StyledTextArray secondHalf;
5093 SplitText( textToInsert, firstHalf, secondHalf );
5095 // Clear text. This text will be filled with the text inserted.
5096 textToInsert.clear();
5098 // Where to insert the text.
5099 std::size_t positionToInsert = position;
5101 bool end = text.GetLength() <= 1;
5104 // Insert text and check ...
5105 const std::size_t textLength = firstHalf.size();
5106 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5107 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5109 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5111 // Inserted text doesn't fit.
5113 // Remove inserted text
5114 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5115 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5117 // The iteration finishes when only one character doesn't fit.
5118 end = textLength <= 1;
5122 // Prepare next two halves for next iteration.
5123 MarkupProcessor::StyledTextArray copyText = firstHalf;
5124 SplitText( copyText, firstHalf, secondHalf );
5131 // store text to be inserted in mStyledText.
5132 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5134 // Increase the inserted characters counter.
5135 insertedStringLength += textLength;
5137 // Prepare next two halves for next iteration.
5138 MarkupProcessor::StyledTextArray copyText = secondHalf;
5139 SplitText( copyText, firstHalf, secondHalf );
5141 // Update where next text has to be inserted
5142 positionToInsert += textLength;
5148 if( textToInsert.empty() && emptyTextView )
5150 // No character has been added and the text-view was empty.
5151 // Set the placeholder text.
5152 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5153 mPlaceHolderSet = true;
5157 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5158 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5159 mPlaceHolderSet = false;
5162 return insertedStringLength;
5165 void TextInput::GetTextLayoutInfo()
5167 if( mStyledText.empty() )
5169 // The text-input has no text, clear the text-view's layout info.
5170 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5174 if( mDisplayedTextView )
5176 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5180 // There is no text-view.
5181 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5186 void TextInput::SetOffsetFromText( const Vector4& offset )
5188 mPopupOffsetFromText = offset;
5191 const Vector4& TextInput::GetOffsetFromText() const
5193 return mPopupOffsetFromText;
5196 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5198 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5202 TextInput& textInputImpl( GetImpl( textInput ) );
5204 switch ( propertyIndex )
5206 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5208 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5211 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5213 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5216 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5218 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5221 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5223 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5226 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5228 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5231 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5233 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5236 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5238 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5241 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5243 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5246 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5248 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5251 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5253 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5256 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5258 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5261 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5263 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5266 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5268 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5271 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5273 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5276 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5278 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5285 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5287 Property::Value value;
5289 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5293 TextInput& textInputImpl( GetImpl( textInput ) );
5295 switch ( propertyIndex )
5297 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5299 value = textInputImpl.GetMaterialDiffuseColor();
5302 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5304 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5307 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5309 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5312 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5314 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5317 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5319 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5322 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5324 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5327 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5329 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5332 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5334 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5337 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5339 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5342 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5344 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5347 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5349 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5352 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5354 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5357 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5359 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5362 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5364 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5367 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5369 value = textInputImpl.GetOffsetFromText();
5377 void TextInput::EmitStyleChangedSignal()
5379 // emit signal if input style changes.
5380 Toolkit::TextInput handle( GetOwner() );
5381 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5384 void TextInput::EmitTextModified()
5386 // emit signal when text changes.
5387 Toolkit::TextInput handle( GetOwner() );
5388 mTextModifiedSignal.Emit( handle );
5392 void TextInput::EmitMaxInputCharactersReachedSignal()
5394 // emit signal if max characters is reached during text input.
5395 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5397 Toolkit::TextInput handle( GetOwner() );
5398 mMaxInputCharactersReachedSignalV2.Emit( handle );
5401 void TextInput::EmitInputTextExceedsBoundariesSignal()
5403 // Emit a signal when the input text exceeds the boundaries of the text input.
5405 Toolkit::TextInput handle( GetOwner() );
5406 mInputTextExceedBoundariesSignalV2.Emit( handle );
5409 } // namespace Internal
5411 } // namespace Toolkit