2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
89 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
91 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
95 if( ( *it ).mIsVisible )
97 return --cursorPosition;
106 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
108 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
110 if( ( *it ).mIsVisible )
112 return cursorPosition;
118 return cursorPosition;
122 * Whether the given position plus the cursor size offset is inside the given boundary.
124 * @param[in] position The given position.
125 * @param[in] cursorSize The cursor size.
126 * @param[in] controlSize The given boundary.
128 * @return whether the given position is inside the given boundary.
130 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
132 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
133 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
134 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
135 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
139 * Splits a text in two halves.
141 * If the text's number of characters is odd, firstHalf has one more character.
143 * @param[in] text The text to be split.
144 * @param[out] firstHalf The first half of the text.
145 * @param[out] secondHalf The second half of the text.
147 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
148 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
149 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
154 const std::size_t textLength = text.size();
155 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
157 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
158 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
161 } // end of namespace
169 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
170 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
171 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
172 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
173 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
174 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
175 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
176 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
177 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
178 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
179 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
180 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
181 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
182 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
183 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
184 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
195 return Toolkit::TextInput::New();
198 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
200 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
201 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
202 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
203 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
204 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
205 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
209 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
210 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
211 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
212 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
213 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
214 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
215 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
216 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
217 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
227 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
229 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
231 QuadCoordinates quad(x1, y1, x2, y2);
232 mQuadList.push_back( quad );
235 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
237 for(std::size_t i = 0;i < mQuadList.size(); i++)
239 QuadCoordinates& quad = mQuadList[i];
241 quad.min.Clamp(min, max);
242 quad.max.Clamp(min, max);
246 // [TextInput] ////////////////////////////////////////////////////////////////
248 Dali::Toolkit::TextInput TextInput::New()
250 // Create the implementation
251 TextInputPtr textInput(new TextInput());
252 // Pass ownership to CustomActor via derived handle
253 Dali::Toolkit::TextInput handle(*textInput);
254 handle.SetName( "TextInput");
256 textInput->Initialize();
260 TextInput::TextInput()
261 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
266 mDisplayedTextView(),
267 mStyledPlaceHolderText(),
268 mMaxStringLength( DEFAULT_MAX_SIZE ),
269 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
270 mCursorPosition( 0 ),
271 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
272 mIsSelectionHandleOneFlipped( false ),
273 mIsSelectionHandleTwoFlipped( false ),
274 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
275 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
276 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
277 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
278 mSelectionHandleOnePosition( 0 ),
279 mSelectionHandleTwoPosition( 0 ),
281 mPreEditStartPosition( 0 ),
282 mPreEditLength ( 0 ),
283 mNumberOfSurroundingCharactersDeleted( 0 ),
284 mTouchStartTime( 0 ),
286 mCurrentCopySelecton(),
289 mScrollDisplacement(),
290 mCurrentHandlePosition(),
291 mCurrentSelectionId(),
292 mCurrentSelectionHandlePosition(),
293 mRequestedSelection( 0, 0 ),
294 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
295 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
297 mMaterialColor( LIGHTBLUE ),
298 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
299 mOverrideAutomaticAlignment( false ),
300 mCursorRTLEnabled( false ),
301 mClosestCursorPositionEOL ( false ),
302 mCursorBlinkStatus( true ),
303 mCursorVisibility( false ),
304 mGrabHandleVisibility( false ),
305 mIsCursorInScrollArea( true ),
306 mIsGrabHandleInScrollArea( true ),
307 mEditModeActive( false ),
308 mEditOnTouch( true ),
309 mTextSelection( true ),
310 mExceedEnabled( true ),
311 mGrabHandleEnabled( true ),
312 mIsSelectionHandleFlipEnabled( true ),
313 mPreEditFlag( false ),
314 mIgnoreCommitFlag( false ),
315 mIgnoreFirstCommitFlag( false ),
316 mSelectingText( false ),
317 mPreserveCursorPosition( false ),
318 mSelectTextOnCommit( false ),
319 mUnderlinedPriorToPreEdit ( false ),
320 mCommitByKeyInput( false ),
321 mPlaceHolderSet( false ),
322 mMarkUpEnabled( false )
324 // Updates the line height accordingly with the input style.
328 TextInput::~TextInput()
330 StopCursorBlinkTimer();
335 std::string TextInput::GetText() const
339 // Return text-view's text only if the text-input's text is not empty
340 // in order to not to return the placeholder text.
341 if( !mStyledText.empty() )
343 text = mDisplayedTextView.GetText();
349 std::string TextInput::GetMarkupText() const
351 std::string markupString;
352 MarkupProcessor::GetMarkupString( mStyledText, markupString );
357 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
359 // Get the placeholder styled text array from the markup string.
360 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
362 if( mStyledText.empty() )
364 // Set the placeholder text only if the styled text is empty.
365 mDisplayedTextView.SetText( mStyledPlaceHolderText );
366 mPlaceHolderSet = true;
370 std::string TextInput::GetPlaceholderText()
372 // Traverses the styled placeholder array getting only the text.
373 // Note that for some languages a 'character' could be represented by more than one 'char'
375 std::string placeholderText;
376 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
378 placeholderText.append( (*it).mText.GetText() );
381 return placeholderText ;
384 void TextInput::SetInitialText(const std::string& initialText)
386 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
388 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
390 mPreEditFlag = false;
391 mIgnoreCommitFlag = true;
394 SetText( initialText );
395 PreEditReset( false ); // Reset keyboard as text changed
398 void TextInput::SetText(const std::string& initialText)
400 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
402 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
404 if( mStyledText.empty() )
406 // If the initial text is empty, set the placeholder text.
407 mDisplayedTextView.SetText( mStyledPlaceHolderText );
408 mPlaceHolderSet = true;
412 mDisplayedTextView.SetText( mStyledText );
413 mPlaceHolderSet = false;
418 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
420 ImfManager imfManager = ImfManager::Get();
423 imfManager.SetCursorPosition( mCursorPosition );
424 imfManager.SetSurroundingText( initialText );
425 imfManager.NotifyCursorPosition();
428 if( IsScrollEnabled() )
430 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
433 ShowGrabHandleAndSetVisibility( false );
442 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
444 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
446 mDisplayedTextView.SetText( styleText );
447 mPlaceHolderSet = false;
449 // If text alignment hasn't been manually set by application developer, then we
450 // automatically determine the alignment based on the content of the text i.e. what
451 // language the text begins with.
452 // TODO: This should determine different alignments for each line (broken by '\n') of text.
453 if(!mOverrideAutomaticAlignment)
455 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
456 bool leftToRight(true);
458 if( !styleText.empty() )
460 bool breakOut(false);
462 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
464 const Text& text = textIter->mText;
466 for( std::size_t i = 0; i < text.GetLength(); ++i )
468 Character character( text[i] );
469 if( character.GetCharacterDirection() != Character::Neutral )
471 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
479 // Based on this direction, either left or right align text if not manually set by application developer.
480 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
481 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
482 Toolkit::Alignment::VerticalTop ) );
483 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
489 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
491 mMaxStringLength = maxChars;
494 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
496 DALI_ASSERT_DEBUG( maxLines > 0 )
500 mNumberOflinesLimit = maxLines;
504 std::size_t TextInput::GetNumberOfLinesLimit() const
506 return mNumberOflinesLimit;
509 std::size_t TextInput::GetNumberOfCharacters() const
511 return mStyledText.size();
515 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
517 mMaterialColor = color;
518 if ( mCustomMaterial )
520 mCustomMaterial.SetDiffuseColor( mMaterialColor );
521 mMeshData.SetMaterial( mCustomMaterial );
525 const Vector4& TextInput::GetMaterialDiffuseColor() const
527 return mMaterialColor;
532 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
534 return mInputStartedSignalV2;
537 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
539 return mInputFinishedSignalV2;
542 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
544 return mCutAndPasteToolBarDisplayedV2;
547 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
549 return mStyleChangedSignalV2;
552 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
554 return mTextModifiedSignal;
557 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
559 return mMaxInputCharactersReachedSignalV2;
562 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
564 return mInputTextExceedBoundariesSignalV2;
567 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
569 Dali::BaseHandle handle( object );
571 bool connected( true );
572 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
574 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
576 textInput.InputStartedSignal().Connect( tracker, functor );
578 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
580 textInput.InputFinishedSignal().Connect( tracker, functor );
582 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
584 textInput.StyleChangedSignal().Connect( tracker, functor );
586 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
588 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
590 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
592 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
596 // signalName does not match any signal
603 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
607 // update line height before calculate the actual position.
612 if( setCursorOnTouchPoint )
614 // Sets the cursor position for the given touch point.
615 ReturnClosestIndex( touchPoint, mCursorPosition );
617 // Creates the grab handle.
618 if( IsGrabHandleEnabled() )
620 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
624 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
625 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
626 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
627 ShowGrabHandleAndSetVisibility( true );
629 // Scrolls the text-view if needed.
630 if( IsScrollEnabled() )
632 ScrollTextViewToMakeCursorVisible( cursorPosition );
638 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
650 bool TextInput::IsEditable() const
652 return mEditModeActive;
655 void TextInput::SetEditOnTouch( bool editOnTouch )
657 mEditOnTouch = editOnTouch;
660 bool TextInput::IsEditOnTouch() const
665 void TextInput::SetTextSelectable( bool textSelectable )
667 mTextSelection = textSelectable;
670 bool TextInput::IsTextSelectable() const
672 return mTextSelection;
675 bool TextInput::IsTextSelected() const
677 return mHighlightMeshActor;
680 void TextInput::DeSelectText()
687 void TextInput::SetGrabHandleImage(Dali::Image image )
691 CreateGrabHandle(image);
695 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
697 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
701 mCursor.SetImage( image );
702 mCursor.SetNinePatchBorder( border );
706 Vector3 TextInput::GetSelectionHandleSize()
708 return DEFAULT_SELECTION_HANDLE_SIZE;
711 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
713 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
717 mCursorRTL.SetImage( image);
718 mCursorRTL.SetNinePatchBorder( border );
722 void TextInput::EnableGrabHandle(bool toggle)
724 // enables grab handle with will in turn de-activate magnifier
725 mGrabHandleEnabled = toggle;
728 bool TextInput::IsGrabHandleEnabled()
730 // if false then magnifier will be shown instead.
731 return mGrabHandleEnabled;
734 void TextInput::EnableSelectionHandleFlip( bool toggle )
736 // Deprecated function. To be removed.
737 mIsSelectionHandleFlipEnabled = toggle;
740 bool TextInput::IsSelectionHandleFlipEnabled()
742 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
746 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
748 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
749 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
750 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
752 mSelectionHandleFlipMargin = margin;
755 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
757 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
758 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
760 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
761 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
763 const Vector4 boundary( originX,
765 originX + boundingRectangle.width,
766 originY + boundingRectangle.height );
768 mBoundingRectangleWorldCoordinates = boundary;
771 const Rect<float> TextInput::GetBoundingRectangle() const
773 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
775 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
776 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
778 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
783 const Vector4& TextInput::GetSelectionHandleFlipMargin()
785 return mSelectionHandleFlipMargin;
788 void TextInput::SetTextColor( const Vector4& color )
790 mDisplayedTextView.SetColor( color );
793 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
795 if( style != mInputStyle )
798 bool emitSignal = false;
800 // mask: modify style according to mask, if different emit signal.
801 const TextStyle oldInputStyle( mInputStyle );
803 // Copy the new style.
804 mInputStyle.Copy( style, mask );
806 // if style has changed, emit signal.
807 if( oldInputStyle != mInputStyle )
812 // Updates the line height accordingly with the input style.
815 // Changing font point size will require the cursor to be re-sized
820 EmitStyleChangedSignal();
825 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
827 if ( IsTextSelected() )
829 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
830 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
832 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
834 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
837 // Keeps the old style to be compared with the new one.
838 const TextStyle oldInputStyle( mInputStyle );
840 // Copy only those parameters from the style which are set in the mask.
841 mInputStyle.Copy( style, mask );
843 if( mInputStyle != oldInputStyle )
845 // Updates the line height accordingly with the input style.
848 EmitStyleChangedSignal();
853 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
855 if( !mStyledText.empty() )
857 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
861 TextStyle TextInput::GetStyleAtCursor() const
865 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
867 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
868 style = mStyledText.at( mCursorPosition-1 ).mStyle;
874 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
876 Dali::Font defaultFont = Dali::Font::New();
877 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
884 TextStyle TextInput::GetStyleAt( std::size_t position ) const
886 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
888 if( position >= mStyledText.size() )
890 position = mStyledText.size() - 1;
893 return mStyledText.at( position ).mStyle;
896 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
898 mDisplayedTextView.SetTextAlignment( align );
899 mOverrideAutomaticAlignment = true;
902 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
904 mDisplayedTextView.SetLineJustification( justification );
905 mOverrideAutomaticAlignment = true;
908 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
910 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
913 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
915 return mDisplayedTextView.GetFadeBoundary();
918 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
920 return mDisplayedTextView.GetTextAlignment();
923 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
925 mDisplayedTextView.SetMultilinePolicy( policy );
928 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
930 return mDisplayedTextView.GetMultilinePolicy();
933 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
935 mDisplayedTextView.SetWidthExceedPolicy( policy );
938 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
940 return mDisplayedTextView.GetWidthExceedPolicy();
943 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
945 mDisplayedTextView.SetHeightExceedPolicy( policy );
948 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
950 return mDisplayedTextView.GetHeightExceedPolicy();
953 void TextInput::SetExceedEnabled( bool enable )
955 mExceedEnabled = enable;
958 bool TextInput::GetExceedEnabled() const
960 return mExceedEnabled;
963 void TextInput::SetBackground(Dali::Image image )
965 // TODO Should add this function and add public api to match.
968 bool TextInput::OnTouchEvent(const TouchEvent& event)
973 bool TextInput::OnKeyEvent(const KeyEvent& event)
975 switch( event.state )
979 return OnKeyDownEvent(event);
985 return OnKeyUpEvent(event);
997 void TextInput::OnKeyInputFocusGained()
999 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1001 mEditModeActive = true;
1003 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1005 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1007 // Updates the line height accordingly with the input style.
1010 // Connect the signals to use in text input.
1011 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1012 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1014 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1017 GetTextLayoutInfo();
1020 SetCursorVisibility( true );
1021 StartCursorBlinkTimer();
1023 Toolkit::TextInput handle( GetOwner() );
1024 mInputStartedSignalV2.Emit( handle );
1026 ImfManager imfManager = ImfManager::Get();
1030 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1032 // Notify that the text editing start.
1033 imfManager.Activate();
1035 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1036 imfManager.SetRestoreAferFocusLost( true );
1038 imfManager.SetCursorPosition( mCursorPosition );
1039 imfManager.NotifyCursorPosition();
1042 mClipboard = Clipboard::Get(); // Store handle to clipboard
1044 // Now in edit mode we can accept string to paste from clipboard
1045 if( Adaptor::IsAvailable() )
1047 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1050 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1055 void TextInput::OnKeyInputFocusLost()
1057 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1061 // If key input focus is lost, it removes the
1062 // underline from the last pre-edit text.
1063 RemovePreEditStyle();
1064 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1065 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1069 ImfManager imfManager = ImfManager::Get();
1072 // The text editing is finished. Therefore the imf manager don't have restore activation.
1073 imfManager.SetRestoreAferFocusLost( false );
1075 // Notify that the text editing finish.
1076 imfManager.Deactivate();
1078 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1080 // Disconnect signal used the text input.
1081 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1083 Toolkit::TextInput handle( GetOwner() );
1084 mInputFinishedSignalV2.Emit( handle );
1085 mEditModeActive = false;
1086 mPreEditFlag = false;
1088 SetCursorVisibility( false );
1089 StopCursorBlinkTimer();
1091 ShowGrabHandleAndSetVisibility( false );
1094 // No longer in edit mode so do not want to receive string from clipboard
1095 if( Adaptor::IsAvailable() )
1097 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1100 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1102 Clipboard clipboard = Clipboard::Get();
1106 clipboard.HideClipboard();
1111 void TextInput::OnControlStageConnection()
1113 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1115 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1117 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1121 void TextInput::CreateActiveLayer()
1123 Actor self = Self();
1124 mActiveLayer = Layer::New();
1125 mActiveLayer.SetName ( "ActiveLayerActor" );
1127 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1128 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1129 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1131 self.Add( mActiveLayer );
1132 mActiveLayer.RaiseToTop();
1135 void TextInput::OnInitialize()
1137 CreateTextViewActor();
1141 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1142 // different positions depending on language)
1143 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1144 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1146 Actor self = Self();
1147 self.Add( mCursor );
1148 self.Add( mCursorRTL );
1150 mCursorVisibility = false;
1152 CreateActiveLayer(); // todo move this so layer only created when needed.
1154 // Assign names to image actors
1155 mCursor.SetName("mainCursor");
1156 mCursorRTL.SetName("rtlCursor");
1159 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1161 mDisplayedTextView.SetSize( targetSize );
1162 GetTextLayoutInfo();
1163 mActiveLayer.SetSize(targetSize);
1166 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1168 Relayout( mDisplayedTextView, size, container );
1169 Relayout( mPopupPanel.GetRootActor(), size, container );
1171 GetTextLayoutInfo();
1176 Vector3 TextInput::GetNaturalSize()
1178 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1180 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1182 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1183 naturalSize.height = mLineHeight;
1189 float TextInput::GetHeightForWidth( float width )
1191 float height = mDisplayedTextView.GetHeightForWidth( width );
1193 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1195 // If the height is zero, it means there is no text. Let's return the cursor height.
1196 height = mLineHeight;
1202 /*end of Virtual methods from parent*/
1204 // Private Internal methods
1206 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1208 switch (gesture.state)
1210 case Gesture::Started:
1211 // fall through so code not duplicated
1212 case Gesture::Continuing:
1214 if (actor == mGrabArea)
1216 SetCursorVisibility( true );
1217 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1218 MoveGrabHandle( gesture.displacement );
1219 HidePopup(); // Do not show popup whilst handle is moving
1221 else if (actor == mHandleOneGrabArea)
1223 // the displacement in PanGesture is affected by the actor's rotation.
1224 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1225 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1227 MoveSelectionHandle( HandleOne, gesture.displacement );
1229 mState = StateDraggingHandle;
1232 else if (actor == mHandleTwoGrabArea)
1234 // the displacement in PanGesture is affected by the actor's rotation.
1235 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1236 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1238 MoveSelectionHandle( HandleTwo, gesture.displacement );
1240 mState = StateDraggingHandle;
1246 case Gesture::Finished:
1248 // Revert back to non-pressed selection handle images
1249 if (actor == mGrabArea)
1251 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1252 SetCursorVisibility( true );
1253 SetUpPopupSelection();
1256 if (actor == mHandleOneGrabArea)
1258 // the displacement in PanGesture is affected by the actor's rotation.
1259 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1260 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1262 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1264 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1266 ShowPopupCutCopyPaste();
1268 if (actor == mHandleTwoGrabArea)
1270 // the displacement in PanGesture is affected by the actor's rotation.
1271 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1272 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1274 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1276 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1278 ShowPopupCutCopyPaste();
1287 // Stop the flashing animation so easy to see when moved.
1288 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1290 if (touch.GetPoint(0).state == TouchPoint::Down)
1292 SetCursorVisibility( true );
1293 StopCursorBlinkTimer();
1295 else if (touch.GetPoint(0).state == TouchPoint::Up)
1297 SetCursorVisibility( true );
1298 StartCursorBlinkTimer();
1303 // selection handle one
1304 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1306 if (touch.GetPoint(0).state == TouchPoint::Down)
1308 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1310 else if (touch.GetPoint(0).state == TouchPoint::Up)
1312 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1317 // selection handle two
1318 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1320 if (touch.GetPoint(0).state == TouchPoint::Down)
1322 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1324 else if (touch.GetPoint(0).state == TouchPoint::Up)
1326 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1331 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1333 // If text exists then select nearest word.
1334 if ( !mStyledText.empty())
1338 ShowGrabHandleAndSetVisibility( false );
1343 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1344 // converts the pre-edit word being displayed to a committed word.
1345 if ( !mUnderlinedPriorToPreEdit )
1348 style.SetUnderline( false );
1349 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1351 mPreEditFlag = false;
1352 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1353 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1354 PreEditReset( false );
1356 mCursorPosition = 0;
1358 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1359 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1361 std::size_t start = 0;
1362 std::size_t end = 0;
1363 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1365 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1367 ImfManager imfManager = ImfManager::Get();
1370 imfManager.SetCursorPosition ( mCursorPosition );
1371 imfManager.NotifyCursorPosition();
1374 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1376 SelectText( start, end );
1377 ShowPopupCutCopyPaste();
1381 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1382 HidePopup( false ); // Hide popup with setting to do auto show.
1383 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1387 else if ( mClipboard && mClipboard.NumberOfItems() )
1389 ShowPopupCutCopyPaste();
1392 // If no text and clipboard empty then do nothing
1395 // TODO: Change the function name to be more general.
1396 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1398 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1399 , (mEditOnTouch)?"true":"false"
1400 , (mEditModeActive)?"true":"false");
1402 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1407 if( mGrabArea == actor )
1409 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1411 SetUpPopupSelection();
1421 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1423 // Initially don't create the grab handle.
1424 bool createGrabHandle = false;
1426 if ( !mEditModeActive )
1428 // update line height before calculate the actual position.
1431 // Only start edit mode if TextInput configured to edit on touch
1434 // Set the initial cursor position in the tap point.
1435 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1441 // Show the keyboard if it was hidden.
1442 if (!VirtualKeyboard::IsVisible())
1444 VirtualKeyboard::Show();
1447 // Reset keyboard as tap event has occurred.
1448 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1449 PreEditReset( true );
1451 GetTextLayoutInfo();
1453 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1455 // 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.
1457 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1459 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1461 // Notify keyboard so it can 're-capture' word for predictive text.
1462 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1463 ImfManager imfManager = ImfManager::Get();
1466 imfManager.SetCursorPosition ( mCursorPosition );
1467 imfManager.NotifyCursorPosition();
1469 const TextStyle oldInputStyle( mInputStyle );
1471 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1475 // Create the grab handle.
1476 // Grab handle is created later.
1477 createGrabHandle = true;
1479 if( oldInputStyle != mInputStyle )
1481 // Updates the line height accordingly with the input style.
1484 EmitStyleChangedSignal();
1489 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1490 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1491 // otherwise the Grab handle will be shown when selecting.
1492 if ( createGrabHandle && IsGrabHandleEnabled() )
1494 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1498 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1499 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1500 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1501 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1506 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1508 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1510 // Ignore longpress if in selection mode already
1511 if( mHighlightMeshActor )
1516 if(longPress.state == Dali::Gesture::Started)
1518 // Start edit mode on long press
1519 if ( !mEditModeActive )
1524 // If text exists then select nearest word.
1525 if ( !mStyledText.empty())
1529 ShowGrabHandleAndSetVisibility( false );
1534 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1535 // converts the pre-edit word being displayed to a committed word.
1536 if ( !mUnderlinedPriorToPreEdit )
1539 style.SetUnderline( false );
1540 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1542 mPreEditFlag = false;
1543 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1544 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1545 PreEditReset( false );
1547 mCursorPosition = 0;
1549 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1550 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1552 std::size_t start = 0;
1553 std::size_t end = 0;
1554 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1556 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1558 ImfManager imfManager = ImfManager::Get();
1561 imfManager.SetCursorPosition ( mCursorPosition );
1562 imfManager.NotifyCursorPosition();
1565 SelectText( start, end );
1568 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1569 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1571 ShowPopupCutCopyPaste();
1576 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1578 const Text clipboardText( notifier.GetContent() );
1579 PasteText( clipboardText );
1581 SetCursorVisibility( true );
1582 StartCursorBlinkTimer();
1584 ShowGrabHandleAndSetVisibility( false );
1590 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1592 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1594 const std::string& name = button.GetName();
1596 if(name == TextInputPopup::OPTION_SELECT_WORD)
1598 std::size_t start = 0;
1599 std::size_t end = 0;
1600 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1602 SelectText( start, end );
1604 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1606 SetCursorVisibility(false);
1607 StopCursorBlinkTimer();
1609 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1610 std::size_t start = 0;
1612 SelectText( start, end );
1614 else if(name == TextInputPopup::OPTION_CUT)
1616 bool ret = CopySelectedTextToClipboard();
1620 DeleteHighlightedText( true );
1624 SetCursorVisibility( true );
1625 StartCursorBlinkTimer();
1629 else if(name == TextInputPopup::OPTION_COPY)
1631 CopySelectedTextToClipboard();
1635 SetCursorVisibility( true );
1636 StartCursorBlinkTimer();
1640 else if(name == TextInputPopup::OPTION_PASTE)
1642 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1644 PasteText(retrievedString);
1646 SetCursorVisibility( true );
1647 StartCursorBlinkTimer();
1649 ShowGrabHandleAndSetVisibility( false );
1653 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1655 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1656 // Hence pass the false parameter for signalFinished.
1657 HidePopup( true, false );
1658 mClipboard.ShowClipboard();
1664 bool TextInput::OnCursorBlinkTimerTick()
1667 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1668 if ( mCursorRTLEnabled )
1670 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1672 mCursorBlinkStatus = !mCursorBlinkStatus;
1677 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1679 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1681 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1682 if(mHighlightMeshActor && mState == StateEdit)
1684 ShowPopupCutCopyPaste();
1688 //FIXME this routine needs to be re-written as it contains too many branches.
1689 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1691 std::string keyName = event.keyPressedName;
1692 std::string keyString = event.keyPressed;
1694 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1696 // Do not consume "Tab" and "Escape" keys.
1697 if(keyName == "Tab" || keyName == "Escape")
1699 // Escape key to end the edit mode
1705 HidePopup(); // If Pop-up shown then hides it as editing text.
1707 // Update Flag, indicates whether to update the text-input contents or not.
1708 // Any key stroke that results in a visual change of the text-input should
1709 // set this flag to true.
1712 // Whether to scroll text to cursor position.
1713 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1714 bool scroll = false;
1716 if (keyName == "Return")
1718 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1720 bool preEditFlagPreviouslySet( mPreEditFlag );
1722 // replaces highlighted text with new line
1723 DeleteHighlightedText( false );
1725 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1727 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1728 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1731 mCommitByKeyInput = true;
1734 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1735 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1737 mPreEditFlag = true;
1738 mIgnoreCommitFlag = false;
1748 else if ( keyName == "space" )
1750 if ( mHighlightMeshActor )
1752 // Some text is selected so erase it before adding space.
1753 DeleteHighlightedText( true );
1757 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1759 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1760 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1763 mCommitByKeyInput = true;
1768 else if (keyName == "BackSpace")
1770 if ( mHighlightMeshActor )
1772 // Some text is selected so erase it
1773 DeleteHighlightedText( true );
1778 if ( mCursorPosition > 0 )
1780 DeleteCharacter( mCursorPosition );
1786 else if (keyName == "Right")
1791 else if (keyName == "Left")
1793 AdvanceCursor(true);
1796 else // event is a character
1798 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1799 if ( !keyString.empty() )
1801 // replaces highlighted text with new character
1802 DeleteHighlightedText( false );
1804 // Received key String
1805 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1811 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1812 // as this is a costly operation.
1818 if(update || scroll)
1820 if( IsScrollEnabled() )
1822 // Calculates the new cursor position (in actor coordinates)
1823 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1825 ScrollTextViewToMakeCursorVisible( cursorPosition );
1832 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1834 std::string keyName = event.keyPressedName;
1835 std::string keyString = event.keyPressed;
1837 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1839 // The selected text become deselected when the key code is DALI_KEY_BACK.
1840 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1849 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1851 // Updates the stored scroll position.
1852 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1854 const Vector3& controlSize = GetControlSize();
1855 Size cursorSize( CURSOR_THICKNESS, 0.f );
1857 // Updates the cursor and grab handle position and visibility.
1858 if( mGrabHandle || mCursor )
1860 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1861 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1863 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1865 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1869 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1870 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1875 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1876 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1880 // Updates the selection handles and highlighted text position and visibility.
1881 if( mSelectionHandleOne && mSelectionHandleTwo )
1883 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1884 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1885 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1886 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1887 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1888 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1890 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1891 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1893 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1894 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1895 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1896 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1898 if( mHighlightMeshActor )
1900 mHighlightMeshActor.SetVisible( true );
1906 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1908 // Scroll the text to make the cursor visible.
1909 const Size cursorSize( CURSOR_THICKNESS,
1910 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1912 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1914 const Vector3& controlSize = GetControlSize();
1916 // Calculates the new scroll position.
1917 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1918 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1920 scrollOffset.x += cursorPosition.x;
1923 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1925 scrollOffset.y += cursorPosition.y;
1928 // Sets the new scroll position.
1929 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1930 SetScrollPosition( scrollOffset );
1933 void TextInput::StartScrollTimer()
1937 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1938 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1941 if( !mScrollTimer.IsRunning() )
1943 mScrollTimer.Start();
1947 void TextInput::StopScrollTimer()
1951 mScrollTimer.Stop();
1955 bool TextInput::OnScrollTimerTick()
1957 // TODO: need to set the new style accordingly the new handle position.
1959 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1961 // nothing to do if all handles are invisible or doesn't exist.
1967 // Choose between the grab handle or the selection handles.
1968 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1969 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1970 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1972 std::size_t newCursorPosition = 0;
1973 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1975 // Whether the handle's position is different of the previous one and in the case of the selection handle,
1976 // the new selection handle's position needs to be different of the other one.
1977 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1978 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1979 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1981 if( differentSelectionHandles )
1983 handlePosition = newCursorPosition;
1985 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1987 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1989 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1990 scrollPosition += scrollDelta;
1991 SetScrollPosition( scrollPosition );
1993 if( mDisplayedTextView.IsScrollPositionTrimmed() )
1998 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2001 actualHandlePosition.x += mScrollDisplacement.x;
2002 actualHandlePosition.y += mScrollDisplacement.y;
2007 // Public Internal Methods (public for testing purpose)
2009 void TextInput::SetUpTouchEvents()
2011 if ( !mTapDetector )
2013 mTapDetector = TapGestureDetector::New();
2014 // Attach the actors and connect the signal
2015 mTapDetector.Attach(Self());
2017 // As contains children which may register for tap the default control detector is not used.
2018 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2021 if ( !mDoubleTapDetector )
2023 mDoubleTapDetector = TapGestureDetector::New();
2024 mDoubleTapDetector.SetTapsRequired( 2 );
2025 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2027 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2028 // so that we do not, unnecessarily, have a double tap request all the time
2031 if ( !mPanGestureDetector )
2033 mPanGestureDetector = PanGestureDetector::New();
2034 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2037 if ( !mLongPressDetector )
2039 mLongPressDetector = LongPressGestureDetector::New();
2040 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2041 mLongPressDetector.Attach(Self());
2045 void TextInput::CreateTextViewActor()
2047 mDisplayedTextView = Toolkit::TextView::New();
2048 mDisplayedTextView.SetName( "DisplayedTextView ");
2049 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2050 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2051 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2052 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2053 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2054 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2055 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2056 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2057 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2058 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2060 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2062 Self().Add( mDisplayedTextView );
2065 // Start a timer to initiate, used by the cursor to blink.
2066 void TextInput::StartCursorBlinkTimer()
2068 if ( !mCursorBlinkTimer )
2070 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2071 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2074 if ( !mCursorBlinkTimer.IsRunning() )
2076 mCursorBlinkTimer.Start();
2080 // Start a timer to initiate, used by the cursor to blink.
2081 void TextInput::StopCursorBlinkTimer()
2083 if ( mCursorBlinkTimer )
2085 mCursorBlinkTimer.Stop();
2089 void TextInput::StartEditMode()
2091 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2093 if(!mEditModeActive)
2098 if ( mDoubleTapDetector )
2100 mDoubleTapDetector.Attach( Self() );
2104 void TextInput::EndEditMode()
2106 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2108 ClearKeyInputFocus();
2110 if ( mDoubleTapDetector )
2112 mDoubleTapDetector.Detach( Self() );
2116 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2118 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2120 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2122 style.SetUnderline( true );
2123 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2127 void TextInput::RemovePreEditStyle()
2129 if ( !mUnderlinedPriorToPreEdit )
2132 style.SetUnderline( false );
2133 SetActiveStyle( style, TextStyle::UNDERLINE );
2137 // IMF related methods
2140 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2142 bool update( false );
2143 bool preeditResetRequired ( false );
2145 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2147 HidePopup(); // If Pop-up shown then hides it as editing text.
2150 switch ( imfEvent.eventName )
2152 case ImfManager::PREEDIT:
2154 mIgnoreFirstCommitFlag = false;
2156 // 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
2157 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2159 // replaces highlighted text with new character
2160 DeleteHighlightedText( false );
2163 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2165 if( IsScrollEnabled() )
2167 // Calculates the new cursor position (in actor coordinates)
2168 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2169 ScrollTextViewToMakeCursorVisible( cursorPosition );
2176 case ImfManager::COMMIT:
2178 if( mIgnoreFirstCommitFlag )
2180 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2181 mIgnoreFirstCommitFlag = false;
2185 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2187 // 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
2188 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2190 // replaces highlighted text with new character
2191 DeleteHighlightedText( false );
2194 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2195 // not needed, one such scenario is when the pre-edit word is too long to fit.
2196 if ( !mIgnoreCommitFlag )
2198 update = CommitReceived( imfEvent.predictiveString );
2202 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2208 if( IsScrollEnabled() )
2210 // Calculates the new cursor position (in actor coordinates)
2211 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2213 ScrollTextViewToMakeCursorVisible( cursorPosition );
2218 case ImfManager::DELETESURROUNDING:
2220 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2221 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2223 mPreEditFlag = false;
2225 std::size_t toDelete = 0;
2226 std::size_t numberOfCharacters = 0;
2228 if( mHighlightMeshActor )
2230 // delete highlighted text.
2231 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2232 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2236 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2238 toDelete = mCursorPosition + imfEvent.cursorOffset;
2240 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2242 numberOfCharacters = mStyledText.size() - toDelete;
2246 numberOfCharacters = imfEvent.numberOfChars;
2249 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2250 DeleteRange( toDelete, numberOfCharacters );
2252 mCursorPosition = toDelete;
2253 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2257 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2260 case ImfManager::GETSURROUNDING:
2262 // 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
2263 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2264 if (! ( mHighlightMeshActor || mSelectingText ) )
2266 std::string text( GetText() );
2267 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2269 imfManager.SetCursorPosition( mCursorPosition );
2270 imfManager.SetSurroundingText( text );
2273 if( 0 != mNumberOfSurroundingCharactersDeleted )
2275 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2276 mNumberOfSurroundingCharactersDeleted = 0;
2278 if( mStyledText.empty() )
2280 // Styled text is empty, so set the placeholder text.
2281 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2282 mPlaceHolderSet = true;
2287 case ImfManager::VOID:
2289 DALI_ASSERT_DEBUG( false );
2293 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2295 return callbackData;
2298 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2300 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2302 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2303 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2305 bool preeditResetRequest ( false );
2307 if( mPreEditFlag ) // Already in pre-edit state.
2309 if( mStyledText.size() >= mMaxStringLength )
2311 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2312 // Cannot fit these characters into field, clear pre-edit.
2313 if ( !mUnderlinedPriorToPreEdit )
2316 style.SetUnderline( false );
2317 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2319 mIgnoreCommitFlag = true;
2320 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2321 mPreEditFlag = false;
2322 EmitMaxInputCharactersReachedSignal();
2326 // delete existing pre-edit string
2327 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2329 // Store new pre-edit string
2330 mPreEditString.SetText( keyString );
2332 if ( keyString.empty() )
2334 mPreEditFlag = false;
2335 mCursorPosition = mPreEditStartPosition;
2337 if( mStyledText.empty() )
2339 // Styled text is empty, so set the placeholder text.
2340 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2341 mPlaceHolderSet = true;
2345 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2347 GetTextLayoutInfo();
2352 // Insert new pre-edit string. InsertAt updates the size and position table.
2353 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2354 // 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.
2355 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2356 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2357 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2360 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2364 else // mPreEditFlag not set
2366 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2368 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2369 // new pre-edit so move into pre-edit state by setting flag
2370 mPreEditFlag = true;
2371 mPreEditString.SetText( keyString ); // store new pre-edit string
2372 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2373 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2374 // 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.
2375 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2376 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2377 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2378 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2384 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2388 return preeditResetRequest;
2391 bool TextInput::CommitReceived(const std::string& keyString )
2393 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2394 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2396 bool update( false );
2398 RemovePreEditStyle();
2400 const std::size_t styledTextSize( mStyledText.size() );
2401 if( styledTextSize >= mMaxStringLength )
2403 // Cannot fit these characters into field, clear pre-edit.
2406 mIgnoreCommitFlag = true;
2407 mPreEditFlag = false;
2409 EmitMaxInputCharactersReachedSignal();
2415 // delete existing pre-edit string
2416 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2417 mPreEditFlag = false;
2419 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2420 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2422 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2424 // No need to update cursor position as Cursor location given by touch.
2425 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2426 mPreserveCursorPosition = false;
2430 // Cursor not set by touch so needs to be re-positioned to input more text
2431 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2433 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2434 if ( mCommitByKeyInput )
2436 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2437 mCommitByKeyInput = false;
2443 if ( mSelectTextOnCommit )
2445 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2450 else // mPreEditFlag not set
2452 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2454 if( mStyledText.empty() && mPlaceHolderSet )
2456 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2457 mDisplayedTextView.SetText( "" );
2458 mNumberOfSurroundingCharactersDeleted = 0;
2459 mPlaceHolderSet = false;
2461 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2463 mNumberOfSurroundingCharactersDeleted = 0;
2468 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2473 mSelectTextOnCommit = false;
2475 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2476 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2481 // End of IMF related methods
2483 std::size_t TextInput::DeletePreEdit()
2485 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2487 DALI_ASSERT_DEBUG( mPreEditFlag );
2489 const std::size_t preEditStringLength = mPreEditString.GetLength();
2490 const std::size_t styledTextSize = mStyledText.size();
2492 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2494 // Prevents erase items outside mStyledText bounds.
2495 if( mPreEditStartPosition > styledTextSize )
2497 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2498 mPreEditStartPosition = styledTextSize;
2501 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2503 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2504 endPosition = styledTextSize;
2507 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2509 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2510 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2512 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2514 return preEditStringLength;
2517 void TextInput::PreEditReset( bool preserveCursorPosition )
2519 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2520 preserveCursorPosition, mCursorPosition);
2522 // 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.
2523 mPreserveCursorPosition = preserveCursorPosition;
2525 // Reset incase we are in a pre-edit state.
2526 ImfManager imfManager = ImfManager::Get();
2529 imfManager.Reset(); // Will trigger a commit message
2533 void TextInput::CursorUpdate()
2537 ImfManager imfManager = ImfManager::Get();
2540 std::string text( GetText() );
2541 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2542 imfManager.SetCursorPosition ( mCursorPosition );
2543 imfManager.NotifyCursorPosition();
2547 /* Delete highlighted characters redisplay*/
2548 void TextInput::DeleteHighlightedText( bool inheritStyle )
2550 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2552 if( mHighlightMeshActor )
2554 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2556 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2557 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2559 // Get the styled text of the characters to be deleted as it may be needed if
2560 // the "exceed the text-input's boundaries" option is disabled.
2561 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2563 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2565 mStyledText.erase( start, end ); // erase range of characters
2567 // Remove text from TextView.
2569 if( mStyledText.empty() )
2571 // Styled text is empty, so set the placeholder text.
2572 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2573 mPlaceHolderSet = true;
2577 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2579 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2581 // It may happen than after removing a white space or a new line character,
2582 // two words merge, this new word could be big enough to not fit in its
2583 // current line, so moved to the next one, and make some part of the text to
2584 // exceed the text-input's boundary.
2585 if( !mExceedEnabled )
2587 // Get the new text layout after removing some characters.
2588 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2590 // Get text-input's size.
2591 const Vector3& size = GetControlSize();
2593 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2594 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2596 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2598 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2599 styledCharactersToDelete.begin(),
2600 styledCharactersToDelete.end() );
2604 GetTextLayoutInfo();
2612 const TextStyle oldInputStyle( mInputStyle );
2614 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2616 if( oldInputStyle != mInputStyle )
2618 // Updates the line height accordingly with the input style.
2621 EmitStyleChangedSignal();
2627 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2629 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2630 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2632 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2635 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2637 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2638 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2640 mStyledText.erase(itStart, itEnd);
2642 // update the selection handles if they are visible.
2643 if( mHighlightMeshActor )
2645 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2646 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2648 if( minHandle >= start + ncharacters )
2650 minHandle -= ncharacters;
2652 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2657 if( maxHandle >= start + ncharacters )
2659 maxHandle -= ncharacters;
2661 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2667 // 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.
2670 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2672 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2673 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2674 // Mean we do not re-draw the text more than we have too.
2677 /* Delete character at current cursor position and redisplay*/
2678 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2680 // Ensure positionToDelete is not out of bounds.
2681 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2682 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2683 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2685 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2688 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2690 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2692 // Get the styled text of the character to be deleted as it may be needed if
2693 // the "exceed the text-input's boundaries" option is disabled.
2694 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2696 mStyledText.erase(it); // erase the character left of positionToDelete
2698 if( mStyledText.empty() )
2700 // Styled text is empty, so set the placeholder text.
2701 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2702 mPlaceHolderSet = true;
2706 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2708 const Character characterToDelete = styledCharacterToDelete.mText[0];
2710 // It may happen than after removing a white space or a new line character,
2711 // two words merge, this new word could be big enough to not fit in its
2712 // current line, so moved to the next one, and make some part of the text to
2713 // exceed the text-input's boundary.
2714 if( !mExceedEnabled )
2716 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2718 // Get the new text layout after removing one character.
2719 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2721 // Get text-input's size.
2722 const Vector3& size = GetControlSize();
2724 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2725 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2727 MarkupProcessor::StyledTextArray array;
2728 array.push_back( styledCharacterToDelete );
2729 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2731 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2736 GetTextLayoutInfo();
2738 ShowGrabHandleAndSetVisibility( false );
2740 mCursorPosition = positionToDelete -1;
2742 const TextStyle oldInputStyle( mInputStyle );
2744 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2746 if( oldInputStyle != mInputStyle )
2748 // Updates the line height accordingly with the input style.
2751 EmitStyleChangedSignal();
2756 /*Insert new character into the string and (optionally) redisplay text-input*/
2757 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2759 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2761 // Ensure insertionPosition is not out of bounds.
2762 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2764 bool textExceedsMaximunNumberOfCharacters = false;
2765 bool textExceedsBoundary = false;
2766 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2768 ShowGrabHandleAndSetVisibility( false );
2770 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2774 mIgnoreCommitFlag = true;
2775 mPreEditFlag = false;
2776 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2777 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2780 if( textExceedsMaximunNumberOfCharacters )
2782 EmitMaxInputCharactersReachedSignal();
2785 if( textExceedsBoundary )
2787 EmitInputTextExceedsBoundariesSignal();
2788 PreEditReset( false );
2792 return insertedStringLength;
2795 ImageActor TextInput::CreateCursor( const Vector4& color)
2798 cursor = CreateSolidColorActor(color);
2799 cursor.SetName( "Cursor" );
2801 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2802 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2803 cursor.SetVisible(false);
2808 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2810 // As cursor is not moving due to grab handle, handle should be hidden.
2811 ShowGrabHandleAndSetVisibility( false );
2813 bool cursorPositionChanged = false;
2816 if ( mCursorPosition >= places )
2818 mCursorPosition = mCursorPosition - places;
2819 cursorPositionChanged = true;
2824 if ((mCursorPosition + places) <= mStyledText.size())
2826 mCursorPosition = mCursorPosition + places;
2827 cursorPositionChanged = true;
2831 if( cursorPositionChanged )
2833 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2835 const TextStyle oldInputStyle( mInputStyle );
2836 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2840 if( oldInputStyle != mInputStyle )
2842 // Updates the line height accordingly with the input style.
2845 EmitStyleChangedSignal();
2848 ImfManager imfManager = ImfManager::Get();
2851 imfManager.SetCursorPosition ( mCursorPosition );
2852 imfManager.NotifyCursorPosition();
2857 void TextInput::DrawCursor(const std::size_t nthChar)
2859 // Get height of cursor and set its size
2860 Size size( CURSOR_THICKNESS, 0.0f );
2861 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2863 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2867 // Measure Font so know how big text will be if no initial text to measure.
2868 size.height = mLineHeight;
2871 mCursor.SetSize(size);
2873 // If the character is italic then the cursor also tilts.
2874 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2876 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2878 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2880 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2881 bool altPositionValid; // Alternate cursor validity flag.
2882 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2883 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2885 SetAltCursorEnabled( altPositionValid );
2887 if(!altPositionValid)
2889 mCursor.SetPosition( position + UI_OFFSET );
2893 size.height *= 0.5f;
2894 mCursor.SetSize(size);
2895 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2897 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2898 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2899 size.height = rowSize.height * 0.5f;
2900 mCursorRTL.SetSize(size);
2901 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2904 if( IsScrollEnabled() )
2906 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2907 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2912 void TextInput::SetAltCursorEnabled( bool enabled )
2914 mCursorRTLEnabled = enabled;
2915 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2918 void TextInput::SetCursorVisibility( bool visible )
2920 mCursorVisibility = visible;
2921 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2922 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2925 void TextInput::CreateGrabHandle( Dali::Image image )
2931 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2935 mGrabHandleImage = image;
2938 mGrabHandle = ImageActor::New(mGrabHandleImage);
2939 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2940 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2942 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2944 ShowGrabHandleAndSetVisibility( false );
2946 CreateGrabArea( mGrabHandle );
2948 mActiveLayer.Add(mGrabHandle);
2952 void TextInput::CreateGrabArea( Actor& parent )
2954 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2955 mGrabArea.SetName( "GrabArea" );
2956 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2957 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2958 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2959 mTapDetector.Attach( mGrabArea );
2960 mPanGestureDetector.Attach( mGrabArea );
2961 mLongPressDetector.Attach( mGrabArea );
2963 parent.Add(mGrabArea);
2966 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2968 Vector3 actualHandlePosition;
2972 mActualGrabHandlePosition.x += displacement.x;
2973 mActualGrabHandlePosition.y += displacement.y;
2975 // Grab handle should jump to the nearest character and take cursor with it
2976 std::size_t newCursorPosition = 0;
2977 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2979 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2981 bool handleVisible = true;
2983 if( IsScrollEnabled() )
2985 const Vector3 controlSize = GetControlSize();
2986 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2987 // Scrolls the text if the handle is not in a visible position
2988 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2995 mCurrentHandlePosition = actualHandlePosition;
2996 mScrollDisplacement = Vector2::ZERO;
3000 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3002 mScrollDisplacement.x = -SCROLL_SPEED;
3004 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3006 mScrollDisplacement.x = SCROLL_SPEED;
3008 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3010 mScrollDisplacement.y = -SCROLL_SPEED;
3012 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3014 mScrollDisplacement.y = SCROLL_SPEED;
3020 if( handleVisible && // Only redraw cursor and do updates if position changed
3021 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3023 mCursorPosition = newCursorPosition;
3025 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3027 const TextStyle oldInputStyle( mInputStyle );
3029 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3031 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3033 if( oldInputStyle != mInputStyle )
3035 // Updates the line height accordingly with the input style.
3038 EmitStyleChangedSignal();
3043 return actualHandlePosition;
3046 void TextInput::ShowGrabHandle( bool visible )
3048 if ( IsGrabHandleEnabled() )
3052 mGrabHandle.SetVisible( mGrabHandleVisibility );
3054 StartMonitoringStageForTouch();
3058 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3060 mGrabHandleVisibility = visible;
3061 ShowGrabHandle( visible );
3064 // Callbacks connected to be Property notifications for Boundary checking.
3066 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3068 mIsSelectionHandleOneFlipped = true;
3069 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3070 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3073 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3075 mIsSelectionHandleOneFlipped = false;
3076 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3077 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3080 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3082 mIsSelectionHandleTwoFlipped = true;
3083 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3084 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3087 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3089 mIsSelectionHandleTwoFlipped = false;
3090 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3091 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3094 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3095 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3097 mSelectionHandleOne.SetOpacity(0.0f);
3100 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3102 mSelectionHandleOne.SetOpacity(1.0f);
3105 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3107 mSelectionHandleTwo.SetOpacity(0.0f);
3110 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3112 mSelectionHandleTwo.SetOpacity(1.0f);
3115 // End of Callbacks connected to be Property notifications for Boundary checking.
3117 void TextInput::SetUpHandlePropertyNotifications()
3119 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3121 Vector3 handlesize = GetSelectionHandleSize();
3123 // Exceeding horizontal boundary
3124 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3125 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3127 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3128 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3130 // Within horizontal boundary
3131 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3132 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3134 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3135 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3137 // Exceeding vertical boundary
3138 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3139 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3140 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3141 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3143 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3144 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3145 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3146 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3148 // Within vertical boundary
3149 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3150 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3151 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3152 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3154 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3155 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3156 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3157 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3160 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3162 mSelectionHandleOnePosition = start;
3163 mSelectionHandleTwoPosition = end;
3165 if ( !mSelectionHandleOne )
3167 // create normal and pressed images
3168 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3169 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3171 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3172 mSelectionHandleOne.SetName("SelectionHandleOne");
3173 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3174 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3175 mIsSelectionHandleOneFlipped = false;
3176 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3178 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3179 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3181 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3182 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3184 mTapDetector.Attach( mHandleOneGrabArea );
3185 mPanGestureDetector.Attach( mHandleOneGrabArea );
3187 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3189 mSelectionHandleOne.Add( mHandleOneGrabArea );
3190 mActiveLayer.Add( mSelectionHandleOne );
3193 if ( !mSelectionHandleTwo )
3195 // create normal and pressed images
3196 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3197 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3199 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3200 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3201 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3202 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3203 mIsSelectionHandleTwoFlipped = false;
3204 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3206 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3207 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3208 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3209 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3211 mTapDetector.Attach( mHandleTwoGrabArea );
3212 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3214 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3216 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3218 mActiveLayer.Add( mSelectionHandleTwo );
3221 SetUpHandlePropertyNotifications();
3223 // update table as text may have changed.
3224 GetTextLayoutInfo();
3226 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3227 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3229 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3230 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3232 // Calculates and set the visibility if the scroll mode is enabled.
3233 bool isSelectionHandleOneVisible = true;
3234 bool isSelectionHandleTwoVisible = true;
3235 if( IsScrollEnabled() )
3237 const Vector3& controlSize( GetControlSize() );
3238 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3239 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3240 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3241 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3244 CreateHighlight(); // function will only create highlight if not already created.
3247 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3249 Vector3 actualHandlePosition;
3251 if ( mSelectionHandleOne && mSelectionHandleTwo )
3253 const Vector3& controlSize = GetControlSize();
3255 Size cursorSize( CURSOR_THICKNESS, 0.f );
3257 // Get a reference of the wanted selection handle (handle one or two).
3258 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3260 // Get a reference for the current position of the handle and a copy of its pair
3261 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3262 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3264 // Get a handle of the selection handle actor
3265 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3267 // Selection handles should jump to the nearest character
3268 std::size_t newHandlePosition = 0;
3269 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3271 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3273 bool handleVisible = true;
3275 if( IsScrollEnabled() )
3277 mCurrentSelectionId = handleId;
3279 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3280 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3281 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3288 mCurrentSelectionHandlePosition = actualHandlePosition;
3289 mScrollDisplacement = Vector2::ZERO;
3293 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3295 mScrollDisplacement.x = -SCROLL_SPEED;
3297 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3299 mScrollDisplacement.x = SCROLL_SPEED;
3301 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3303 mScrollDisplacement.y = -SCROLL_SPEED;
3305 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3307 mScrollDisplacement.y = SCROLL_SPEED;
3313 if ( handleVisible && // Ensure the handle is visible.
3314 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3315 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3317 currentSelectionHandlePosition = newHandlePosition;
3319 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3320 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3324 if ( handleId == HandleOne )
3326 const TextStyle oldInputStyle( mInputStyle );
3328 // Set Active Style to that of first character in selection
3329 if( mSelectionHandleOnePosition < mStyledText.size() )
3331 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3334 if( oldInputStyle != mInputStyle )
3336 // Updates the line height accordingly with the input style.
3339 EmitStyleChangedSignal();
3345 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3348 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3351 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3352 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3354 if ( selectionHandleActor )
3356 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3357 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3358 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3360 if( IsScrollEnabled() )
3362 const Size cursorSize( CURSOR_THICKNESS,
3363 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3364 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3366 GetControlSize() ) );
3371 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3373 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3374 // For now the visual position of end of logical string will be end of visual string.
3375 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3377 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3380 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3382 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3383 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3384 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3385 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3387 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3389 // Deselect text prior to startSelectionIt
3390 for(;it!=startSelectionIt;++it)
3392 selectedVisualText[*it] = false;
3395 // Select text from startSelectionIt -> endSelectionIt
3396 for(;it!=endSelectionIt;++it)
3398 selectedVisualText[*it] = true;
3401 // Deselect text after endSelection
3404 selectedVisualText[*it] = false;
3407 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3410 // Calculate the dimensions of the quads they will make the highlight mesh
3411 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3413 // At the moment there is no public API to modify the block alignment option.
3414 const bool blockAlignEnabled = true;
3416 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3418 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3420 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3421 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3423 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3424 std::vector<bool> selectedVisualText;
3425 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3426 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3427 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3429 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3430 float rowLeft = 0.0f;
3431 float rowRight = 0.0f;
3432 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3433 float maxRowLeft = std::numeric_limits<float>::max();
3434 float maxRowRight = 0.0f;
3436 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3438 // Scan through entire text.
3441 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3443 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3444 bool charSelected( false );
3445 if( selectedIt != selectedEndIt )
3447 charSelected = *selectedIt++;
3450 if(selectionState == SelectionNone)
3454 selectionState = SelectionStarted;
3455 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3456 rowRight = rowLeft + charInfo.mSize.width;
3459 else if(selectionState == SelectionStarted)
3461 // break selection on:
3462 // 1. new line causing selection break. (\n or wordwrap)
3463 // 2. character not selected.
3464 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3467 // finished selection.
3468 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3469 // that it resides on. That way this enumeration is not necessary.
3471 if(lastIt->mIsNewParagraphChar)
3473 // 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.
3474 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3476 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3477 maxRowLeft = std::min(maxRowLeft, min.x);
3478 maxRowRight = std::max(maxRowRight, max.x);
3479 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3480 float rowTop = rowBottom - rowSize.height;
3482 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3483 if(charSelected && blockAlignEnabled)
3485 rowRight = std::numeric_limits<float>::max();
3487 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3489 selectionState = SelectionNone;
3491 // Still selected? start a new selection
3494 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3495 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3496 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3497 selectionState = SelectionStarted;
3502 // build up highlight(s) with this selection data.
3503 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3504 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3511 // If reached end, and still on selection, then close selection.
3514 if(selectionState == SelectionStarted)
3516 // finished selection.
3518 if(lastIt->mIsNewParagraphChar)
3520 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3522 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3523 maxRowLeft = std::min(maxRowLeft, min.x);
3524 maxRowRight = std::max(maxRowRight, max.x);
3525 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3526 float rowTop = rowBottom - rowSize.height;
3527 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3531 // Get the top left and bottom right corners.
3532 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3533 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3534 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3536 // Clamp quads so they appear to clip to borders of the whole text.
3537 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3539 // For block-align align Further Clamp quads to max left and right extents
3540 if(blockAlignEnabled)
3542 // BlockAlign: Will adjust highlight to block:
3544 // H[ello] (top row right = max of all rows right)
3545 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3546 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3547 // [text] (bottom row left = min of all rows left)
3548 // (common in SMS messaging selection)
3550 // As opposed to the default which is tight text highlighting.
3555 // (common in regular text editors/web browser selection)
3557 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3560 // Finally clamp quads again so they don't exceed the boundry of the control.
3561 const Vector3& controlSize = GetControlSize();
3562 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3565 return mNewHighlightInfo;
3568 void TextInput::UpdateHighlight()
3570 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3572 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3574 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3575 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3576 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3577 // [BOTTOM] [ MIDDLE ]
3580 // Each quad is created as 2 triangles.
3581 // Middle is just 1 quad regardless of its size.
3595 if ( mHighlightMeshActor )
3597 // vertex and triangle buffers should always be present if MeshActor is alive.
3598 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3599 MeshData::VertexContainer vertices;
3600 Dali::MeshData::FaceIndices faceIndices;
3602 if( !newHighlightInfo.mQuadList.empty() )
3604 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3605 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3607 // vertex position defaults to (0 0 0)
3608 MeshData::Vertex vertex;
3609 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3612 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3614 // Add each quad geometry (a sub-selection) to the mesh data.
3624 QuadCoordinates& quad = *iter;
3626 vertex.x = quad.min.x;
3627 vertex.y = quad.min.y;
3628 vertices.push_back( vertex );
3631 vertex.x = quad.max.x;
3632 vertex.y = quad.min.y;
3633 vertices.push_back( vertex );
3635 // bottom-left (v+2)
3636 vertex.x = quad.min.x;
3637 vertex.y = quad.max.y;
3638 vertices.push_back( vertex );
3640 // bottom-right (v+3)
3641 vertex.x = quad.max.x;
3642 vertex.y = quad.max.y;
3643 vertices.push_back( vertex );
3645 // triangle A (3, 1, 0)
3646 faceIndices.push_back( v + 3 );
3647 faceIndices.push_back( v + 1 );
3648 faceIndices.push_back( v );
3650 // triangle B (0, 2, 3)
3651 faceIndices.push_back( v );
3652 faceIndices.push_back( v + 2 );
3653 faceIndices.push_back( v + 3 );
3655 mMeshData.SetFaceIndices( faceIndices );
3658 BoneContainer bones(0); // passed empty as bones not required
3659 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3660 mHighlightMesh.UpdateMeshData(mMeshData);
3665 void TextInput::ClearPopup()
3667 mPopupPanel.Clear();
3670 void TextInput::AddPopupOptions()
3672 mPopupPanel.AddPopupOptions();
3675 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3677 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3679 Vector3 clampedPosition ( position );
3680 Vector3 tailOffsetPosition ( position );
3682 float xOffSet( 0.0f );
3684 Actor self = Self();
3685 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3687 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3688 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3690 // Clamp to left or right or of boundary
3691 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3693 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3695 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3697 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3700 clampedPosition.x = position.x + xOffSet;
3701 tailOffsetPosition.x = -xOffSet;
3703 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3704 bool flipTail( false );
3706 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3708 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3712 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3713 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3716 void TextInput::HidePopup(bool animate, bool signalFinished )
3718 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3720 mPopupPanel.Hide( animate );
3722 if( animate && signalFinished )
3724 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3729 void TextInput::ShowPopup( bool animate )
3732 Vector2 alternativePopupPosition;
3734 if(mHighlightMeshActor && mState == StateEdit)
3737 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3739 // When text is selected, show popup above top handle (and text), or below bottom handle.
3740 // topHandle: referring to the top most point of the handle or the top line of selection.
3741 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3743 topHandle = mSelectionHandleOneActualPosition;
3744 bottomHandle = mSelectionHandleTwoActualPosition;
3745 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3749 topHandle = mSelectionHandleTwoActualPosition;
3750 bottomHandle = mSelectionHandleOneActualPosition;
3751 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3753 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3754 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3756 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3758 position.x = xPosition;
3760 // Alternative position if no upper space
3761 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3762 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3766 // When no text is selected, show popup at world position of grab handle or cursor
3767 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3768 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3769 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3770 // if can't be positioned above, then position below row.
3771 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3774 // If grab handle enabled then position pop-up below the grab handle.
3775 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3779 SetPopupPosition( position, alternativePopupPosition );
3782 mPopupPanel.Show( Self(), animate );
3783 StartMonitoringStageForTouch();
3785 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3788 void TextInput::ShowPopupCutCopyPaste()
3792 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3793 // Check the selected text is whole text or not.
3794 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3796 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3799 if ( !mStyledText.empty() )
3801 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3802 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3805 if( mClipboard && mClipboard.NumberOfItems() )
3807 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3808 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3813 mPopupPanel.Hide(false);
3817 void TextInput::SetUpPopupSelection( bool showCutButton )
3820 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3821 // If no text exists then don't offer to select
3822 if ( !mStyledText.empty() )
3824 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3825 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3826 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, showCutButton );
3828 // if clipboard has valid contents then offer paste option
3829 if( mClipboard && mClipboard.NumberOfItems() )
3831 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3832 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3837 mPopupPanel.Hide(false);
3840 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3845 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3846 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3847 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3848 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3850 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3852 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3854 float closestYdifference = std::numeric_limits<float>::max();
3855 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3856 std::size_t numberOfMatchedCharacters = 0;
3858 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3859 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3861 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3863 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3864 float baselinePosition = info.mPosition.y - info.mDescender;
3866 if( info.mIsVisible )
3868 // store difference between source y point and the y position of the current character
3869 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3871 if( currentYdifference < closestYdifference )
3873 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3874 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3875 closestYdifference = currentYdifference;
3876 matchedCharacters.clear();
3877 numberOfMatchedCharacters = 0; // reset count
3880 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3881 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3883 // ignore new line character.
3884 if( !info.mIsNewParagraphChar )
3886 matchedCharacters.push_back( info );
3887 numberOfMatchedCharacters++;
3891 } // End of loop checking each character's y position in the character layout table
3893 // Check if last character is a newline, if it is
3894 // then need pretend there is an imaginary line afterwards,
3895 // and check if user is touching below previous line.
3896 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3898 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3900 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3904 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3905 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3907 bool matched( false );
3909 // 2 Iterate through matching list of y positions and find closest matching X position.
3910 for( ; it != endIt; ++it )
3912 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3914 if( info.mIsVisible )
3916 // stop when on left side of character's center.
3917 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3918 if( sourceScrollOffset.x < characterMidPointPosition )
3920 if(info.mIsRightToLeftCharacter)
3922 rightToLeftChar = true;
3924 glyphIntersection = info.mPosition.x;
3929 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3935 rightToLeftChar = lastRightToLeftChar;
3938 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3939 closestIndex = lineOffset + matchCharacterIndex;
3941 mClosestCursorPositionEOL = false; // reset
3942 if ( it == endIt && !matched )
3944 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3947 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3948 if( rightToLeftChar && lastRightToLeftChar )
3950 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3955 // closestIndex is the visual index, need to convert it to the logical index
3956 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3958 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3960 // Checks for situations where user is touching between LTR and RTL
3961 // characters. To identify if the user means the end of a LTR string
3962 // or the beginning of an RTL string, and vice versa.
3963 if( closestIndex > 0 )
3965 if( rightToLeftChar && !lastRightToLeftChar )
3970 // A: In this touch range, the user is indicating that they wish to place
3971 // the cursor at the end of the LTR text.
3972 // B: In this touch range, the user is indicating that they wish to place
3973 // the cursor at the end of the RTL text.
3975 // Result of touching A area:
3976 // [.....LTR]|[RTL......]+
3978 // |: primary cursor (for typing LTR chars)
3979 // +: secondary cursor (for typing RTL chars)
3981 // Result of touching B area:
3982 // [.....LTR]+[RTL......]|
3984 // |: primary cursor (for typing RTL chars)
3985 // +: secondary cursor (for typing LTR chars)
3987 if( sourceScrollOffset.x < glyphIntersection )
3992 else if( !rightToLeftChar && lastRightToLeftChar )
3994 if( sourceScrollOffset.x < glyphIntersection )
4001 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4002 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4003 // one further ahead
4004 if( rightToLeftChar && !lastRightToLeftChar )
4009 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4011 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4013 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4022 float TextInput::GetLineJustificationPosition() const
4024 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4025 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4026 float alignmentOffset = 0.f;
4028 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4029 if( alignment & Toolkit::Alignment::HorizontalLeft )
4031 alignmentOffset = 0.f;
4033 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4035 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4037 else if( alignment & Toolkit::Alignment::HorizontalRight )
4039 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4042 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4043 float justificationOffset = 0.f;
4045 switch( justification )
4047 case Toolkit::TextView::Left:
4049 justificationOffset = 0.f;
4052 case Toolkit::TextView::Center:
4054 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4057 case Toolkit::TextView::Right:
4059 justificationOffset = mTextLayoutInfo.mTextSize.width;
4062 case Toolkit::TextView::Justified:
4064 justificationOffset = 0.f;
4069 DALI_ASSERT_ALWAYS( false );
4073 return alignmentOffset + justificationOffset;
4076 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4078 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4079 A newline character is not inserted in this case */
4081 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4083 Vector3 cursorPosition;
4085 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4087 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4089 // end character so use
4090 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4091 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4095 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4098 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4100 // If previous character on a different line then use current characters position
4101 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4103 if ( mClosestCursorPositionEOL )
4105 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4109 cursorPosition = Vector3(currentCharInfo.mPosition);
4114 // Previous character is on same line so use position of previous character plus it's width.
4115 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4118 return cursorPosition;
4121 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4123 bool direction(false);
4124 Vector3 alternatePosition;
4125 bool alternatePositionValid(false);
4127 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4130 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4132 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4134 alternatePositionValid = false;
4135 directionRTL = false;
4137 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4139 std::size_t visualCharacterPosition;
4141 // When cursor is not at beginning, consider possibility of
4142 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4143 if(characterPosition > 0)
4145 // Cursor position should be the end of the last character.
4146 // If the last character is LTR, then the end is on the right side of the glyph.
4147 // If the last character is RTL, then the end is on the left side of the glyph.
4148 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4150 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4152 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4155 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4156 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4158 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4159 const Vector3& size = GetControlSize();
4161 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4163 --visualCharacterPosition;
4165 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4168 if(!info.mIsNewParagraphChar)
4170 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4174 // When cursor points to first character on new line, position cursor at the start of this glyph.
4175 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4177 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4178 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4179 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4181 cursorPosition.x = infoNext.mPosition.x + start;
4182 cursorPosition.y = infoNext.mPosition.y;
4186 // If cursor points to the end of text, then can only position
4187 // cursor where the new line starts based on the line-justification position.
4188 cursorPosition.x = GetLineJustificationPosition();
4190 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4192 // If this is after the last character, then we can assume that the new cursor
4193 // should be exactly one row below the current row.
4195 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4196 cursorPosition.y = info.mPosition.y + rowRect.height;
4200 // If this is not after last character, then we can use this row's height.
4201 // should be exactly one row below the current row.
4203 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4204 cursorPosition.y = info.mPosition.y + rowRect.height;
4209 directionRTL = info.mIsRightToLeftCharacter;
4211 // 1. When the cursor is neither at the beginning or the end,
4212 // we can show multiple cursors under situations when the cursor is
4213 // between RTL and LTR text...
4214 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4216 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4218 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4219 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4221 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4223 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4224 // Text: [...LTR...]|[...RTL...]
4226 // Alternate cursor pos: ^
4227 // In which case we need to display an alternate cursor for the RTL text.
4229 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4230 alternatePosition.y = infoAlt.mPosition.y;
4231 alternatePositionValid = true;
4233 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4235 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4236 // Text: |[...RTL...] [...LTR....]
4238 // Alternate cursor pos: ^
4239 // In which case we need to display an alternate cursor for the RTL text.
4241 alternatePosition.x = infoAlt.mPosition.x;
4242 alternatePosition.y = infoAlt.mPosition.y;
4243 alternatePositionValid = true;
4248 // 2. When the cursor is at the end of the text,
4249 // and we have multi-directional text,
4250 // we can also consider showing mulitple cursors.
4251 // The rule here is:
4252 // If first and last characters on row are different
4253 // Directions, then two cursors need to be displayed.
4255 // Get first logical glyph on row
4256 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4258 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4259 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4261 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4263 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4264 // Text: [...LTR...]|[...RTL...]
4266 // Alternate cursor pos: ^
4267 // In which case we need to display an alternate cursor for the RTL text, this cursor
4268 // should be at the end of the given line.
4270 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4271 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4272 alternatePosition.y = infoAlt.mPosition.y;
4273 alternatePositionValid = true;
4275 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4277 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4278 // Text: |[...RTL...] [...LTR....]
4280 // Alternate cursor pos: ^
4281 // In which case we need to display an alternate cursor for the RTL text.
4283 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4284 alternatePosition.x = infoAlt.mPosition.x;
4285 alternatePosition.y = infoAlt.mPosition.y;
4286 alternatePositionValid = true;
4289 } // characterPosition > 0
4290 else if(characterPosition == 0)
4292 // When the cursor position is at the beginning, it should be at the start of the current character.
4293 // If the current character is LTR, then the start is on the right side of the glyph.
4294 // If the current character is RTL, then the start is on the left side of the glyph.
4295 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4297 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4299 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4302 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4303 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4305 cursorPosition.x = info.mPosition.x + start;
4306 cursorPosition.y = info.mPosition.y;
4307 directionRTL = info.mIsRightToLeftCharacter;
4312 // If the character table is void, place the cursor accordingly the text alignment.
4313 const Vector3& size = GetControlSize();
4315 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4316 float alignmentOffset = 0.f;
4318 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4319 if( alignment & Toolkit::Alignment::HorizontalLeft )
4321 alignmentOffset = 0.f;
4323 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4325 alignmentOffset = 0.5f * ( size.width );
4327 else if( alignment & Toolkit::Alignment::HorizontalRight )
4329 alignmentOffset = size.width;
4332 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4333 cursorPosition.x = alignmentOffset;
4335 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4336 if( alignment & Toolkit::Alignment::VerticalTop )
4338 cursorPosition.y = mLineHeight;
4340 else if( alignment & Toolkit::Alignment::VerticalCenter )
4342 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4344 else if( alignment & Toolkit::Alignment::VerticalBottom )
4346 cursorPosition.y = size.height;
4350 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4351 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4352 if( alternatePositionValid )
4354 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4355 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4358 return cursorPosition;
4361 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4363 // scan string from current position to beginning of current line to note direction of line
4364 while(logicalPosition)
4367 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4368 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4375 return logicalPosition;
4378 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4382 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4385 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4387 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4388 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4390 min = Vector2::ZERO;
4391 max = Vector2(0.0f, mLineHeight);
4395 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4396 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4397 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4399 // If cursor is pointing to end of line, then start from last character.
4400 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4402 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4404 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4405 if( !it->mIsVisible )
4407 characterPosition = FindVisibleCharacter( Left, characterPosition );
4408 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4411 // Scan characters left and right of cursor, stopping when end of line/string reached or
4412 // y position greater than threshold of reference line.
4414 // 1. scan left until we reach the beginning or a different line.
4415 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4416 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4417 // min-x position is the left-most char's left (x)
4418 // max-x position is the right-most char's right (x)
4419 // min-y position is the minimum of all character's top (y)
4420 // max-y position is the maximum of all character's bottom (y+height)
4421 min.y = validCharIt->mPosition.y;
4422 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4427 min.y = std::min(min.y, validCharIt->mPosition.y);
4428 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4437 if( (it->mPosition.y < referenceLine) ||
4438 (it->mIsNewParagraphChar) ||
4445 // info refers to the first character on this line.
4446 min.x = validCharIt->mPosition.x;
4448 // 2. scan right until we reach end or a different line
4449 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4450 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4454 if( (it->mPosition.y > referenceLine) ||
4455 (it->mIsNewParagraphChar) ||
4462 min.y = std::min(min.y, validCharIt->mPosition.y);
4463 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4468 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4470 if ( validCharIt != end )
4472 // info refers to the last character on this line.
4473 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4476 return Size( max.x - min.x, max.y - min.y );
4479 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4481 Actor popUpPanel = mPopupPanel.GetRootActor();
4483 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4489 Dali::Actor parent( touchedActor.GetParent() );
4493 return WasTouchedCheck( parent );
4500 void TextInput::StartMonitoringStageForTouch()
4502 Stage stage = Stage::GetCurrent();
4503 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4506 void TextInput::EndMonitoringStageForTouch()
4508 Stage stage = Stage::GetCurrent();
4509 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4512 void TextInput::OnStageTouched(const TouchEvent& event)
4514 if( event.GetPointCount() > 0 )
4516 if ( TouchPoint::Down == event.GetPoint(0).state )
4518 const Actor touchedActor(event.GetPoint(0).hitActor);
4520 bool popUpShown( false );
4522 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4527 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4529 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4531 EndMonitoringStageForTouch();
4532 HidePopup( true, false );
4535 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4537 EndMonitoringStageForTouch();
4538 ShowGrabHandleAndSetVisibility( false );
4544 void TextInput::SelectText(std::size_t start, std::size_t end)
4546 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4547 IsGrabHandleEnabled()?"true":"false",
4548 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4549 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4550 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4552 StartMonitoringStageForTouch();
4554 if ( mEditModeActive ) // Only allow text selection when in edit mode
4556 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4557 mSelectingText = true;
4559 std::size_t selectionStartPosition = std::min( start, end );
4561 // Hide grab handle when selecting.
4562 ShowGrabHandleAndSetVisibility( false );
4564 if( start != end ) // something to select
4566 SetCursorVisibility( false );
4567 StopCursorBlinkTimer();
4569 CreateSelectionHandles(start, end);
4572 const TextStyle oldInputStyle( mInputStyle );
4573 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4575 if( oldInputStyle != mInputStyle )
4577 // Updates the line height accordingly with the input style.
4580 EmitStyleChangedSignal();
4586 mSelectingText = false;
4590 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4592 MarkupProcessor::StyledTextArray currentSelectedText;
4594 if ( IsTextSelected() )
4596 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4597 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4599 for(; it != end; ++it)
4601 MarkupProcessor::StyledText& styledText( *it );
4602 currentSelectedText.push_back( styledText );
4605 return currentSelectedText;
4608 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4610 const std::size_t beginIndex = std::min( begin, end );
4611 const std::size_t endIndex = std::max( begin, end );
4614 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4616 // Create a styled text array used to replace the text into the text-view.
4617 MarkupProcessor::StyledTextArray text;
4618 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4620 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4621 GetTextLayoutInfo();
4623 if( IsScrollEnabled() )
4625 // Need to set the scroll position as the text's size may have changed.
4626 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4629 ShowGrabHandleAndSetVisibility( false );
4635 // Set Handle positioning as the new style may have repositioned the characters.
4636 SetSelectionHandlePosition(HandleOne);
4637 SetSelectionHandlePosition(HandleTwo);
4640 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4642 // Just hide the grab handle when keyboard is hidden.
4643 if (!keyboardShown )
4645 ShowGrabHandleAndSetVisibility( false );
4647 // If the keyboard is not now being shown, then hide the popup panel
4648 mPopupPanel.Hide( true );
4652 // Removes highlight and resumes edit mode state
4653 void TextInput::RemoveHighlight( bool hidePopup )
4655 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4657 if ( mHighlightMeshActor )
4659 if ( mSelectionHandleOne )
4661 mActiveLayer.Remove( mSelectionHandleOne );
4662 mSelectionHandleOne.Reset();
4663 mSelectionHandleOneOffset.x = 0.0f;
4665 if ( mSelectionHandleTwo )
4667 mActiveLayer.Remove( mSelectionHandleTwo );
4668 mSelectionHandleTwo.Reset();
4669 mSelectionHandleTwoOffset.x = 0.0f;
4672 mNewHighlightInfo.mQuadList.clear();
4674 Self().Remove( mHighlightMeshActor );
4676 SetCursorVisibility( true );
4677 StartCursorBlinkTimer();
4679 mHighlightMeshActor.Reset();
4680 // NOTE: We cannot dereference mHighlightMesh, due
4681 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4689 mSelectionHandleOnePosition = 0;
4690 mSelectionHandleTwoPosition = 0;
4693 void TextInput::CreateHighlight()
4695 if ( !mHighlightMeshActor )
4697 mMeshData = MeshData( );
4698 mMeshData.SetHasNormals( true );
4700 mCustomMaterial = Material::New("CustomMaterial");
4701 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4703 mMeshData.SetMaterial( mCustomMaterial );
4705 mHighlightMesh = Mesh::New( mMeshData );
4707 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4708 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4709 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4710 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4711 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4712 mHighlightMeshActor.SetAffectedByLighting(false);
4714 Self().Add(mHighlightMeshActor);
4719 bool TextInput::CopySelectedTextToClipboard()
4721 mCurrentCopySelecton.clear();
4723 mCurrentCopySelecton = GetSelectedText();
4725 std::string stringToStore;
4727 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4728 * a marked up string.
4730 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4731 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4732 bool success = mClipboard.SetItem( stringToStore );
4736 void TextInput::PasteText( const Text& text )
4738 // Update Flag, indicates whether to update the text-input contents or not.
4739 // Any key stroke that results in a visual change of the text-input should
4740 // set this flag to true.
4741 bool update = false;
4742 if( mHighlightMeshActor )
4744 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4745 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4747 ImfManager imfManager = ImfManager::Get();
4750 imfManager.SetCursorPosition( mCursorPosition );
4751 imfManager.NotifyCursorPosition();
4753 DeleteHighlightedText( true );
4757 bool textExceedsMaximunNumberOfCharacters = false;
4758 bool textExceedsBoundary = false;
4760 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4762 mCursorPosition += insertedStringLength;
4763 ImfManager imfManager = ImfManager::Get();
4766 imfManager.SetCursorPosition ( mCursorPosition );
4767 imfManager.NotifyCursorPosition();
4770 update = update || ( insertedStringLength > 0 );
4777 if( insertedStringLength < text.GetLength() )
4779 EmitMaxInputCharactersReachedSignal();
4782 if( textExceedsBoundary )
4784 EmitInputTextExceedsBoundariesSignal();
4788 void TextInput::SetTextDirection()
4790 // Put the cursor to the right if we are empty and an RTL language is being used.
4791 if ( mStyledText.empty() )
4793 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4795 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4796 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4798 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4799 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4801 int alignment( mDisplayedTextView.GetTextAlignment() &
4802 ( Toolkit::Alignment::VerticalTop |
4803 Toolkit::Alignment::VerticalCenter |
4804 Toolkit::Alignment::VerticalBottom |
4805 Toolkit::Alignment::HorizontalCenter ) );
4806 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4808 // If our alignment is in the center, then do not change.
4809 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4811 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4814 // If our justification is in the center, then do not change.
4815 if ( justification != Toolkit::TextView::Center )
4817 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4820 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4821 mDisplayedTextView.SetLineJustification( justification );
4825 void TextInput::UpdateLineHeight()
4827 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4828 mLineHeight = font.GetLineHeight();
4830 // 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.
4832 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4834 if( !mExceedEnabled || shrink )
4836 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4840 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4842 std::size_t position = 0;
4844 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4850 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4852 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4854 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4860 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4861 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4863 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4869 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4874 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4881 void TextInput::SetSortModifier( float depthOffset )
4883 if(mDisplayedTextView)
4885 mDisplayedTextView.SetSortModifier(depthOffset);
4889 void TextInput::SetSnapshotModeEnabled( bool enable )
4891 if(mDisplayedTextView)
4893 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4897 bool TextInput::IsSnapshotModeEnabled() const
4899 bool snapshotEnabled = false;
4901 if(mDisplayedTextView)
4903 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4906 return snapshotEnabled;
4909 void TextInput::SetMarkupProcessingEnabled( bool enable )
4911 mMarkUpEnabled = enable;
4914 bool TextInput::IsMarkupProcessingEnabled() const
4916 return mMarkUpEnabled;
4919 void TextInput::SetScrollEnabled( bool enable )
4921 if( mDisplayedTextView )
4923 mDisplayedTextView.SetScrollEnabled( enable );
4928 // Don't set cursor's and handle's visibility to false if they are outside the
4929 // boundaries of the text-input.
4930 mIsCursorInScrollArea = true;
4931 mIsGrabHandleInScrollArea = true;
4932 if( mSelectionHandleOne && mSelectionHandleTwo )
4934 mSelectionHandleOne.SetVisible( true );
4935 mSelectionHandleTwo.SetVisible( true );
4937 if( mHighlightMeshActor )
4939 mHighlightMeshActor.SetVisible( true );
4945 bool TextInput::IsScrollEnabled() const
4947 bool scrollEnabled = false;
4949 if( mDisplayedTextView )
4951 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4954 return scrollEnabled;
4957 void TextInput::SetScrollPosition( const Vector2& position )
4959 if( mDisplayedTextView )
4961 mDisplayedTextView.SetScrollPosition( position );
4965 Vector2 TextInput::GetScrollPosition() const
4967 Vector2 scrollPosition;
4969 if( mDisplayedTextView )
4971 scrollPosition = mDisplayedTextView.GetScrollPosition();
4974 return scrollPosition;
4977 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4979 // determine number of characters that we can write to style text buffer, this is the insertStringLength
4980 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4981 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4983 // Add style to the new input text.
4984 MarkupProcessor::StyledTextArray textToInsert;
4985 for( std::size_t i = 0; i < insertedStringLength; ++i )
4987 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
4988 textToInsert.push_back( newStyledCharacter );
4991 //Insert text to the TextView.
4992 const bool emptyTextView = mStyledText.empty();
4993 if( emptyTextView && mPlaceHolderSet )
4995 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
4996 mDisplayedTextView.SetText( textToInsert );
5000 if( 0 == numberOfCharactersToReplace )
5002 mDisplayedTextView.InsertTextAt( position, textToInsert );
5006 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5009 mPlaceHolderSet = false;
5011 if( textToInsert.empty() )
5013 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5014 GetTextLayoutInfo();
5018 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5019 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5022 textExceedsBoundary = false;
5024 if( !mExceedEnabled )
5026 const Vector3& size = GetControlSize();
5028 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5030 // If new text does not fit within TextView
5031 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5032 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5033 GetTextLayoutInfo();
5034 textExceedsBoundary = true;
5035 insertedStringLength = 0;
5038 if( textExceedsBoundary )
5040 // Add the part of the text which fits on the text-input.
5042 // Split the text which doesn't fit in two halves.
5043 MarkupProcessor::StyledTextArray firstHalf;
5044 MarkupProcessor::StyledTextArray secondHalf;
5045 SplitText( textToInsert, firstHalf, secondHalf );
5047 // Clear text. This text will be filled with the text inserted.
5048 textToInsert.clear();
5050 // Where to insert the text.
5051 std::size_t positionToInsert = position;
5053 bool end = text.GetLength() <= 1;
5056 // Insert text and check ...
5057 const std::size_t textLength = firstHalf.size();
5058 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5059 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5061 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5063 // Inserted text doesn't fit.
5065 // Remove inserted text
5066 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5067 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5069 // The iteration finishes when only one character doesn't fit.
5070 end = textLength <= 1;
5074 // Prepare next two halves for next iteration.
5075 MarkupProcessor::StyledTextArray copyText = firstHalf;
5076 SplitText( copyText, firstHalf, secondHalf );
5083 // store text to be inserted in mStyledText.
5084 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5086 // Increase the inserted characters counter.
5087 insertedStringLength += textLength;
5089 // Prepare next two halves for next iteration.
5090 MarkupProcessor::StyledTextArray copyText = secondHalf;
5091 SplitText( copyText, firstHalf, secondHalf );
5093 // Update where next text has to be inserted
5094 positionToInsert += textLength;
5100 if( textToInsert.empty() && emptyTextView )
5102 // No character has been added and the text-view was empty.
5103 // Set the placeholder text.
5104 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5105 mPlaceHolderSet = true;
5109 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5110 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5111 mPlaceHolderSet = false;
5114 return insertedStringLength;
5117 void TextInput::GetTextLayoutInfo()
5119 if( mStyledText.empty() )
5121 // The text-input has no text, clear the text-view's layout info.
5122 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5126 if( mDisplayedTextView )
5128 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5132 // There is no text-view.
5133 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5138 void TextInput::SetOffsetFromText( const Vector4& offset )
5140 mPopupOffsetFromText = offset;
5143 const Vector4& TextInput::GetOffsetFromText() const
5145 return mPopupOffsetFromText;
5148 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5150 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5154 TextInput& textInputImpl( GetImpl( textInput ) );
5156 switch ( propertyIndex )
5158 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5160 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5163 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5165 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5168 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5170 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5173 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5175 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5178 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5180 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5183 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5185 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5188 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5190 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5193 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5195 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5198 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5200 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5203 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5205 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5208 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5210 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5213 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5215 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5218 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5220 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5223 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5225 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5228 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5230 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5233 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5235 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5241 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5243 Property::Value value;
5245 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5249 TextInput& textInputImpl( GetImpl( textInput ) );
5251 switch ( propertyIndex )
5253 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5255 value = textInputImpl.GetMaterialDiffuseColor();
5258 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5260 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5263 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5265 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5268 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5270 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5273 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5275 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5278 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5280 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5283 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5285 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5288 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5290 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5293 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5295 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5298 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5300 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5303 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5305 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5308 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5310 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5313 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5315 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5318 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5320 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5323 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5325 value = textInputImpl.GetOffsetFromText();
5328 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5330 value = textInputImpl.mCursor.GetCurrentColor();
5337 void TextInput::EmitStyleChangedSignal()
5339 // emit signal if input style changes.
5340 Toolkit::TextInput handle( GetOwner() );
5341 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5344 void TextInput::EmitTextModified()
5346 // emit signal when text changes.
5347 Toolkit::TextInput handle( GetOwner() );
5348 mTextModifiedSignal.Emit( handle );
5352 void TextInput::EmitMaxInputCharactersReachedSignal()
5354 // emit signal if max characters is reached during text input.
5355 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5357 Toolkit::TextInput handle( GetOwner() );
5358 mMaxInputCharactersReachedSignalV2.Emit( handle );
5361 void TextInput::EmitInputTextExceedsBoundariesSignal()
5363 // Emit a signal when the input text exceeds the boundaries of the text input.
5365 Toolkit::TextInput handle( GetOwner() );
5366 mInputTextExceedBoundariesSignalV2.Emit( handle );
5369 } // namespace Internal
5371 } // namespace Toolkit