2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
90 * Whether the given style is the default style or not.
91 * @param[in] style The given style.
92 * @return \e true if the given style is the default. Otherwise it returns \e false.
94 bool IsDefaultStyle( const TextStyle& style )
96 return DEFAULT_TEXT_STYLE == style;
100 * Whether the given styled text is using the default style or not.
101 * @param[in] textArray The given text.
102 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108 const TextStyle& style( (*it).mStyle );
110 if( !IsDefaultStyle( style ) )
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
125 if( ( *it ).mIsVisible )
127 return --cursorPosition;
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
138 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140 if( ( *it ).mIsVisible )
142 return cursorPosition;
148 return cursorPosition;
152 * Whether the given position plus the cursor size offset is inside the given boundary.
154 * @param[in] position The given position.
155 * @param[in] cursorSize The cursor size.
156 * @param[in] controlSize The given boundary.
158 * @return whether the given position is inside the given boundary.
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
169 * Splits a text in two halves.
171 * If the text's number of characters is odd, firstHalf has one more character.
173 * @param[in] text The text to be split.
174 * @param[out] firstHalf The first half of the text.
175 * @param[out] secondHalf The second half of the text.
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
184 const std::size_t textLength = text.size();
185 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
191 } // end of namespace
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
261 QuadCoordinates quad(x1, y1, x2, y2);
262 mQuadList.push_back( quad );
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
267 for(std::size_t i = 0;i < mQuadList.size(); i++)
269 QuadCoordinates& quad = mQuadList[i];
271 quad.min.Clamp(min, max);
272 quad.max.Clamp(min, max);
276 // [TextInput] ////////////////////////////////////////////////////////////////
278 Dali::Toolkit::TextInput TextInput::New()
280 // Create the implementation
281 TextInputPtr textInput(new TextInput());
282 // Pass ownership to CustomActor via derived handle
283 Dali::Toolkit::TextInput handle(*textInput);
284 handle.SetName( "TextInput");
286 textInput->Initialize();
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
296 mDisplayedTextView(),
297 mStyledPlaceHolderText(),
298 mMaxStringLength( DEFAULT_MAX_SIZE ),
299 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300 mCursorPosition( 0 ),
301 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302 mIsSelectionHandleOneFlipped( false ),
303 mIsSelectionHandleTwoFlipped( false ),
304 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308 mSelectionHandleOnePosition( 0 ),
309 mSelectionHandleTwoPosition( 0 ),
311 mPreEditStartPosition( 0 ),
312 mPreEditLength ( 0 ),
313 mNumberOfSurroundingCharactersDeleted( 0 ),
314 mTouchStartTime( 0 ),
316 mCurrentCopySelecton(),
319 mScrollDisplacement(),
320 mCurrentHandlePosition(),
321 mCurrentSelectionId(),
322 mCurrentSelectionHandlePosition(),
323 mRequestedSelection( 0, 0 ),
324 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
327 mMaterialColor( LIGHTBLUE ),
328 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329 mOverrideAutomaticAlignment( false ),
330 mCursorRTLEnabled( false ),
331 mClosestCursorPositionEOL ( false ),
332 mCursorBlinkStatus( true ),
333 mCursorVisibility( false ),
334 mGrabHandleVisibility( false ),
335 mIsCursorInScrollArea( true ),
336 mIsGrabHandleInScrollArea( true ),
337 mEditModeActive( false ),
338 mEditOnTouch( true ),
339 mTextSelection( true ),
340 mExceedEnabled( true ),
341 mGrabHandleEnabled( true ),
342 mIsSelectionHandleFlipEnabled( true ),
343 mPreEditFlag( false ),
344 mIgnoreCommitFlag( false ),
345 mIgnoreFirstCommitFlag( false ),
346 mSelectingText( false ),
347 mPreserveCursorPosition( false ),
348 mSelectTextOnCommit( false ),
349 mUnderlinedPriorToPreEdit ( false ),
350 mCommitByKeyInput( false ),
351 mPlaceHolderSet( false ),
352 mMarkUpEnabled( false )
354 // Updates the line height accordingly with the input style.
358 TextInput::~TextInput()
360 StopCursorBlinkTimer();
365 std::string TextInput::GetText() const
369 // Return text-view's text only if the text-input's text is not empty
370 // in order to not to return the placeholder text.
371 if( !mStyledText.empty() )
373 text = mDisplayedTextView.GetText();
379 std::string TextInput::GetMarkupText() const
381 std::string markupString;
382 MarkupProcessor::GetMarkupString( mStyledText, markupString );
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
389 // Get the placeholder styled text array from the markup string.
390 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
392 if( mStyledText.empty() )
394 // Set the placeholder text only if the styled text is empty.
395 mDisplayedTextView.SetText( mStyledPlaceHolderText );
396 mPlaceHolderSet = true;
400 std::string TextInput::GetPlaceholderText()
402 // Traverses the styled placeholder array getting only the text.
403 // Note that for some languages a 'character' could be represented by more than one 'char'
405 std::string placeholderText;
406 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
408 placeholderText.append( (*it).mText.GetText() );
411 return placeholderText ;
414 void TextInput::SetInitialText(const std::string& initialText)
416 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
418 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
420 mPreEditFlag = false;
421 mIgnoreCommitFlag = true;
424 SetText( initialText );
425 PreEditReset( false ); // Reset keyboard as text changed
428 void TextInput::SetText(const std::string& initialText)
430 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
432 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
434 if( mStyledText.empty() )
436 // If the initial text is empty, set the placeholder text.
437 mDisplayedTextView.SetText( mStyledPlaceHolderText );
438 mPlaceHolderSet = true;
442 mDisplayedTextView.SetText( mStyledText );
443 mPlaceHolderSet = false;
448 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
450 ImfManager imfManager = ImfManager::Get();
453 imfManager.SetCursorPosition( mCursorPosition );
454 imfManager.SetSurroundingText( initialText );
455 imfManager.NotifyCursorPosition();
458 if( IsScrollEnabled() )
460 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
463 ShowGrabHandleAndSetVisibility( false );
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
474 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
476 mDisplayedTextView.SetText( styleText );
477 mPlaceHolderSet = false;
479 // If text alignment hasn't been manually set by application developer, then we
480 // automatically determine the alignment based on the content of the text i.e. what
481 // language the text begins with.
482 // TODO: This should determine different alignments for each line (broken by '\n') of text.
483 if(!mOverrideAutomaticAlignment)
485 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486 bool leftToRight(true);
488 if( !styleText.empty() )
490 bool breakOut(false);
492 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
494 const Text& text = textIter->mText;
496 for( std::size_t i = 0; i < text.GetLength(); ++i )
498 Character character( text[i] );
499 if( character.GetCharacterDirection() != Character::Neutral )
501 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
509 // Based on this direction, either left or right align text if not manually set by application developer.
510 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512 Toolkit::Alignment::VerticalTop ) );
513 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
521 mMaxStringLength = maxChars;
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
526 DALI_ASSERT_DEBUG( maxLines > 0 )
530 mNumberOflinesLimit = maxLines;
534 std::size_t TextInput::GetNumberOfLinesLimit() const
536 return mNumberOflinesLimit;
539 std::size_t TextInput::GetNumberOfCharacters() const
541 return mStyledText.size();
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
547 mMaterialColor = color;
548 if ( mCustomMaterial )
550 mCustomMaterial.SetDiffuseColor( mMaterialColor );
551 mMeshData.SetMaterial( mCustomMaterial );
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
557 return mMaterialColor;
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
564 return mInputStartedSignalV2;
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
569 return mInputFinishedSignalV2;
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
574 return mCutAndPasteToolBarDisplayedV2;
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
579 return mStyleChangedSignalV2;
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
584 return mTextModifiedSignal;
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
589 return mMaxInputCharactersReachedSignalV2;
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
594 return mInputTextExceedBoundariesSignalV2;
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
599 Dali::BaseHandle handle( object );
601 bool connected( true );
602 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
604 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
606 textInput.InputStartedSignal().Connect( tracker, functor );
608 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
610 textInput.InputFinishedSignal().Connect( tracker, functor );
612 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
614 textInput.StyleChangedSignal().Connect( tracker, functor );
616 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
618 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
620 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
622 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
626 // signalName does not match any signal
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
637 // update line height before calculate the actual position.
642 if( setCursorOnTouchPoint )
644 // Sets the cursor position for the given touch point.
645 ReturnClosestIndex( touchPoint, mCursorPosition );
647 // Creates the grab handle.
648 if( IsGrabHandleEnabled() )
650 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
654 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657 ShowGrabHandleAndSetVisibility( true );
659 // Scrolls the text-view if needed.
660 if( IsScrollEnabled() )
662 ScrollTextViewToMakeCursorVisible( cursorPosition );
668 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
680 bool TextInput::IsEditable() const
682 return mEditModeActive;
685 void TextInput::SetEditOnTouch( bool editOnTouch )
687 mEditOnTouch = editOnTouch;
690 bool TextInput::IsEditOnTouch() const
695 void TextInput::SetTextSelectable( bool textSelectable )
697 mTextSelection = textSelectable;
700 bool TextInput::IsTextSelectable() const
702 return mTextSelection;
705 bool TextInput::IsTextSelected() const
707 return mHighlightMeshActor;
710 void TextInput::DeSelectText()
717 void TextInput::SetGrabHandleImage(Dali::Image image )
721 CreateGrabHandle(image);
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
727 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
731 mCursor.SetImage( image );
732 mCursor.SetNinePatchBorder( border );
736 Vector3 TextInput::GetSelectionHandleSize()
738 return DEFAULT_SELECTION_HANDLE_SIZE;
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
743 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
747 mCursorRTL.SetImage( image);
748 mCursorRTL.SetNinePatchBorder( border );
752 void TextInput::EnableGrabHandle(bool toggle)
754 // enables grab handle with will in turn de-activate magnifier
755 mGrabHandleEnabled = toggle;
758 bool TextInput::IsGrabHandleEnabled()
760 // if false then magnifier will be shown instead.
761 return mGrabHandleEnabled;
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
766 // Deprecated function. To be removed.
767 mIsSelectionHandleFlipEnabled = toggle;
770 bool TextInput::IsSelectionHandleFlipEnabled()
772 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
778 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
782 mSelectionHandleFlipMargin = margin;
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
787 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
793 const Vector4 boundary( originX,
795 originX + boundingRectangle.width,
796 originY + boundingRectangle.height );
798 mBoundingRectangleWorldCoordinates = boundary;
801 const Rect<float> TextInput::GetBoundingRectangle() const
803 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
805 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
808 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
815 return mSelectionHandleFlipMargin;
818 void TextInput::SetTextColor( const Vector4& color )
820 mDisplayedTextView.SetColor( color );
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
825 if( style != mInputStyle )
828 bool emitSignal = false;
830 // mask: modify style according to mask, if different emit signal.
831 const TextStyle oldInputStyle( mInputStyle );
833 // Copy the new style.
834 mInputStyle.Copy( style, mask );
836 // if style has changed, emit signal.
837 if( oldInputStyle != mInputStyle )
842 // Updates the line height accordingly with the input style.
845 // Changing font point size will require the cursor to be re-sized
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
857 if ( IsTextSelected() )
859 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
862 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
864 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
867 // Keeps the old style to be compared with the new one.
868 const TextStyle oldInputStyle( mInputStyle );
870 // Copy only those parameters from the style which are set in the mask.
871 mInputStyle.Copy( style, mask );
873 if( mInputStyle != oldInputStyle )
875 // Updates the line height accordingly with the input style.
878 EmitStyleChangedSignal();
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
885 if( !mStyledText.empty() )
887 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
891 TextStyle TextInput::GetStyleAtCursor() const
895 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
897 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898 style = mStyledText.at( mCursorPosition-1 ).mStyle;
904 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
906 Dali::Font defaultFont = Dali::Font::New();
907 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
916 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
918 if( position >= mStyledText.size() )
920 position = mStyledText.size() - 1;
923 return mStyledText.at( position ).mStyle;
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
928 mDisplayedTextView.SetTextAlignment( align );
929 mOverrideAutomaticAlignment = true;
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
934 mDisplayedTextView.SetLineJustification( justification );
935 mOverrideAutomaticAlignment = true;
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
940 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
945 return mDisplayedTextView.GetFadeBoundary();
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
950 return mDisplayedTextView.GetTextAlignment();
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
955 mDisplayedTextView.SetMultilinePolicy( policy );
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
960 return mDisplayedTextView.GetMultilinePolicy();
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
965 mDisplayedTextView.SetWidthExceedPolicy( policy );
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
970 return mDisplayedTextView.GetWidthExceedPolicy();
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
975 mDisplayedTextView.SetHeightExceedPolicy( policy );
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
980 return mDisplayedTextView.GetHeightExceedPolicy();
983 void TextInput::SetExceedEnabled( bool enable )
985 mExceedEnabled = enable;
988 bool TextInput::GetExceedEnabled() const
990 return mExceedEnabled;
993 void TextInput::SetBackground(Dali::Image image )
995 // TODO Should add this function and add public api to match.
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1005 switch( event.state )
1007 case KeyEvent::Down:
1009 return OnKeyDownEvent(event);
1015 return OnKeyUpEvent(event);
1027 void TextInput::OnKeyInputFocusGained()
1029 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1031 mEditModeActive = true;
1033 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1035 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1037 // Updates the line height accordingly with the input style.
1040 // Connect the signals to use in text input.
1041 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1044 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1047 GetTextLayoutInfo();
1050 SetCursorVisibility( true );
1051 StartCursorBlinkTimer();
1053 Toolkit::TextInput handle( GetOwner() );
1054 mInputStartedSignalV2.Emit( handle );
1056 ImfManager imfManager = ImfManager::Get();
1060 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1062 // Notify that the text editing start.
1063 imfManager.Activate();
1065 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066 imfManager.SetRestoreAferFocusLost( true );
1068 imfManager.SetCursorPosition( mCursorPosition );
1069 imfManager.NotifyCursorPosition();
1072 mClipboard = Clipboard::Get(); // Store handle to clipboard
1074 // Now in edit mode we can accept string to paste from clipboard
1075 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1078 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1082 void TextInput::OnKeyInputFocusLost()
1084 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1088 // If key input focus is lost, it removes the
1089 // underline from the last pre-edit text.
1090 RemovePreEditStyle();
1091 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1092 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1096 ImfManager imfManager = ImfManager::Get();
1099 // The text editing is finished. Therefore the imf manager don't have restore activation.
1100 imfManager.SetRestoreAferFocusLost( false );
1102 // Notify that the text editing finish.
1103 imfManager.Deactivate();
1105 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1107 // Disconnect signal used the text input.
1108 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1110 Toolkit::TextInput handle( GetOwner() );
1111 mInputFinishedSignalV2.Emit( handle );
1112 mEditModeActive = false;
1113 mPreEditFlag = false;
1115 SetCursorVisibility( false );
1116 StopCursorBlinkTimer();
1118 ShowGrabHandleAndSetVisibility( false );
1121 // No longer in edit mode so do not want to receive string from clipboard
1122 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1125 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1128 Clipboard clipboard = Clipboard::Get();
1131 clipboard.HideClipboard();
1135 void TextInput::OnControlStageConnection()
1137 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1139 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1141 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1145 void TextInput::CreateActiveLayer()
1147 Actor self = Self();
1148 mActiveLayer = Layer::New();
1149 mActiveLayer.SetName ( "ActiveLayerActor" );
1151 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1152 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1153 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1155 self.Add( mActiveLayer );
1156 mActiveLayer.RaiseToTop();
1159 void TextInput::OnInitialize()
1161 CreateTextViewActor();
1165 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1166 // different positions depending on language)
1167 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1168 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1170 Actor self = Self();
1171 self.Add( mCursor );
1172 self.Add( mCursorRTL );
1174 mCursorVisibility = false;
1176 CreateActiveLayer(); // todo move this so layer only created when needed.
1178 // Assign names to image actors
1179 mCursor.SetName("mainCursor");
1180 mCursorRTL.SetName("rtlCursor");
1183 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1185 mDisplayedTextView.SetSize( targetSize );
1186 GetTextLayoutInfo();
1187 mActiveLayer.SetSize(targetSize);
1190 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1192 Relayout( mDisplayedTextView, size, container );
1193 Relayout( mPopupPanel.GetRootActor(), size, container );
1195 GetTextLayoutInfo();
1200 Vector3 TextInput::GetNaturalSize()
1202 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1204 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1206 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1207 naturalSize.height = mLineHeight;
1213 float TextInput::GetHeightForWidth( float width )
1215 float height = mDisplayedTextView.GetHeightForWidth( width );
1217 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1219 // If the height is zero, it means there is no text. Let's return the cursor height.
1220 height = mLineHeight;
1226 /*end of Virtual methods from parent*/
1228 // Private Internal methods
1230 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1232 switch (gesture.state)
1234 case Gesture::Started:
1235 // fall through so code not duplicated
1236 case Gesture::Continuing:
1238 if (actor == mGrabArea)
1240 SetCursorVisibility( true );
1241 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1242 MoveGrabHandle( gesture.displacement );
1243 HidePopup(); // Do not show popup whilst handle is moving
1245 else if (actor == mHandleOneGrabArea)
1247 // the displacement in PanGesture is affected by the actor's rotation.
1248 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1249 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1251 MoveSelectionHandle( HandleOne, gesture.displacement );
1253 mState = StateDraggingHandle;
1256 else if (actor == mHandleTwoGrabArea)
1258 // the displacement in PanGesture is affected by the actor's rotation.
1259 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1260 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1262 MoveSelectionHandle( HandleTwo, gesture.displacement );
1264 mState = StateDraggingHandle;
1270 case Gesture::Finished:
1272 // Revert back to non-pressed selection handle images
1273 if (actor == mGrabArea)
1275 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1276 SetCursorVisibility( true );
1277 SetUpPopupSelection();
1280 if (actor == mHandleOneGrabArea)
1282 // the displacement in PanGesture is affected by the actor's rotation.
1283 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1284 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1286 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1288 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1290 ShowPopupCutCopyPaste();
1292 if (actor == mHandleTwoGrabArea)
1294 // the displacement in PanGesture is affected by the actor's rotation.
1295 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1296 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1298 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1300 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1302 ShowPopupCutCopyPaste();
1311 // Stop the flashing animation so easy to see when moved.
1312 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1314 if (touch.GetPoint(0).state == TouchPoint::Down)
1316 SetCursorVisibility( true );
1317 StopCursorBlinkTimer();
1319 else if (touch.GetPoint(0).state == TouchPoint::Up)
1321 SetCursorVisibility( true );
1322 StartCursorBlinkTimer();
1327 // selection handle one
1328 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1330 if (touch.GetPoint(0).state == TouchPoint::Down)
1332 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1334 else if (touch.GetPoint(0).state == TouchPoint::Up)
1336 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1341 // selection handle two
1342 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1344 if (touch.GetPoint(0).state == TouchPoint::Down)
1346 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1348 else if (touch.GetPoint(0).state == TouchPoint::Up)
1350 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1355 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1357 // If text exists then select nearest word.
1358 if ( !mStyledText.empty())
1362 ShowGrabHandleAndSetVisibility( false );
1367 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1368 // converts the pre-edit word being displayed to a committed word.
1369 if ( !mUnderlinedPriorToPreEdit )
1372 style.SetUnderline( false );
1373 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1375 mPreEditFlag = false;
1376 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1377 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1378 PreEditReset( false );
1380 mCursorPosition = 0;
1382 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1383 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1385 std::size_t start = 0;
1386 std::size_t end = 0;
1387 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1389 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1391 ImfManager imfManager = ImfManager::Get();
1394 imfManager.SetCursorPosition ( mCursorPosition );
1395 imfManager.NotifyCursorPosition();
1398 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1400 SelectText( start, end );
1401 ShowPopupCutCopyPaste();
1405 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1406 HidePopup( false ); // Hide popup with setting to do auto show.
1407 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1411 else if ( mClipboard && mClipboard.NumberOfItems() )
1413 ShowPopupCutCopyPaste();
1416 // If no text and clipboard empty then do nothing
1419 // TODO: Change the function name to be more general.
1420 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1422 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1423 , (mEditOnTouch)?"true":"false"
1424 , (mEditModeActive)?"true":"false");
1426 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1431 if( mGrabArea == actor )
1433 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1435 SetUpPopupSelection();
1445 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1447 // Initially don't create the grab handle.
1448 bool createGrabHandle = false;
1450 if ( !mEditModeActive )
1452 // update line height before calculate the actual position.
1455 // Only start edit mode if TextInput configured to edit on touch
1458 // Set the initial cursor position in the tap point.
1459 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1465 // Show the keyboard if it was hidden.
1466 if (!VirtualKeyboard::IsVisible())
1468 VirtualKeyboard::Show();
1471 // Reset keyboard as tap event has occurred.
1472 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1473 PreEditReset( true );
1475 GetTextLayoutInfo();
1477 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1479 // 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.
1481 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1483 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1485 // Notify keyboard so it can 're-capture' word for predictive text.
1486 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1487 ImfManager imfManager = ImfManager::Get();
1490 imfManager.SetCursorPosition ( mCursorPosition );
1491 imfManager.NotifyCursorPosition();
1493 const TextStyle oldInputStyle( mInputStyle );
1495 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1499 // Create the grab handle.
1500 // Grab handle is created later.
1501 createGrabHandle = true;
1503 if( oldInputStyle != mInputStyle )
1505 // Updates the line height accordingly with the input style.
1508 EmitStyleChangedSignal();
1513 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1514 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1515 // otherwise the Grab handle will be shown when selecting.
1516 if ( createGrabHandle && IsGrabHandleEnabled() )
1518 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1522 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1523 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1524 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1525 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1530 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1532 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1534 // Ignore longpress if in selection mode already
1535 if( mHighlightMeshActor )
1540 if(longPress.state == Dali::Gesture::Started)
1542 // Start edit mode on long press
1543 if ( !mEditModeActive )
1548 // If text exists then select nearest word.
1549 if ( !mStyledText.empty())
1553 ShowGrabHandleAndSetVisibility( false );
1558 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1559 // converts the pre-edit word being displayed to a committed word.
1560 if ( !mUnderlinedPriorToPreEdit )
1563 style.SetUnderline( false );
1564 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1566 mPreEditFlag = false;
1567 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1568 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1569 PreEditReset( false );
1571 mCursorPosition = 0;
1573 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1574 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1576 std::size_t start = 0;
1577 std::size_t end = 0;
1578 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1580 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1582 ImfManager imfManager = ImfManager::Get();
1585 imfManager.SetCursorPosition ( mCursorPosition );
1586 imfManager.NotifyCursorPosition();
1589 SelectText( start, end );
1592 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1593 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1595 ShowPopupCutCopyPaste();
1600 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1602 const Text clipboardText( notifier.GetContent() );
1603 PasteText( clipboardText );
1605 SetCursorVisibility( true );
1606 StartCursorBlinkTimer();
1608 ShowGrabHandleAndSetVisibility( false );
1614 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1616 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1618 const std::string& name = button.GetName();
1620 if(name == TextInputPopup::OPTION_SELECT_WORD)
1622 std::size_t start = 0;
1623 std::size_t end = 0;
1624 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1626 SelectText( start, end );
1628 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1630 SetCursorVisibility(false);
1631 StopCursorBlinkTimer();
1633 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1634 std::size_t start = 0;
1636 SelectText( start, end );
1638 else if(name == TextInputPopup::OPTION_CUT)
1640 bool ret = CopySelectedTextToClipboard();
1644 DeleteHighlightedText( true );
1648 SetCursorVisibility( true );
1649 StartCursorBlinkTimer();
1653 else if(name == TextInputPopup::OPTION_COPY)
1655 CopySelectedTextToClipboard();
1659 SetCursorVisibility( true );
1660 StartCursorBlinkTimer();
1664 else if(name == TextInputPopup::OPTION_PASTE)
1666 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1668 PasteText(retrievedString);
1670 SetCursorVisibility( true );
1671 StartCursorBlinkTimer();
1673 ShowGrabHandleAndSetVisibility( false );
1677 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1679 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1680 // Hence pass the false parameter for signalFinished.
1681 HidePopup( true, false );
1682 mClipboard.ShowClipboard();
1688 bool TextInput::OnCursorBlinkTimerTick()
1691 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1692 if ( mCursorRTLEnabled )
1694 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1696 mCursorBlinkStatus = !mCursorBlinkStatus;
1701 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1703 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1705 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1706 if(mHighlightMeshActor && mState == StateEdit)
1708 ShowPopupCutCopyPaste();
1712 //FIXME this routine needs to be re-written as it contains too many branches.
1713 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1715 std::string keyName = event.keyPressedName;
1716 std::string keyString = event.keyPressed;
1718 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1720 // Do not consume "Tab" and "Escape" keys.
1721 if(keyName == "Tab" || keyName == "Escape")
1723 // Escape key to end the edit mode
1729 HidePopup(); // If Pop-up shown then hides it as editing text.
1731 // Update Flag, indicates whether to update the text-input contents or not.
1732 // Any key stroke that results in a visual change of the text-input should
1733 // set this flag to true.
1736 // Whether to scroll text to cursor position.
1737 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1738 bool scroll = false;
1740 if (keyName == "Return")
1742 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1744 bool preEditFlagPreviouslySet( mPreEditFlag );
1746 // replaces highlighted text with new line
1747 DeleteHighlightedText( false );
1749 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1751 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1752 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1755 mCommitByKeyInput = true;
1758 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1759 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1761 mPreEditFlag = true;
1762 mIgnoreCommitFlag = false;
1772 else if ( keyName == "space" )
1774 if ( mHighlightMeshActor )
1776 // Some text is selected so erase it before adding space.
1777 DeleteHighlightedText( true );
1781 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1783 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1784 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1787 mCommitByKeyInput = true;
1792 else if (keyName == "BackSpace")
1794 if ( mHighlightMeshActor )
1796 // Some text is selected so erase it
1797 DeleteHighlightedText( true );
1802 if ( mCursorPosition > 0 )
1804 DeleteCharacter( mCursorPosition );
1810 else if (keyName == "Right")
1815 else if (keyName == "Left")
1817 AdvanceCursor(true);
1820 else // event is a character
1822 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1823 if ( !keyString.empty() )
1825 // replaces highlighted text with new character
1826 DeleteHighlightedText( false );
1828 // Received key String
1829 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1835 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1836 // as this is a costly operation.
1842 if(update || scroll)
1844 if( IsScrollEnabled() )
1846 // Calculates the new cursor position (in actor coordinates)
1847 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1849 ScrollTextViewToMakeCursorVisible( cursorPosition );
1856 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1858 std::string keyName = event.keyPressedName;
1859 std::string keyString = event.keyPressed;
1861 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1863 // The selected text become deselected when the key code is DALI_KEY_BACK.
1864 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1873 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1875 // Updates the stored scroll position.
1876 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1878 const Vector3& controlSize = GetControlSize();
1879 Size cursorSize( CURSOR_THICKNESS, 0.f );
1881 // Updates the cursor and grab handle position and visibility.
1882 if( mGrabHandle || mCursor )
1884 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1885 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1887 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1889 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1893 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1894 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1899 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1900 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1904 // Updates the selection handles and highlighted text position and visibility.
1905 if( mSelectionHandleOne && mSelectionHandleTwo )
1907 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1908 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1909 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1910 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1911 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1912 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1914 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1915 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1917 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1918 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1919 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1920 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1922 if( mHighlightMeshActor )
1924 mHighlightMeshActor.SetVisible( true );
1930 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1932 // Scroll the text to make the cursor visible.
1933 const Size cursorSize( CURSOR_THICKNESS,
1934 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1936 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1938 const Vector3& controlSize = GetControlSize();
1940 // Calculates the new scroll position.
1941 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1942 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1944 scrollOffset.x += cursorPosition.x;
1947 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1949 scrollOffset.y += cursorPosition.y;
1952 // Sets the new scroll position.
1953 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1954 SetScrollPosition( scrollOffset );
1957 void TextInput::StartScrollTimer()
1961 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1962 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1965 if( !mScrollTimer.IsRunning() )
1967 mScrollTimer.Start();
1971 void TextInput::StopScrollTimer()
1975 mScrollTimer.Stop();
1979 bool TextInput::OnScrollTimerTick()
1981 // TODO: need to set the new style accordingly the new handle position.
1983 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1985 // nothing to do if all handles are invisible or doesn't exist.
1991 // Choose between the grab handle or the selection handles.
1992 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1993 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1994 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1996 std::size_t newCursorPosition = 0;
1997 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1999 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2000 // the new selection handle's position needs to be different of the other one.
2001 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2002 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2003 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2005 if( differentSelectionHandles )
2007 handlePosition = newCursorPosition;
2009 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2011 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2013 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2014 scrollPosition += scrollDelta;
2015 SetScrollPosition( scrollPosition );
2017 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2022 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2025 actualHandlePosition.x += mScrollDisplacement.x;
2026 actualHandlePosition.y += mScrollDisplacement.y;
2031 // Public Internal Methods (public for testing purpose)
2033 void TextInput::SetUpTouchEvents()
2035 if ( !mTapDetector )
2037 mTapDetector = TapGestureDetector::New();
2038 // Attach the actors and connect the signal
2039 mTapDetector.Attach(Self());
2041 // As contains children which may register for tap the default control detector is not used.
2042 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2045 if ( !mDoubleTapDetector )
2047 mDoubleTapDetector = TapGestureDetector::New();
2048 mDoubleTapDetector.SetTapsRequired( 2 );
2049 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2051 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2052 // so that we do not, unnecessarily, have a double tap request all the time
2055 if ( !mPanGestureDetector )
2057 mPanGestureDetector = PanGestureDetector::New();
2058 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2061 if ( !mLongPressDetector )
2063 mLongPressDetector = LongPressGestureDetector::New();
2064 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2065 mLongPressDetector.Attach(Self());
2069 void TextInput::CreateTextViewActor()
2071 mDisplayedTextView = Toolkit::TextView::New();
2072 mDisplayedTextView.SetName( "DisplayedTextView ");
2073 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2074 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2075 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2076 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2077 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2078 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2079 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2080 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2081 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2082 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2084 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2086 Self().Add( mDisplayedTextView );
2089 // Start a timer to initiate, used by the cursor to blink.
2090 void TextInput::StartCursorBlinkTimer()
2092 if ( !mCursorBlinkTimer )
2094 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2095 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2098 if ( !mCursorBlinkTimer.IsRunning() )
2100 mCursorBlinkTimer.Start();
2104 // Start a timer to initiate, used by the cursor to blink.
2105 void TextInput::StopCursorBlinkTimer()
2107 if ( mCursorBlinkTimer )
2109 mCursorBlinkTimer.Stop();
2113 void TextInput::StartEditMode()
2115 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2117 if(!mEditModeActive)
2122 if ( mDoubleTapDetector )
2124 mDoubleTapDetector.Attach( Self() );
2128 void TextInput::EndEditMode()
2130 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2132 ClearKeyInputFocus();
2134 if ( mDoubleTapDetector )
2136 mDoubleTapDetector.Detach( Self() );
2140 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2142 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2144 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2146 style.SetUnderline( true );
2147 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2151 void TextInput::RemovePreEditStyle()
2153 if ( !mUnderlinedPriorToPreEdit )
2156 style.SetUnderline( false );
2157 SetActiveStyle( style, TextStyle::UNDERLINE );
2161 // IMF related methods
2164 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2166 bool update( false );
2167 bool preeditResetRequired ( false );
2169 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2171 HidePopup(); // If Pop-up shown then hides it as editing text.
2174 switch ( imfEvent.eventName )
2176 case ImfManager::PREEDIT:
2178 mIgnoreFirstCommitFlag = false;
2180 // 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
2181 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2183 // replaces highlighted text with new character
2184 DeleteHighlightedText( false );
2187 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2189 if( IsScrollEnabled() )
2191 // Calculates the new cursor position (in actor coordinates)
2192 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2193 ScrollTextViewToMakeCursorVisible( cursorPosition );
2200 case ImfManager::COMMIT:
2202 if( mIgnoreFirstCommitFlag )
2204 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2205 mIgnoreFirstCommitFlag = false;
2209 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2211 // 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
2212 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2214 // replaces highlighted text with new character
2215 DeleteHighlightedText( false );
2218 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2219 // not needed, one such scenario is when the pre-edit word is too long to fit.
2220 if ( !mIgnoreCommitFlag )
2222 update = CommitReceived( imfEvent.predictiveString );
2226 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2232 if( IsScrollEnabled() )
2234 // Calculates the new cursor position (in actor coordinates)
2235 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2237 ScrollTextViewToMakeCursorVisible( cursorPosition );
2242 case ImfManager::DELETESURROUNDING:
2244 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2245 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2247 mPreEditFlag = false;
2249 std::size_t toDelete = 0;
2250 std::size_t numberOfCharacters = 0;
2252 if( mHighlightMeshActor )
2254 // delete highlighted text.
2255 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2256 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2260 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2262 toDelete = mCursorPosition + imfEvent.cursorOffset;
2264 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2266 numberOfCharacters = mStyledText.size() - toDelete;
2270 numberOfCharacters = imfEvent.numberOfChars;
2273 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2274 DeleteRange( toDelete, numberOfCharacters );
2276 mCursorPosition = toDelete;
2277 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2281 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2284 case ImfManager::GETSURROUNDING:
2286 // 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
2287 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2288 if (! ( mHighlightMeshActor || mSelectingText ) )
2290 std::string text( GetText() );
2291 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2293 imfManager.SetCursorPosition( mCursorPosition );
2294 imfManager.SetSurroundingText( text );
2297 if( 0 != mNumberOfSurroundingCharactersDeleted )
2299 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2300 mNumberOfSurroundingCharactersDeleted = 0;
2302 if( mStyledText.empty() )
2304 // Styled text is empty, so set the placeholder text.
2305 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2306 mPlaceHolderSet = true;
2311 case ImfManager::VOID:
2313 DALI_ASSERT_DEBUG( false );
2317 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2319 return callbackData;
2322 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2324 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2326 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2327 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2329 bool preeditResetRequest ( false );
2331 if( mPreEditFlag ) // Already in pre-edit state.
2333 if( mStyledText.size() >= mMaxStringLength )
2335 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2336 // Cannot fit these characters into field, clear pre-edit.
2337 if ( !mUnderlinedPriorToPreEdit )
2340 style.SetUnderline( false );
2341 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2343 mIgnoreCommitFlag = true;
2344 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2345 mPreEditFlag = false;
2346 EmitMaxInputCharactersReachedSignal();
2350 // delete existing pre-edit string
2351 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2353 // Store new pre-edit string
2354 mPreEditString.SetText( keyString );
2356 if ( keyString.empty() )
2358 mPreEditFlag = false;
2359 mCursorPosition = mPreEditStartPosition;
2361 if( mStyledText.empty() )
2363 // Styled text is empty, so set the placeholder text.
2364 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2365 mPlaceHolderSet = true;
2369 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2371 GetTextLayoutInfo();
2376 // Insert new pre-edit string. InsertAt updates the size and position table.
2377 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2378 // 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.
2379 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2380 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2381 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2384 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2388 else // mPreEditFlag not set
2390 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2392 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2393 // new pre-edit so move into pre-edit state by setting flag
2394 mPreEditFlag = true;
2395 mPreEditString.SetText( keyString ); // store new pre-edit string
2396 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2397 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2398 // 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.
2399 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2400 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2401 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2402 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2408 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2412 return preeditResetRequest;
2415 bool TextInput::CommitReceived(const std::string& keyString )
2417 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2418 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2420 bool update( false );
2422 RemovePreEditStyle();
2424 const std::size_t styledTextSize( mStyledText.size() );
2425 if( styledTextSize >= mMaxStringLength )
2427 // Cannot fit these characters into field, clear pre-edit.
2430 mIgnoreCommitFlag = true;
2431 mPreEditFlag = false;
2433 EmitMaxInputCharactersReachedSignal();
2439 // delete existing pre-edit string
2440 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2441 mPreEditFlag = false;
2443 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2444 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2446 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2448 // No need to update cursor position as Cursor location given by touch.
2449 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2450 mPreserveCursorPosition = false;
2454 // Cursor not set by touch so needs to be re-positioned to input more text
2455 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2457 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2458 if ( mCommitByKeyInput )
2460 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2461 mCommitByKeyInput = false;
2467 if ( mSelectTextOnCommit )
2469 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2474 else // mPreEditFlag not set
2476 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2478 if( mStyledText.empty() && mPlaceHolderSet )
2480 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2481 mDisplayedTextView.SetText( "" );
2482 mNumberOfSurroundingCharactersDeleted = 0;
2483 mPlaceHolderSet = false;
2485 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2487 mNumberOfSurroundingCharactersDeleted = 0;
2492 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2497 mSelectTextOnCommit = false;
2499 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2500 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2505 // End of IMF related methods
2507 std::size_t TextInput::DeletePreEdit()
2509 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2511 DALI_ASSERT_DEBUG( mPreEditFlag );
2513 const std::size_t preEditStringLength = mPreEditString.GetLength();
2514 const std::size_t styledTextSize = mStyledText.size();
2516 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2518 // Prevents erase items outside mStyledText bounds.
2519 if( mPreEditStartPosition > styledTextSize )
2521 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2522 mPreEditStartPosition = styledTextSize;
2525 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2527 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2528 endPosition = styledTextSize;
2531 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2533 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2534 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2536 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2538 return preEditStringLength;
2541 void TextInput::PreEditReset( bool preserveCursorPosition )
2543 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2544 preserveCursorPosition, mCursorPosition);
2546 // 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.
2547 mPreserveCursorPosition = preserveCursorPosition;
2549 // Reset incase we are in a pre-edit state.
2550 ImfManager imfManager = ImfManager::Get();
2553 imfManager.Reset(); // Will trigger a commit message
2557 void TextInput::CursorUpdate()
2561 ImfManager imfManager = ImfManager::Get();
2564 std::string text( GetText() );
2565 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2566 imfManager.SetCursorPosition ( mCursorPosition );
2567 imfManager.NotifyCursorPosition();
2571 /* Delete highlighted characters redisplay*/
2572 void TextInput::DeleteHighlightedText( bool inheritStyle )
2574 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2576 if( mHighlightMeshActor )
2578 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2580 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2581 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2583 // Get the styled text of the characters to be deleted as it may be needed if
2584 // the "exceed the text-input's boundaries" option is disabled.
2585 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2587 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2589 mStyledText.erase( start, end ); // erase range of characters
2591 // Remove text from TextView.
2593 if( mStyledText.empty() )
2595 // Styled text is empty, so set the placeholder text.
2596 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2597 mPlaceHolderSet = true;
2601 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2603 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2605 // It may happen than after removing a white space or a new line character,
2606 // two words merge, this new word could be big enough to not fit in its
2607 // current line, so moved to the next one, and make some part of the text to
2608 // exceed the text-input's boundary.
2609 if( !mExceedEnabled )
2611 // Get the new text layout after removing some characters.
2612 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2614 // Get text-input's size.
2615 const Vector3& size = GetControlSize();
2617 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2618 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2620 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2622 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2623 styledCharactersToDelete.begin(),
2624 styledCharactersToDelete.end() );
2628 GetTextLayoutInfo();
2636 const TextStyle oldInputStyle( mInputStyle );
2638 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2640 if( oldInputStyle != mInputStyle )
2642 // Updates the line height accordingly with the input style.
2645 EmitStyleChangedSignal();
2651 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2653 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2654 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2656 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2659 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2661 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2662 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2664 mStyledText.erase(itStart, itEnd);
2666 // update the selection handles if they are visible.
2667 if( mHighlightMeshActor )
2669 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2670 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2672 if( minHandle >= start + ncharacters )
2674 minHandle -= ncharacters;
2676 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2681 if( maxHandle >= start + ncharacters )
2683 maxHandle -= ncharacters;
2685 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2691 // 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.
2694 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2696 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2697 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2698 // Mean we do not re-draw the text more than we have too.
2701 /* Delete character at current cursor position and redisplay*/
2702 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2704 // Ensure positionToDelete is not out of bounds.
2705 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2706 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2707 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2709 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2712 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2714 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2716 // Get the styled text of the character to be deleted as it may be needed if
2717 // the "exceed the text-input's boundaries" option is disabled.
2718 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2720 mStyledText.erase(it); // erase the character left of positionToDelete
2722 if( mStyledText.empty() )
2724 // Styled text is empty, so set the placeholder text.
2725 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2726 mPlaceHolderSet = true;
2730 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2732 const Character characterToDelete = styledCharacterToDelete.mText[0];
2734 // It may happen than after removing a white space or a new line character,
2735 // two words merge, this new word could be big enough to not fit in its
2736 // current line, so moved to the next one, and make some part of the text to
2737 // exceed the text-input's boundary.
2738 if( !mExceedEnabled )
2740 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2742 // Get the new text layout after removing one character.
2743 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2745 // Get text-input's size.
2746 const Vector3& size = GetControlSize();
2748 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2749 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2751 MarkupProcessor::StyledTextArray array;
2752 array.push_back( styledCharacterToDelete );
2753 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2755 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2760 GetTextLayoutInfo();
2762 ShowGrabHandleAndSetVisibility( false );
2764 mCursorPosition = positionToDelete -1;
2766 const TextStyle oldInputStyle( mInputStyle );
2768 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2770 if( oldInputStyle != mInputStyle )
2772 // Updates the line height accordingly with the input style.
2775 EmitStyleChangedSignal();
2780 /*Insert new character into the string and (optionally) redisplay text-input*/
2781 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2783 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2785 // Ensure insertionPosition is not out of bounds.
2786 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2788 bool textExceedsMaximunNumberOfCharacters = false;
2789 bool textExceedsBoundary = false;
2790 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2792 ShowGrabHandleAndSetVisibility( false );
2794 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2798 mIgnoreCommitFlag = true;
2799 mPreEditFlag = false;
2800 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2801 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2804 if( textExceedsMaximunNumberOfCharacters )
2806 EmitMaxInputCharactersReachedSignal();
2809 if( textExceedsBoundary )
2811 EmitInputTextExceedsBoundariesSignal();
2812 PreEditReset( false );
2816 return insertedStringLength;
2819 ImageActor TextInput::CreateCursor( const Vector4& color)
2822 cursor = CreateSolidColorActor(color);
2823 cursor.SetName( "Cursor" );
2825 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2826 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2827 cursor.SetVisible(false);
2832 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2834 // As cursor is not moving due to grab handle, handle should be hidden.
2835 ShowGrabHandleAndSetVisibility( false );
2837 bool cursorPositionChanged = false;
2840 if ( mCursorPosition >= places )
2842 mCursorPosition = mCursorPosition - places;
2843 cursorPositionChanged = true;
2848 if ((mCursorPosition + places) <= mStyledText.size())
2850 mCursorPosition = mCursorPosition + places;
2851 cursorPositionChanged = true;
2855 if( cursorPositionChanged )
2857 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2859 const TextStyle oldInputStyle( mInputStyle );
2860 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2864 if( oldInputStyle != mInputStyle )
2866 // Updates the line height accordingly with the input style.
2869 EmitStyleChangedSignal();
2872 ImfManager imfManager = ImfManager::Get();
2875 imfManager.SetCursorPosition ( mCursorPosition );
2876 imfManager.NotifyCursorPosition();
2881 void TextInput::DrawCursor(const std::size_t nthChar)
2883 // Get height of cursor and set its size
2884 Size size( CURSOR_THICKNESS, 0.0f );
2885 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2887 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2891 // Measure Font so know how big text will be if no initial text to measure.
2892 size.height = mLineHeight;
2895 mCursor.SetSize(size);
2897 // If the character is italic then the cursor also tilts.
2898 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2900 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2902 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2904 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2905 bool altPositionValid; // Alternate cursor validity flag.
2906 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2907 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2909 SetAltCursorEnabled( altPositionValid );
2911 if(!altPositionValid)
2913 mCursor.SetPosition( position + UI_OFFSET );
2917 size.height *= 0.5f;
2918 mCursor.SetSize(size);
2919 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2921 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2922 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2923 size.height = rowSize.height * 0.5f;
2924 mCursorRTL.SetSize(size);
2925 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2928 if( IsScrollEnabled() )
2930 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2931 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2936 void TextInput::SetAltCursorEnabled( bool enabled )
2938 mCursorRTLEnabled = enabled;
2939 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2942 void TextInput::SetCursorVisibility( bool visible )
2944 mCursorVisibility = visible;
2945 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2946 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2949 void TextInput::CreateGrabHandle( Dali::Image image )
2955 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2959 mGrabHandleImage = image;
2962 mGrabHandle = ImageActor::New(mGrabHandleImage);
2963 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2964 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2966 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2968 ShowGrabHandleAndSetVisibility( false );
2970 CreateGrabArea( mGrabHandle );
2972 mActiveLayer.Add(mGrabHandle);
2976 void TextInput::CreateGrabArea( Actor& parent )
2978 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2979 mGrabArea.SetName( "GrabArea" );
2980 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2981 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2982 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2983 mTapDetector.Attach( mGrabArea );
2984 mPanGestureDetector.Attach( mGrabArea );
2985 mLongPressDetector.Attach( mGrabArea );
2987 parent.Add(mGrabArea);
2990 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2992 Vector3 actualHandlePosition;
2996 mActualGrabHandlePosition.x += displacement.x;
2997 mActualGrabHandlePosition.y += displacement.y;
2999 // Grab handle should jump to the nearest character and take cursor with it
3000 std::size_t newCursorPosition = 0;
3001 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3003 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3005 bool handleVisible = true;
3007 if( IsScrollEnabled() )
3009 const Vector3 controlSize = GetControlSize();
3010 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3011 // Scrolls the text if the handle is not in a visible position
3012 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3019 mCurrentHandlePosition = actualHandlePosition;
3020 mScrollDisplacement = Vector2::ZERO;
3024 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3026 mScrollDisplacement.x = -SCROLL_SPEED;
3028 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3030 mScrollDisplacement.x = SCROLL_SPEED;
3032 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3034 mScrollDisplacement.y = -SCROLL_SPEED;
3036 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3038 mScrollDisplacement.y = SCROLL_SPEED;
3044 if( handleVisible && // Only redraw cursor and do updates if position changed
3045 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3047 mCursorPosition = newCursorPosition;
3049 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3051 const TextStyle oldInputStyle( mInputStyle );
3053 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3055 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3057 if( oldInputStyle != mInputStyle )
3059 // Updates the line height accordingly with the input style.
3062 EmitStyleChangedSignal();
3067 return actualHandlePosition;
3070 void TextInput::ShowGrabHandle( bool visible )
3072 if ( IsGrabHandleEnabled() )
3076 mGrabHandle.SetVisible( mGrabHandleVisibility );
3078 StartMonitoringStageForTouch();
3082 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3084 mGrabHandleVisibility = visible;
3085 ShowGrabHandle( visible );
3088 // Callbacks connected to be Property notifications for Boundary checking.
3090 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3092 mIsSelectionHandleOneFlipped = true;
3093 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3094 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3097 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3099 mIsSelectionHandleOneFlipped = false;
3100 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3101 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3104 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3106 mIsSelectionHandleTwoFlipped = true;
3107 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3108 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3111 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3113 mIsSelectionHandleTwoFlipped = false;
3114 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3115 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3118 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3119 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3121 mSelectionHandleOne.SetOpacity(0.0f);
3124 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3126 mSelectionHandleOne.SetOpacity(1.0f);
3129 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3131 mSelectionHandleTwo.SetOpacity(0.0f);
3134 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3136 mSelectionHandleTwo.SetOpacity(1.0f);
3139 // End of Callbacks connected to be Property notifications for Boundary checking.
3141 void TextInput::SetUpHandlePropertyNotifications()
3143 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3145 Vector3 handlesize = GetSelectionHandleSize();
3147 // Exceeding horizontal boundary
3148 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3149 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3151 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3152 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3154 // Within horizontal boundary
3155 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3156 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3158 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3159 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3161 // Exceeding vertical boundary
3162 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3163 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3164 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3165 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3167 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3168 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3169 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3170 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3172 // Within vertical boundary
3173 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3174 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3175 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3176 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3178 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3179 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3180 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3181 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3184 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3186 mSelectionHandleOnePosition = start;
3187 mSelectionHandleTwoPosition = end;
3189 if ( !mSelectionHandleOne )
3191 // create normal and pressed images
3192 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3193 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3195 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3196 mSelectionHandleOne.SetName("SelectionHandleOne");
3197 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3198 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3199 mIsSelectionHandleOneFlipped = false;
3200 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3202 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3203 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3205 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3206 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3208 mTapDetector.Attach( mHandleOneGrabArea );
3209 mPanGestureDetector.Attach( mHandleOneGrabArea );
3211 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3213 mSelectionHandleOne.Add( mHandleOneGrabArea );
3214 mActiveLayer.Add( mSelectionHandleOne );
3217 if ( !mSelectionHandleTwo )
3219 // create normal and pressed images
3220 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3221 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3223 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3224 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3225 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3226 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3227 mIsSelectionHandleTwoFlipped = false;
3228 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3230 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3231 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3232 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3233 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3235 mTapDetector.Attach( mHandleTwoGrabArea );
3236 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3238 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3240 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3242 mActiveLayer.Add( mSelectionHandleTwo );
3245 SetUpHandlePropertyNotifications();
3247 // update table as text may have changed.
3248 GetTextLayoutInfo();
3250 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3251 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3253 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3254 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3256 // Calculates and set the visibility if the scroll mode is enabled.
3257 bool isSelectionHandleOneVisible = true;
3258 bool isSelectionHandleTwoVisible = true;
3259 if( IsScrollEnabled() )
3261 const Vector3& controlSize( GetControlSize() );
3262 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3263 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3264 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3265 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3268 CreateHighlight(); // function will only create highlight if not already created.
3271 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3273 Vector3 actualHandlePosition;
3275 if ( mSelectionHandleOne && mSelectionHandleTwo )
3277 const Vector3& controlSize = GetControlSize();
3279 Size cursorSize( CURSOR_THICKNESS, 0.f );
3281 // Get a reference of the wanted selection handle (handle one or two).
3282 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3284 // Get a reference for the current position of the handle and a copy of its pair
3285 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3286 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3288 // Get a handle of the selection handle actor
3289 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3291 // Selection handles should jump to the nearest character
3292 std::size_t newHandlePosition = 0;
3293 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3295 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3297 bool handleVisible = true;
3299 if( IsScrollEnabled() )
3301 mCurrentSelectionId = handleId;
3303 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3304 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3305 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3312 mCurrentSelectionHandlePosition = actualHandlePosition;
3313 mScrollDisplacement = Vector2::ZERO;
3317 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3319 mScrollDisplacement.x = -SCROLL_SPEED;
3321 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3323 mScrollDisplacement.x = SCROLL_SPEED;
3325 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3327 mScrollDisplacement.y = -SCROLL_SPEED;
3329 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3331 mScrollDisplacement.y = SCROLL_SPEED;
3337 if ( handleVisible && // Ensure the handle is visible.
3338 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3339 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3341 currentSelectionHandlePosition = newHandlePosition;
3343 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3344 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3348 if ( handleId == HandleOne )
3350 const TextStyle oldInputStyle( mInputStyle );
3352 // Set Active Style to that of first character in selection
3353 if( mSelectionHandleOnePosition < mStyledText.size() )
3355 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3358 if( oldInputStyle != mInputStyle )
3360 // Updates the line height accordingly with the input style.
3363 EmitStyleChangedSignal();
3369 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3372 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3375 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3376 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3378 if ( selectionHandleActor )
3380 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3381 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3382 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3384 if( IsScrollEnabled() )
3386 const Size cursorSize( CURSOR_THICKNESS,
3387 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3388 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3390 GetControlSize() ) );
3395 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3397 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3398 // For now the visual position of end of logical string will be end of visual string.
3399 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3401 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3404 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3406 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3407 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3408 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3409 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3411 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3413 // Deselect text prior to startSelectionIt
3414 for(;it!=startSelectionIt;++it)
3416 selectedVisualText[*it] = false;
3419 // Select text from startSelectionIt -> endSelectionIt
3420 for(;it!=endSelectionIt;++it)
3422 selectedVisualText[*it] = true;
3425 // Deselect text after endSelection
3428 selectedVisualText[*it] = false;
3431 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3434 // Calculate the dimensions of the quads they will make the highlight mesh
3435 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3437 // At the moment there is no public API to modify the block alignment option.
3438 const bool blockAlignEnabled = true;
3440 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3442 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3444 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3445 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3447 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3448 std::vector<bool> selectedVisualText;
3449 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3450 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3451 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3453 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3454 float rowLeft = 0.0f;
3455 float rowRight = 0.0f;
3456 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3457 float maxRowLeft = std::numeric_limits<float>::max();
3458 float maxRowRight = 0.0f;
3460 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3462 // Scan through entire text.
3465 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3467 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3468 bool charSelected( false );
3469 if( selectedIt != selectedEndIt )
3471 charSelected = *selectedIt++;
3474 if(selectionState == SelectionNone)
3478 selectionState = SelectionStarted;
3479 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3480 rowRight = rowLeft + charInfo.mSize.width;
3483 else if(selectionState == SelectionStarted)
3485 // break selection on:
3486 // 1. new line causing selection break. (\n or wordwrap)
3487 // 2. character not selected.
3488 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3491 // finished selection.
3492 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3493 // that it resides on. That way this enumeration is not necessary.
3495 if(lastIt->mIsNewParagraphChar)
3497 // 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.
3498 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3500 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3501 maxRowLeft = std::min(maxRowLeft, min.x);
3502 maxRowRight = std::max(maxRowRight, max.x);
3503 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3504 float rowTop = rowBottom - rowSize.height;
3506 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3507 if(charSelected && blockAlignEnabled)
3509 rowRight = std::numeric_limits<float>::max();
3511 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3513 selectionState = SelectionNone;
3515 // Still selected? start a new selection
3518 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3519 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3520 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3521 selectionState = SelectionStarted;
3526 // build up highlight(s) with this selection data.
3527 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3528 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3535 // If reached end, and still on selection, then close selection.
3538 if(selectionState == SelectionStarted)
3540 // finished selection.
3542 if(lastIt->mIsNewParagraphChar)
3544 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3546 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3547 maxRowLeft = std::min(maxRowLeft, min.x);
3548 maxRowRight = std::max(maxRowRight, max.x);
3549 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3550 float rowTop = rowBottom - rowSize.height;
3551 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3555 // Get the top left and bottom right corners.
3556 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3557 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3558 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3560 // Clamp quads so they appear to clip to borders of the whole text.
3561 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3563 // For block-align align Further Clamp quads to max left and right extents
3564 if(blockAlignEnabled)
3566 // BlockAlign: Will adjust highlight to block:
3568 // H[ello] (top row right = max of all rows right)
3569 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3570 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3571 // [text] (bottom row left = min of all rows left)
3572 // (common in SMS messaging selection)
3574 // As opposed to the default which is tight text highlighting.
3579 // (common in regular text editors/web browser selection)
3581 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3584 // Finally clamp quads again so they don't exceed the boundry of the control.
3585 const Vector3& controlSize = GetControlSize();
3586 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3589 return mNewHighlightInfo;
3592 void TextInput::UpdateHighlight()
3594 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3596 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3598 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3599 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3600 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3601 // [BOTTOM] [ MIDDLE ]
3604 // Each quad is created as 2 triangles.
3605 // Middle is just 1 quad regardless of its size.
3619 if ( mHighlightMeshActor )
3621 // vertex and triangle buffers should always be present if MeshActor is alive.
3622 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3623 MeshData::VertexContainer vertices;
3624 Dali::MeshData::FaceIndices faceIndices;
3626 if( !newHighlightInfo.mQuadList.empty() )
3628 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3629 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3631 // vertex position defaults to (0 0 0)
3632 MeshData::Vertex vertex;
3633 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3636 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3638 // Add each quad geometry (a sub-selection) to the mesh data.
3648 QuadCoordinates& quad = *iter;
3650 vertex.x = quad.min.x;
3651 vertex.y = quad.min.y;
3652 vertices.push_back( vertex );
3655 vertex.x = quad.max.x;
3656 vertex.y = quad.min.y;
3657 vertices.push_back( vertex );
3659 // bottom-left (v+2)
3660 vertex.x = quad.min.x;
3661 vertex.y = quad.max.y;
3662 vertices.push_back( vertex );
3664 // bottom-right (v+3)
3665 vertex.x = quad.max.x;
3666 vertex.y = quad.max.y;
3667 vertices.push_back( vertex );
3669 // triangle A (3, 1, 0)
3670 faceIndices.push_back( v + 3 );
3671 faceIndices.push_back( v + 1 );
3672 faceIndices.push_back( v );
3674 // triangle B (0, 2, 3)
3675 faceIndices.push_back( v );
3676 faceIndices.push_back( v + 2 );
3677 faceIndices.push_back( v + 3 );
3679 mMeshData.SetFaceIndices( faceIndices );
3682 BoneContainer bones(0); // passed empty as bones not required
3683 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3684 mHighlightMesh.UpdateMeshData(mMeshData);
3689 void TextInput::ClearPopup()
3691 mPopupPanel.Clear();
3694 void TextInput::AddPopupOptions()
3696 mPopupPanel.AddPopupOptions();
3699 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3701 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3703 Vector3 clampedPosition ( position );
3704 Vector3 tailOffsetPosition ( position );
3706 float xOffSet( 0.0f );
3708 Actor self = Self();
3709 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3711 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3712 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3714 // Clamp to left or right or of boundary
3715 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3717 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3719 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3721 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3724 clampedPosition.x = position.x + xOffSet;
3725 tailOffsetPosition.x = -xOffSet;
3727 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3728 bool flipTail( false );
3730 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3732 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3736 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3737 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3740 void TextInput::HidePopup(bool animate, bool signalFinished )
3742 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3744 mPopupPanel.Hide( animate );
3746 if( animate && signalFinished )
3748 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3753 void TextInput::ShowPopup( bool animate )
3756 Vector2 alternativePopupPosition;
3758 if(mHighlightMeshActor && mState == StateEdit)
3761 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3763 // When text is selected, show popup above top handle (and text), or below bottom handle.
3764 // topHandle: referring to the top most point of the handle or the top line of selection.
3765 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3767 topHandle = mSelectionHandleOneActualPosition;
3768 bottomHandle = mSelectionHandleTwoActualPosition;
3769 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3773 topHandle = mSelectionHandleTwoActualPosition;
3774 bottomHandle = mSelectionHandleOneActualPosition;
3775 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3777 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3778 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3780 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3782 position.x = xPosition;
3784 // Alternative position if no upper space
3785 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3786 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3790 // When no text is selected, show popup at world position of grab handle or cursor
3791 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3792 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3793 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3794 // if can't be positioned above, then position below row.
3795 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3798 // If grab handle enabled then position pop-up below the grab handle.
3799 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3803 SetPopupPosition( position, alternativePopupPosition );
3806 mPopupPanel.Show( Self(), animate );
3807 StartMonitoringStageForTouch();
3809 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3812 void TextInput::ShowPopupCutCopyPaste()
3816 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3817 // Check the selected text is whole text or not.
3818 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3820 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3823 if ( !mStyledText.empty() )
3825 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3826 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3829 if( mClipboard && mClipboard.NumberOfItems() )
3831 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3832 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3837 mPopupPanel.Hide(false);
3841 void TextInput::SetUpPopupSelection( bool showCutButton )
3844 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3845 // If no text exists then don't offer to select
3846 if ( !mStyledText.empty() )
3848 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3849 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3850 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, showCutButton );
3852 // if clipboard has valid contents then offer paste option
3853 if( mClipboard && mClipboard.NumberOfItems() )
3855 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3856 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3861 mPopupPanel.Hide(false);
3864 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3869 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3870 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3871 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3872 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3874 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3876 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3878 float closestYdifference = std::numeric_limits<float>::max();
3879 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3880 std::size_t numberOfMatchedCharacters = 0;
3882 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3883 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3885 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3887 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3888 float baselinePosition = info.mPosition.y - info.mDescender;
3890 if( info.mIsVisible )
3892 // store difference between source y point and the y position of the current character
3893 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3895 if( currentYdifference < closestYdifference )
3897 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3898 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3899 closestYdifference = currentYdifference;
3900 matchedCharacters.clear();
3901 numberOfMatchedCharacters = 0; // reset count
3904 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3905 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3907 // ignore new line character.
3908 if( !info.mIsNewParagraphChar )
3910 matchedCharacters.push_back( info );
3911 numberOfMatchedCharacters++;
3915 } // End of loop checking each character's y position in the character layout table
3917 // Check if last character is a newline, if it is
3918 // then need pretend there is an imaginary line afterwards,
3919 // and check if user is touching below previous line.
3920 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3922 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3924 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3928 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3929 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3931 bool matched( false );
3933 // 2 Iterate through matching list of y positions and find closest matching X position.
3934 for( ; it != endIt; ++it )
3936 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3938 if( info.mIsVisible )
3940 // stop when on left side of character's center.
3941 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3942 if( sourceScrollOffset.x < characterMidPointPosition )
3944 if(info.mIsRightToLeftCharacter)
3946 rightToLeftChar = true;
3948 glyphIntersection = info.mPosition.x;
3953 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3959 rightToLeftChar = lastRightToLeftChar;
3962 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3963 closestIndex = lineOffset + matchCharacterIndex;
3965 mClosestCursorPositionEOL = false; // reset
3966 if ( it == endIt && !matched )
3968 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3971 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3972 if( rightToLeftChar && lastRightToLeftChar )
3974 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3979 // closestIndex is the visual index, need to convert it to the logical index
3980 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3982 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3984 // Checks for situations where user is touching between LTR and RTL
3985 // characters. To identify if the user means the end of a LTR string
3986 // or the beginning of an RTL string, and vice versa.
3987 if( closestIndex > 0 )
3989 if( rightToLeftChar && !lastRightToLeftChar )
3994 // A: In this touch range, the user is indicating that they wish to place
3995 // the cursor at the end of the LTR text.
3996 // B: In this touch range, the user is indicating that they wish to place
3997 // the cursor at the end of the RTL text.
3999 // Result of touching A area:
4000 // [.....LTR]|[RTL......]+
4002 // |: primary cursor (for typing LTR chars)
4003 // +: secondary cursor (for typing RTL chars)
4005 // Result of touching B area:
4006 // [.....LTR]+[RTL......]|
4008 // |: primary cursor (for typing RTL chars)
4009 // +: secondary cursor (for typing LTR chars)
4011 if( sourceScrollOffset.x < glyphIntersection )
4016 else if( !rightToLeftChar && lastRightToLeftChar )
4018 if( sourceScrollOffset.x < glyphIntersection )
4025 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4026 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4027 // one further ahead
4028 if( rightToLeftChar && !lastRightToLeftChar )
4033 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4035 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4037 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4046 float TextInput::GetLineJustificationPosition() const
4048 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4049 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4050 float alignmentOffset = 0.f;
4052 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4053 if( alignment & Toolkit::Alignment::HorizontalLeft )
4055 alignmentOffset = 0.f;
4057 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4059 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4061 else if( alignment & Toolkit::Alignment::HorizontalRight )
4063 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4066 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4067 float justificationOffset = 0.f;
4069 switch( justification )
4071 case Toolkit::TextView::Left:
4073 justificationOffset = 0.f;
4076 case Toolkit::TextView::Center:
4078 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4081 case Toolkit::TextView::Right:
4083 justificationOffset = mTextLayoutInfo.mTextSize.width;
4086 case Toolkit::TextView::Justified:
4088 justificationOffset = 0.f;
4093 DALI_ASSERT_ALWAYS( false );
4097 return alignmentOffset + justificationOffset;
4100 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4102 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4103 A newline character is not inserted in this case */
4105 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4107 Vector3 cursorPosition;
4109 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4111 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4113 // end character so use
4114 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4115 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4119 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4122 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4124 // If previous character on a different line then use current characters position
4125 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4127 if ( mClosestCursorPositionEOL )
4129 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4133 cursorPosition = Vector3(currentCharInfo.mPosition);
4138 // Previous character is on same line so use position of previous character plus it's width.
4139 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4142 return cursorPosition;
4145 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4147 bool direction(false);
4148 Vector3 alternatePosition;
4149 bool alternatePositionValid(false);
4151 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4154 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4156 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4158 alternatePositionValid = false;
4159 directionRTL = false;
4161 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4163 std::size_t visualCharacterPosition;
4165 // When cursor is not at beginning, consider possibility of
4166 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4167 if(characterPosition > 0)
4169 // Cursor position should be the end of the last character.
4170 // If the last character is LTR, then the end is on the right side of the glyph.
4171 // If the last character is RTL, then the end is on the left side of the glyph.
4172 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4174 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4176 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4179 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4180 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4182 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4183 const Vector3& size = GetControlSize();
4185 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4187 --visualCharacterPosition;
4189 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4192 if(!info.mIsNewParagraphChar)
4194 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4198 // When cursor points to first character on new line, position cursor at the start of this glyph.
4199 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4201 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4202 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4203 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4205 cursorPosition.x = infoNext.mPosition.x + start;
4206 cursorPosition.y = infoNext.mPosition.y;
4210 // If cursor points to the end of text, then can only position
4211 // cursor where the new line starts based on the line-justification position.
4212 cursorPosition.x = GetLineJustificationPosition();
4214 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4216 // If this is after the last character, then we can assume that the new cursor
4217 // should be exactly one row below the current row.
4219 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4220 cursorPosition.y = info.mPosition.y + rowRect.height;
4224 // If this is not after last character, then we can use this row's height.
4225 // should be exactly one row below the current row.
4227 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4228 cursorPosition.y = info.mPosition.y + rowRect.height;
4233 directionRTL = info.mIsRightToLeftCharacter;
4235 // 1. When the cursor is neither at the beginning or the end,
4236 // we can show multiple cursors under situations when the cursor is
4237 // between RTL and LTR text...
4238 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4240 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4242 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4243 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4245 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4247 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4248 // Text: [...LTR...]|[...RTL...]
4250 // Alternate cursor pos: ^
4251 // In which case we need to display an alternate cursor for the RTL text.
4253 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4254 alternatePosition.y = infoAlt.mPosition.y;
4255 alternatePositionValid = true;
4257 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4259 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4260 // Text: |[...RTL...] [...LTR....]
4262 // Alternate cursor pos: ^
4263 // In which case we need to display an alternate cursor for the RTL text.
4265 alternatePosition.x = infoAlt.mPosition.x;
4266 alternatePosition.y = infoAlt.mPosition.y;
4267 alternatePositionValid = true;
4272 // 2. When the cursor is at the end of the text,
4273 // and we have multi-directional text,
4274 // we can also consider showing mulitple cursors.
4275 // The rule here is:
4276 // If first and last characters on row are different
4277 // Directions, then two cursors need to be displayed.
4279 // Get first logical glyph on row
4280 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4282 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4283 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4285 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4287 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4288 // Text: [...LTR...]|[...RTL...]
4290 // Alternate cursor pos: ^
4291 // In which case we need to display an alternate cursor for the RTL text, this cursor
4292 // should be at the end of the given line.
4294 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4295 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4296 alternatePosition.y = infoAlt.mPosition.y;
4297 alternatePositionValid = true;
4299 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4301 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4302 // Text: |[...RTL...] [...LTR....]
4304 // Alternate cursor pos: ^
4305 // In which case we need to display an alternate cursor for the RTL text.
4307 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4308 alternatePosition.x = infoAlt.mPosition.x;
4309 alternatePosition.y = infoAlt.mPosition.y;
4310 alternatePositionValid = true;
4313 } // characterPosition > 0
4314 else if(characterPosition == 0)
4316 // When the cursor position is at the beginning, it should be at the start of the current character.
4317 // If the current character is LTR, then the start is on the right side of the glyph.
4318 // If the current character is RTL, then the start is on the left side of the glyph.
4319 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4321 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4323 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4326 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4327 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4329 cursorPosition.x = info.mPosition.x + start;
4330 cursorPosition.y = info.mPosition.y;
4331 directionRTL = info.mIsRightToLeftCharacter;
4336 // If the character table is void, place the cursor accordingly the text alignment.
4337 const Vector3& size = GetControlSize();
4339 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4340 float alignmentOffset = 0.f;
4342 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4343 if( alignment & Toolkit::Alignment::HorizontalLeft )
4345 alignmentOffset = 0.f;
4347 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4349 alignmentOffset = 0.5f * ( size.width );
4351 else if( alignment & Toolkit::Alignment::HorizontalRight )
4353 alignmentOffset = size.width;
4356 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4357 cursorPosition.x = alignmentOffset;
4359 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4360 if( alignment & Toolkit::Alignment::VerticalTop )
4362 cursorPosition.y = mLineHeight;
4364 else if( alignment & Toolkit::Alignment::VerticalCenter )
4366 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4368 else if( alignment & Toolkit::Alignment::VerticalBottom )
4370 cursorPosition.y = size.height;
4374 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4375 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4376 if( alternatePositionValid )
4378 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4379 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4382 return cursorPosition;
4385 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4387 // scan string from current position to beginning of current line to note direction of line
4388 while(logicalPosition)
4391 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4392 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4399 return logicalPosition;
4402 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4406 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4409 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4411 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4412 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4414 min = Vector2::ZERO;
4415 max = Vector2(0.0f, mLineHeight);
4419 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4420 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4421 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4423 // If cursor is pointing to end of line, then start from last character.
4424 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4426 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4428 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4429 if( !it->mIsVisible )
4431 characterPosition = FindVisibleCharacter( Left, characterPosition );
4432 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4435 // Scan characters left and right of cursor, stopping when end of line/string reached or
4436 // y position greater than threshold of reference line.
4438 // 1. scan left until we reach the beginning or a different line.
4439 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4440 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4441 // min-x position is the left-most char's left (x)
4442 // max-x position is the right-most char's right (x)
4443 // min-y position is the minimum of all character's top (y)
4444 // max-y position is the maximum of all character's bottom (y+height)
4445 min.y = validCharIt->mPosition.y;
4446 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4451 min.y = std::min(min.y, validCharIt->mPosition.y);
4452 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4461 if( (it->mPosition.y < referenceLine) ||
4462 (it->mIsNewParagraphChar) ||
4469 // info refers to the first character on this line.
4470 min.x = validCharIt->mPosition.x;
4472 // 2. scan right until we reach end or a different line
4473 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4474 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4478 if( (it->mPosition.y > referenceLine) ||
4479 (it->mIsNewParagraphChar) ||
4486 min.y = std::min(min.y, validCharIt->mPosition.y);
4487 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4492 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4494 if ( validCharIt != end )
4496 // info refers to the last character on this line.
4497 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4500 return Size( max.x - min.x, max.y - min.y );
4503 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4505 Actor popUpPanel = mPopupPanel.GetRootActor();
4507 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4513 Dali::Actor parent( touchedActor.GetParent() );
4517 return WasTouchedCheck( parent );
4524 void TextInput::StartMonitoringStageForTouch()
4526 Stage stage = Stage::GetCurrent();
4527 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4530 void TextInput::EndMonitoringStageForTouch()
4532 Stage stage = Stage::GetCurrent();
4533 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4536 void TextInput::OnStageTouched(const TouchEvent& event)
4538 if( event.GetPointCount() > 0 )
4540 if ( TouchPoint::Down == event.GetPoint(0).state )
4542 const Actor touchedActor(event.GetPoint(0).hitActor);
4544 bool popUpShown( false );
4546 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4551 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4553 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4555 EndMonitoringStageForTouch();
4556 HidePopup( true, false );
4559 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4561 EndMonitoringStageForTouch();
4562 ShowGrabHandleAndSetVisibility( false );
4568 void TextInput::SelectText(std::size_t start, std::size_t end)
4570 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4571 IsGrabHandleEnabled()?"true":"false",
4572 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4573 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4574 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4576 StartMonitoringStageForTouch();
4578 if ( mEditModeActive ) // Only allow text selection when in edit mode
4580 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4581 mSelectingText = true;
4583 std::size_t selectionStartPosition = std::min( start, end );
4585 // Hide grab handle when selecting.
4586 ShowGrabHandleAndSetVisibility( false );
4588 if( start != end ) // something to select
4590 SetCursorVisibility( false );
4591 StopCursorBlinkTimer();
4593 CreateSelectionHandles(start, end);
4596 const TextStyle oldInputStyle( mInputStyle );
4597 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4599 if( oldInputStyle != mInputStyle )
4601 // Updates the line height accordingly with the input style.
4604 EmitStyleChangedSignal();
4610 mSelectingText = false;
4614 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4616 MarkupProcessor::StyledTextArray currentSelectedText;
4618 if ( IsTextSelected() )
4620 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4621 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4623 for(; it != end; ++it)
4625 MarkupProcessor::StyledText& styledText( *it );
4626 currentSelectedText.push_back( styledText );
4629 return currentSelectedText;
4632 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4634 const std::size_t beginIndex = std::min( begin, end );
4635 const std::size_t endIndex = std::max( begin, end );
4638 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4640 // Create a styled text array used to replace the text into the text-view.
4641 MarkupProcessor::StyledTextArray text;
4642 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4644 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4645 GetTextLayoutInfo();
4647 if( IsScrollEnabled() )
4649 // Need to set the scroll position as the text's size may have changed.
4650 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4653 ShowGrabHandleAndSetVisibility( false );
4659 // Set Handle positioning as the new style may have repositioned the characters.
4660 SetSelectionHandlePosition(HandleOne);
4661 SetSelectionHandlePosition(HandleTwo);
4664 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4666 // Just hide the grab handle when keyboard is hidden.
4667 if (!keyboardShown )
4669 ShowGrabHandleAndSetVisibility( false );
4671 // If the keyboard is not now being shown, then hide the popup panel
4672 mPopupPanel.Hide( true );
4676 // Removes highlight and resumes edit mode state
4677 void TextInput::RemoveHighlight( bool hidePopup )
4679 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4681 if ( mHighlightMeshActor )
4683 if ( mSelectionHandleOne )
4685 mActiveLayer.Remove( mSelectionHandleOne );
4686 mSelectionHandleOne.Reset();
4687 mSelectionHandleOneOffset.x = 0.0f;
4689 if ( mSelectionHandleTwo )
4691 mActiveLayer.Remove( mSelectionHandleTwo );
4692 mSelectionHandleTwo.Reset();
4693 mSelectionHandleTwoOffset.x = 0.0f;
4696 mNewHighlightInfo.mQuadList.clear();
4698 Self().Remove( mHighlightMeshActor );
4700 SetCursorVisibility( true );
4701 StartCursorBlinkTimer();
4703 mHighlightMeshActor.Reset();
4704 // NOTE: We cannot dereference mHighlightMesh, due
4705 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4713 mSelectionHandleOnePosition = 0;
4714 mSelectionHandleTwoPosition = 0;
4717 void TextInput::CreateHighlight()
4719 if ( !mHighlightMeshActor )
4721 mMeshData = MeshData( );
4722 mMeshData.SetHasNormals( true );
4724 mCustomMaterial = Material::New("CustomMaterial");
4725 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4727 mMeshData.SetMaterial( mCustomMaterial );
4729 mHighlightMesh = Mesh::New( mMeshData );
4731 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4732 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4733 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4734 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4735 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4736 mHighlightMeshActor.SetAffectedByLighting(false);
4738 Self().Add(mHighlightMeshActor);
4743 bool TextInput::CopySelectedTextToClipboard()
4745 mCurrentCopySelecton.clear();
4747 mCurrentCopySelecton = GetSelectedText();
4749 std::string stringToStore;
4751 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4752 * a marked up string.
4754 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4755 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4756 bool success = mClipboard.SetItem( stringToStore );
4760 void TextInput::PasteText( const Text& text )
4762 // Update Flag, indicates whether to update the text-input contents or not.
4763 // Any key stroke that results in a visual change of the text-input should
4764 // set this flag to true.
4765 bool update = false;
4766 if( mHighlightMeshActor )
4768 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4769 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4771 ImfManager imfManager = ImfManager::Get();
4774 imfManager.SetCursorPosition( mCursorPosition );
4775 imfManager.NotifyCursorPosition();
4777 DeleteHighlightedText( true );
4781 bool textExceedsMaximunNumberOfCharacters = false;
4782 bool textExceedsBoundary = false;
4784 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4786 mCursorPosition += insertedStringLength;
4787 ImfManager imfManager = ImfManager::Get();
4790 imfManager.SetCursorPosition ( mCursorPosition );
4791 imfManager.NotifyCursorPosition();
4794 update = update || ( insertedStringLength > 0 );
4801 if( insertedStringLength < text.GetLength() )
4803 EmitMaxInputCharactersReachedSignal();
4806 if( textExceedsBoundary )
4808 EmitInputTextExceedsBoundariesSignal();
4812 void TextInput::SetTextDirection()
4814 // Put the cursor to the right if we are empty and an RTL language is being used.
4815 if ( mStyledText.empty() )
4817 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4819 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4820 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4822 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4823 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4825 int alignment( mDisplayedTextView.GetTextAlignment() &
4826 ( Toolkit::Alignment::VerticalTop |
4827 Toolkit::Alignment::VerticalCenter |
4828 Toolkit::Alignment::VerticalBottom |
4829 Toolkit::Alignment::HorizontalCenter ) );
4830 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4832 // If our alignment is in the center, then do not change.
4833 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4835 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4838 // If our justification is in the center, then do not change.
4839 if ( justification != Toolkit::TextView::Center )
4841 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4844 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4845 mDisplayedTextView.SetLineJustification( justification );
4849 void TextInput::UpdateLineHeight()
4851 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4852 mLineHeight = font.GetLineHeight();
4854 // 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.
4856 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4858 if( !mExceedEnabled || shrink )
4860 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4864 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4866 std::size_t position = 0;
4868 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4874 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4876 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4878 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4884 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4885 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4887 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4893 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4898 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4905 void TextInput::SetSortModifier( float depthOffset )
4907 if(mDisplayedTextView)
4909 mDisplayedTextView.SetSortModifier(depthOffset);
4913 void TextInput::SetSnapshotModeEnabled( bool enable )
4915 if(mDisplayedTextView)
4917 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4921 bool TextInput::IsSnapshotModeEnabled() const
4923 bool snapshotEnabled = false;
4925 if(mDisplayedTextView)
4927 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4930 return snapshotEnabled;
4933 void TextInput::SetMarkupProcessingEnabled( bool enable )
4935 mMarkUpEnabled = enable;
4938 bool TextInput::IsMarkupProcessingEnabled() const
4940 return mMarkUpEnabled;
4943 void TextInput::SetScrollEnabled( bool enable )
4945 if( mDisplayedTextView )
4947 mDisplayedTextView.SetScrollEnabled( enable );
4952 // Don't set cursor's and handle's visibility to false if they are outside the
4953 // boundaries of the text-input.
4954 mIsCursorInScrollArea = true;
4955 mIsGrabHandleInScrollArea = true;
4956 if( mSelectionHandleOne && mSelectionHandleTwo )
4958 mSelectionHandleOne.SetVisible( true );
4959 mSelectionHandleTwo.SetVisible( true );
4961 if( mHighlightMeshActor )
4963 mHighlightMeshActor.SetVisible( true );
4969 bool TextInput::IsScrollEnabled() const
4971 bool scrollEnabled = false;
4973 if( mDisplayedTextView )
4975 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4978 return scrollEnabled;
4981 void TextInput::SetScrollPosition( const Vector2& position )
4983 if( mDisplayedTextView )
4985 mDisplayedTextView.SetScrollPosition( position );
4989 Vector2 TextInput::GetScrollPosition() const
4991 Vector2 scrollPosition;
4993 if( mDisplayedTextView )
4995 scrollPosition = mDisplayedTextView.GetScrollPosition();
4998 return scrollPosition;
5001 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5003 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5004 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5005 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5007 // Add style to the new input text.
5008 MarkupProcessor::StyledTextArray textToInsert;
5009 for( std::size_t i = 0; i < insertedStringLength; ++i )
5011 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5012 textToInsert.push_back( newStyledCharacter );
5015 //Insert text to the TextView.
5016 const bool emptyTextView = mStyledText.empty();
5017 if( emptyTextView && mPlaceHolderSet )
5019 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5020 mDisplayedTextView.SetText( textToInsert );
5024 if( 0 == numberOfCharactersToReplace )
5026 mDisplayedTextView.InsertTextAt( position, textToInsert );
5030 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5033 mPlaceHolderSet = false;
5035 if( textToInsert.empty() )
5037 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5038 GetTextLayoutInfo();
5042 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5043 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5046 textExceedsBoundary = false;
5048 if( !mExceedEnabled )
5050 const Vector3& size = GetControlSize();
5052 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5054 // If new text does not fit within TextView
5055 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5056 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5057 GetTextLayoutInfo();
5058 textExceedsBoundary = true;
5059 insertedStringLength = 0;
5062 if( textExceedsBoundary )
5064 // Add the part of the text which fits on the text-input.
5066 // Split the text which doesn't fit in two halves.
5067 MarkupProcessor::StyledTextArray firstHalf;
5068 MarkupProcessor::StyledTextArray secondHalf;
5069 SplitText( textToInsert, firstHalf, secondHalf );
5071 // Clear text. This text will be filled with the text inserted.
5072 textToInsert.clear();
5074 // Where to insert the text.
5075 std::size_t positionToInsert = position;
5077 bool end = text.GetLength() <= 1;
5080 // Insert text and check ...
5081 const std::size_t textLength = firstHalf.size();
5082 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5083 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5085 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5087 // Inserted text doesn't fit.
5089 // Remove inserted text
5090 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5091 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5093 // The iteration finishes when only one character doesn't fit.
5094 end = textLength <= 1;
5098 // Prepare next two halves for next iteration.
5099 MarkupProcessor::StyledTextArray copyText = firstHalf;
5100 SplitText( copyText, firstHalf, secondHalf );
5107 // store text to be inserted in mStyledText.
5108 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5110 // Increase the inserted characters counter.
5111 insertedStringLength += textLength;
5113 // Prepare next two halves for next iteration.
5114 MarkupProcessor::StyledTextArray copyText = secondHalf;
5115 SplitText( copyText, firstHalf, secondHalf );
5117 // Update where next text has to be inserted
5118 positionToInsert += textLength;
5124 if( textToInsert.empty() && emptyTextView )
5126 // No character has been added and the text-view was empty.
5127 // Set the placeholder text.
5128 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5129 mPlaceHolderSet = true;
5133 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5134 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5135 mPlaceHolderSet = false;
5138 return insertedStringLength;
5141 void TextInput::GetTextLayoutInfo()
5143 if( mStyledText.empty() )
5145 // The text-input has no text, clear the text-view's layout info.
5146 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5150 if( mDisplayedTextView )
5152 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5156 // There is no text-view.
5157 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5162 void TextInput::SetOffsetFromText( const Vector4& offset )
5164 mPopupOffsetFromText = offset;
5167 const Vector4& TextInput::GetOffsetFromText() const
5169 return mPopupOffsetFromText;
5172 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5174 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5178 TextInput& textInputImpl( GetImpl( textInput ) );
5180 switch ( propertyIndex )
5182 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5184 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5187 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5189 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5192 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5194 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5197 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5199 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5202 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5204 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5207 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5209 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5212 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5214 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5217 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5219 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5222 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5224 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5227 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5229 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5232 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5234 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5237 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5239 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5242 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5244 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5247 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5249 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5252 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5254 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5257 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5259 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5265 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5267 Property::Value value;
5269 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5273 TextInput& textInputImpl( GetImpl( textInput ) );
5275 switch ( propertyIndex )
5277 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5279 value = textInputImpl.GetMaterialDiffuseColor();
5282 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5284 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5287 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5289 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5292 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5294 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5297 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5299 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5302 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5304 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5307 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5309 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5312 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5314 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5317 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5319 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5322 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5324 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5327 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5329 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5332 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5334 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5337 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5339 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5342 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5344 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5347 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5349 value = textInputImpl.GetOffsetFromText();
5352 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5354 value = textInputImpl.mCursor.GetCurrentColor();
5361 void TextInput::EmitStyleChangedSignal()
5363 // emit signal if input style changes.
5364 Toolkit::TextInput handle( GetOwner() );
5365 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5368 void TextInput::EmitTextModified()
5370 // emit signal when text changes.
5371 Toolkit::TextInput handle( GetOwner() );
5372 mTextModifiedSignal.Emit( handle );
5376 void TextInput::EmitMaxInputCharactersReachedSignal()
5378 // emit signal if max characters is reached during text input.
5379 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5381 Toolkit::TextInput handle( GetOwner() );
5382 mMaxInputCharactersReachedSignalV2.Emit( handle );
5385 void TextInput::EmitInputTextExceedsBoundariesSignal()
5387 // Emit a signal when the input text exceeds the boundaries of the text input.
5389 Toolkit::TextInput handle( GetOwner() );
5390 mInputTextExceedBoundariesSignalV2.Emit( handle );
5393 } // namespace Internal
5395 } // namespace Toolkit