2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
90 * Whether the given style is the default style or not.
91 * @param[in] style The given style.
92 * @return \e true if the given style is the default. Otherwise it returns \e false.
94 bool IsDefaultStyle( const TextStyle& style )
96 return DEFAULT_TEXT_STYLE == style;
100 * Whether the given styled text is using the default style or not.
101 * @param[in] textArray The given text.
102 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108 const TextStyle& style( (*it).mStyle );
110 if( !IsDefaultStyle( style ) )
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
125 if( ( *it ).mIsVisible )
127 return --cursorPosition;
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
138 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140 if( ( *it ).mIsVisible )
142 return cursorPosition;
148 return cursorPosition;
152 * Whether the given position plus the cursor size offset is inside the given boundary.
154 * @param[in] position The given position.
155 * @param[in] cursorSize The cursor size.
156 * @param[in] controlSize The given boundary.
158 * @return whether the given position is inside the given boundary.
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
169 * Splits a text in two halves.
171 * If the text's number of characters is odd, firstHalf has one more character.
173 * @param[in] text The text to be split.
174 * @param[out] firstHalf The first half of the text.
175 * @param[out] secondHalf The second half of the text.
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
184 const std::size_t textLength = text.size();
185 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
191 } // end of namespace
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
261 QuadCoordinates quad(x1, y1, x2, y2);
262 mQuadList.push_back( quad );
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
267 for(std::size_t i = 0;i < mQuadList.size(); i++)
269 QuadCoordinates& quad = mQuadList[i];
271 quad.min.Clamp(min, max);
272 quad.max.Clamp(min, max);
276 // [TextInput] ////////////////////////////////////////////////////////////////
278 Dali::Toolkit::TextInput TextInput::New()
280 // Create the implementation
281 TextInputPtr textInput(new TextInput());
282 // Pass ownership to CustomActor via derived handle
283 Dali::Toolkit::TextInput handle(*textInput);
284 handle.SetName( "TextInput");
286 textInput->Initialize();
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
296 mDisplayedTextView(),
297 mStyledPlaceHolderText(),
298 mMaxStringLength( DEFAULT_MAX_SIZE ),
299 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300 mCursorPosition( 0 ),
301 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302 mIsSelectionHandleOneFlipped( false ),
303 mIsSelectionHandleTwoFlipped( false ),
304 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308 mSelectionHandleOnePosition( 0 ),
309 mSelectionHandleTwoPosition( 0 ),
311 mPreEditStartPosition( 0 ),
312 mPreEditLength ( 0 ),
313 mNumberOfSurroundingCharactersDeleted( 0 ),
314 mTouchStartTime( 0 ),
316 mCurrentCopySelecton(),
319 mScrollDisplacement(),
320 mCurrentHandlePosition(),
321 mCurrentSelectionId(),
322 mCurrentSelectionHandlePosition(),
323 mRequestedSelection( 0, 0 ),
324 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
327 mMaterialColor( LIGHTBLUE ),
328 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329 mOverrideAutomaticAlignment( false ),
330 mCursorRTLEnabled( false ),
331 mClosestCursorPositionEOL ( false ),
332 mCursorBlinkStatus( true ),
333 mCursorVisibility( false ),
334 mGrabHandleVisibility( false ),
335 mIsCursorInScrollArea( true ),
336 mIsGrabHandleInScrollArea( true ),
337 mEditModeActive( false ),
338 mEditOnTouch( true ),
339 mTextSelection( true ),
340 mExceedEnabled( true ),
341 mGrabHandleEnabled( true ),
342 mIsSelectionHandleFlipEnabled( true ),
343 mPreEditFlag( false ),
344 mIgnoreCommitFlag( false ),
345 mIgnoreFirstCommitFlag( false ),
346 mSelectingText( false ),
347 mPreserveCursorPosition( false ),
348 mSelectTextOnCommit( false ),
349 mUnderlinedPriorToPreEdit ( false ),
350 mCommitByKeyInput( false ),
351 mPlaceHolderSet( false ),
352 mMarkUpEnabled( false )
354 // Updates the line height accordingly with the input style.
358 TextInput::~TextInput()
360 StopCursorBlinkTimer();
365 std::string TextInput::GetText() const
369 // Return text-view's text only if the text-input's text is not empty
370 // in order to not to return the placeholder text.
371 if( !mStyledText.empty() )
373 text = mDisplayedTextView.GetText();
379 std::string TextInput::GetMarkupText() const
381 std::string markupString;
382 MarkupProcessor::GetMarkupString( mStyledText, markupString );
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
389 // Get the placeholder styled text array from the markup string.
390 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
392 if( mStyledText.empty() )
394 // Set the placeholder text only if the styled text is empty.
395 mDisplayedTextView.SetText( mStyledPlaceHolderText );
396 mPlaceHolderSet = true;
400 std::string TextInput::GetPlaceholderText()
402 // Traverses the styled placeholder array getting only the text.
403 // Note that for some languages a 'character' could be represented by more than one 'char'
405 std::string placeholderText;
406 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
408 placeholderText.append( (*it).mText.GetText() );
411 return placeholderText ;
414 void TextInput::SetInitialText(const std::string& initialText)
416 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
418 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
420 mPreEditFlag = false;
421 mIgnoreCommitFlag = true;
424 SetText( initialText );
425 PreEditReset( false ); // Reset keyboard as text changed
428 void TextInput::SetText(const std::string& initialText)
430 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
432 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
434 if( mStyledText.empty() )
436 // If the initial text is empty, set the placeholder text.
437 mDisplayedTextView.SetText( mStyledPlaceHolderText );
438 mPlaceHolderSet = true;
442 mDisplayedTextView.SetText( mStyledText );
443 mPlaceHolderSet = false;
448 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
450 ImfManager imfManager = ImfManager::Get();
453 imfManager.SetCursorPosition( mCursorPosition );
454 imfManager.SetSurroundingText( initialText );
455 imfManager.NotifyCursorPosition();
458 if( IsScrollEnabled() )
460 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
463 ShowGrabHandleAndSetVisibility( false );
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
474 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
476 mDisplayedTextView.SetText( styleText );
477 mPlaceHolderSet = false;
479 // If text alignment hasn't been manually set by application developer, then we
480 // automatically determine the alignment based on the content of the text i.e. what
481 // language the text begins with.
482 // TODO: This should determine different alignments for each line (broken by '\n') of text.
483 if(!mOverrideAutomaticAlignment)
485 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486 bool leftToRight(true);
488 if( !styleText.empty() )
490 bool breakOut(false);
492 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
494 const Text& text = textIter->mText;
496 for( std::size_t i = 0; i < text.GetLength(); ++i )
498 Character character( text[i] );
499 if( character.GetCharacterDirection() != Character::Neutral )
501 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
509 // Based on this direction, either left or right align text if not manually set by application developer.
510 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512 Toolkit::Alignment::VerticalTop ) );
513 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
521 mMaxStringLength = maxChars;
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
526 DALI_ASSERT_DEBUG( maxLines > 0 )
530 mNumberOflinesLimit = maxLines;
534 std::size_t TextInput::GetNumberOfLinesLimit() const
536 return mNumberOflinesLimit;
539 std::size_t TextInput::GetNumberOfCharacters() const
541 return mStyledText.size();
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
547 mMaterialColor = color;
548 if ( mCustomMaterial )
550 mCustomMaterial.SetDiffuseColor( mMaterialColor );
551 mMeshData.SetMaterial( mCustomMaterial );
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
557 return mMaterialColor;
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
564 return mInputStartedSignalV2;
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
569 return mInputFinishedSignalV2;
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
574 return mCutAndPasteToolBarDisplayedV2;
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
579 return mStyleChangedSignalV2;
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
584 return mTextModifiedSignal;
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
589 return mMaxInputCharactersReachedSignalV2;
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
594 return mInputTextExceedBoundariesSignalV2;
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
599 Dali::BaseHandle handle( object );
601 bool connected( true );
602 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
604 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
606 textInput.InputStartedSignal().Connect( tracker, functor );
608 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
610 textInput.InputFinishedSignal().Connect( tracker, functor );
612 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
614 textInput.StyleChangedSignal().Connect( tracker, functor );
616 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
618 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
620 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
622 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
626 // signalName does not match any signal
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
637 // update line height before calculate the actual position.
642 if( setCursorOnTouchPoint )
644 // Sets the cursor position for the given touch point.
645 ReturnClosestIndex( touchPoint, mCursorPosition );
647 // Creates the grab handle.
648 if( IsGrabHandleEnabled() )
650 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
654 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657 ShowGrabHandleAndSetVisibility( true );
659 // Scrolls the text-view if needed.
660 if( IsScrollEnabled() )
662 ScrollTextViewToMakeCursorVisible( cursorPosition );
668 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
680 bool TextInput::IsEditable() const
682 return mEditModeActive;
685 void TextInput::SetEditOnTouch( bool editOnTouch )
687 mEditOnTouch = editOnTouch;
690 bool TextInput::IsEditOnTouch() const
695 void TextInput::SetTextSelectable( bool textSelectable )
697 mTextSelection = textSelectable;
700 bool TextInput::IsTextSelectable() const
702 return mTextSelection;
705 bool TextInput::IsTextSelected() const
707 return mHighlightMeshActor;
710 void TextInput::DeSelectText()
717 void TextInput::SetGrabHandleImage(Dali::Image image )
721 CreateGrabHandle(image);
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
727 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
731 mCursor.SetImage( image );
732 mCursor.SetNinePatchBorder( border );
736 Vector3 TextInput::GetSelectionHandleSize()
738 return DEFAULT_SELECTION_HANDLE_SIZE;
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
743 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
747 mCursorRTL.SetImage( image);
748 mCursorRTL.SetNinePatchBorder( border );
752 void TextInput::EnableGrabHandle(bool toggle)
754 // enables grab handle with will in turn de-activate magnifier
755 mGrabHandleEnabled = toggle;
758 bool TextInput::IsGrabHandleEnabled()
760 // if false then magnifier will be shown instead.
761 return mGrabHandleEnabled;
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
766 // Deprecated function. To be removed.
767 mIsSelectionHandleFlipEnabled = toggle;
770 bool TextInput::IsSelectionHandleFlipEnabled()
772 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
778 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
782 mSelectionHandleFlipMargin = margin;
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
787 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
793 const Vector4 boundary( originX,
795 originX + boundingRectangle.width,
796 originY + boundingRectangle.height );
798 mBoundingRectangleWorldCoordinates = boundary;
801 const Rect<float> TextInput::GetBoundingRectangle() const
803 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
805 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
808 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
815 return mSelectionHandleFlipMargin;
818 void TextInput::SetTextColor( const Vector4& color )
820 mDisplayedTextView.SetColor( color );
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
825 if( style != mInputStyle )
828 bool emitSignal = false;
830 // mask: modify style according to mask, if different emit signal.
831 const TextStyle oldInputStyle( mInputStyle );
833 // Copy the new style.
834 mInputStyle.Copy( style, mask );
836 // if style has changed, emit signal.
837 if( oldInputStyle != mInputStyle )
842 // Updates the line height accordingly with the input style.
845 // Changing font point size will require the cursor to be re-sized
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
857 if ( IsTextSelected() )
859 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
862 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
864 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
867 // Keeps the old style to be compared with the new one.
868 const TextStyle oldInputStyle( mInputStyle );
870 // Copy only those parameters from the style which are set in the mask.
871 mInputStyle.Copy( style, mask );
873 if( mInputStyle != oldInputStyle )
875 // Updates the line height accordingly with the input style.
878 EmitStyleChangedSignal();
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
885 if( !mStyledText.empty() )
887 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
891 TextStyle TextInput::GetStyleAtCursor() const
895 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
897 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898 style = mStyledText.at( mCursorPosition-1 ).mStyle;
904 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
906 Dali::Font defaultFont = Dali::Font::New();
907 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
916 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
918 if( position >= mStyledText.size() )
920 position = mStyledText.size() - 1;
923 return mStyledText.at( position ).mStyle;
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
928 mDisplayedTextView.SetTextAlignment( align );
929 mOverrideAutomaticAlignment = true;
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
934 mDisplayedTextView.SetLineJustification( justification );
935 mOverrideAutomaticAlignment = true;
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
940 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
945 return mDisplayedTextView.GetFadeBoundary();
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
950 return mDisplayedTextView.GetTextAlignment();
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
955 mDisplayedTextView.SetMultilinePolicy( policy );
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
960 return mDisplayedTextView.GetMultilinePolicy();
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
965 mDisplayedTextView.SetWidthExceedPolicy( policy );
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
970 return mDisplayedTextView.GetWidthExceedPolicy();
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
975 mDisplayedTextView.SetHeightExceedPolicy( policy );
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
980 return mDisplayedTextView.GetHeightExceedPolicy();
983 void TextInput::SetExceedEnabled( bool enable )
985 mExceedEnabled = enable;
988 bool TextInput::GetExceedEnabled() const
990 return mExceedEnabled;
993 void TextInput::SetBackground(Dali::Image image )
995 // TODO Should add this function and add public api to match.
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1005 switch( event.state )
1007 case KeyEvent::Down:
1009 return OnKeyDownEvent(event);
1015 return OnKeyUpEvent(event);
1027 void TextInput::OnKeyInputFocusGained()
1029 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1031 mEditModeActive = true;
1033 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1035 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1037 // Updates the line height accordingly with the input style.
1040 // Connect the signals to use in text input.
1041 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1044 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1047 GetTextLayoutInfo();
1050 SetCursorVisibility( true );
1051 StartCursorBlinkTimer();
1053 Toolkit::TextInput handle( GetOwner() );
1054 mInputStartedSignalV2.Emit( handle );
1056 ImfManager imfManager = ImfManager::Get();
1060 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1062 // Notify that the text editing start.
1063 imfManager.Activate();
1065 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066 imfManager.SetRestoreAferFocusLost( true );
1068 imfManager.SetCursorPosition( mCursorPosition );
1069 imfManager.NotifyCursorPosition();
1072 mClipboard = Clipboard::Get(); // Store handle to clipboard
1074 // Now in edit mode we can accept string to paste from clipboard
1075 if( Adaptor::IsAvailable() )
1077 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1080 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1085 void TextInput::OnKeyInputFocusLost()
1087 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1091 // If key input focus is lost, it removes the
1092 // underline from the last pre-edit text.
1093 RemovePreEditStyle();
1094 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1095 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1099 ImfManager imfManager = ImfManager::Get();
1102 // The text editing is finished. Therefore the imf manager don't have restore activation.
1103 imfManager.SetRestoreAferFocusLost( false );
1105 // Notify that the text editing finish.
1106 imfManager.Deactivate();
1108 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1110 // Disconnect signal used the text input.
1111 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1113 Toolkit::TextInput handle( GetOwner() );
1114 mInputFinishedSignalV2.Emit( handle );
1115 mEditModeActive = false;
1116 mPreEditFlag = false;
1118 SetCursorVisibility( false );
1119 StopCursorBlinkTimer();
1121 ShowGrabHandleAndSetVisibility( false );
1124 // No longer in edit mode so do not want to receive string from clipboard
1125 if( Adaptor::IsAvailable() )
1127 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1130 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1132 Clipboard clipboard = Clipboard::Get();
1136 clipboard.HideClipboard();
1141 void TextInput::OnControlStageConnection()
1143 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1145 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1147 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1151 void TextInput::CreateActiveLayer()
1153 Actor self = Self();
1154 mActiveLayer = Layer::New();
1155 mActiveLayer.SetName ( "ActiveLayerActor" );
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1174 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1176 Actor self = Self();
1177 self.Add( mCursor );
1178 self.Add( mCursorRTL );
1180 mCursorVisibility = false;
1182 CreateActiveLayer(); // todo move this so layer only created when needed.
1184 // Assign names to image actors
1185 mCursor.SetName("mainCursor");
1186 mCursorRTL.SetName("rtlCursor");
1189 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1191 mDisplayedTextView.SetSize( targetSize );
1192 GetTextLayoutInfo();
1193 mActiveLayer.SetSize(targetSize);
1196 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1198 Relayout( mDisplayedTextView, size, container );
1199 Relayout( mPopupPanel.GetRootActor(), size, container );
1201 GetTextLayoutInfo();
1206 Vector3 TextInput::GetNaturalSize()
1208 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1210 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1212 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1213 naturalSize.height = mLineHeight;
1219 float TextInput::GetHeightForWidth( float width )
1221 float height = mDisplayedTextView.GetHeightForWidth( width );
1223 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1225 // If the height is zero, it means there is no text. Let's return the cursor height.
1226 height = mLineHeight;
1232 /*end of Virtual methods from parent*/
1234 // Private Internal methods
1236 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1238 switch (gesture.state)
1240 case Gesture::Started:
1241 // fall through so code not duplicated
1242 case Gesture::Continuing:
1244 if (actor == mGrabArea)
1246 SetCursorVisibility( true );
1247 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1248 MoveGrabHandle( gesture.displacement );
1249 HidePopup(); // Do not show popup whilst handle is moving
1251 else if (actor == mHandleOneGrabArea)
1253 // the displacement in PanGesture is affected by the actor's rotation.
1254 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1255 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1257 MoveSelectionHandle( HandleOne, gesture.displacement );
1259 mState = StateDraggingHandle;
1262 else if (actor == mHandleTwoGrabArea)
1264 // the displacement in PanGesture is affected by the actor's rotation.
1265 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1268 MoveSelectionHandle( HandleTwo, gesture.displacement );
1270 mState = StateDraggingHandle;
1276 case Gesture::Finished:
1278 // Revert back to non-pressed selection handle images
1279 if (actor == mGrabArea)
1281 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1282 SetCursorVisibility( true );
1283 SetUpPopupSelection();
1286 if (actor == mHandleOneGrabArea)
1288 // the displacement in PanGesture is affected by the actor's rotation.
1289 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1290 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1292 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1294 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1296 ShowPopupCutCopyPaste();
1298 if (actor == mHandleTwoGrabArea)
1300 // the displacement in PanGesture is affected by the actor's rotation.
1301 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1302 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1304 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1306 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1308 ShowPopupCutCopyPaste();
1317 // Stop the flashing animation so easy to see when moved.
1318 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1320 if (touch.GetPoint(0).state == TouchPoint::Down)
1322 SetCursorVisibility( true );
1323 StopCursorBlinkTimer();
1325 else if (touch.GetPoint(0).state == TouchPoint::Up)
1327 SetCursorVisibility( true );
1328 StartCursorBlinkTimer();
1333 // selection handle one
1334 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1336 if (touch.GetPoint(0).state == TouchPoint::Down)
1338 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1340 else if (touch.GetPoint(0).state == TouchPoint::Up)
1342 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1347 // selection handle two
1348 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1350 if (touch.GetPoint(0).state == TouchPoint::Down)
1352 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1354 else if (touch.GetPoint(0).state == TouchPoint::Up)
1356 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1361 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1363 // If text exists then select nearest word.
1364 if ( !mStyledText.empty())
1368 ShowGrabHandleAndSetVisibility( false );
1373 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1374 // converts the pre-edit word being displayed to a committed word.
1375 if ( !mUnderlinedPriorToPreEdit )
1378 style.SetUnderline( false );
1379 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1381 mPreEditFlag = false;
1382 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1383 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1384 PreEditReset( false );
1386 mCursorPosition = 0;
1388 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1389 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1391 std::size_t start = 0;
1392 std::size_t end = 0;
1393 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1395 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1397 ImfManager imfManager = ImfManager::Get();
1400 imfManager.SetCursorPosition ( mCursorPosition );
1401 imfManager.NotifyCursorPosition();
1404 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1406 SelectText( start, end );
1407 ShowPopupCutCopyPaste();
1411 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1412 HidePopup( false ); // Hide popup with setting to do auto show.
1413 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1417 else if ( mClipboard && mClipboard.NumberOfItems() )
1419 ShowPopupCutCopyPaste();
1422 // If no text and clipboard empty then do nothing
1425 // TODO: Change the function name to be more general.
1426 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1428 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1429 , (mEditOnTouch)?"true":"false"
1430 , (mEditModeActive)?"true":"false");
1432 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1437 if( mGrabArea == actor )
1439 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1441 SetUpPopupSelection();
1451 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1453 // Initially don't create the grab handle.
1454 bool createGrabHandle = false;
1456 if ( !mEditModeActive )
1458 // update line height before calculate the actual position.
1461 // Only start edit mode if TextInput configured to edit on touch
1464 // Set the initial cursor position in the tap point.
1465 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1471 // Show the keyboard if it was hidden.
1472 if (!VirtualKeyboard::IsVisible())
1474 VirtualKeyboard::Show();
1477 // Reset keyboard as tap event has occurred.
1478 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1479 PreEditReset( true );
1481 GetTextLayoutInfo();
1483 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1485 // 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.
1487 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1489 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1491 // Notify keyboard so it can 're-capture' word for predictive text.
1492 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1493 ImfManager imfManager = ImfManager::Get();
1496 imfManager.SetCursorPosition ( mCursorPosition );
1497 imfManager.NotifyCursorPosition();
1499 const TextStyle oldInputStyle( mInputStyle );
1501 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1505 // Create the grab handle.
1506 // Grab handle is created later.
1507 createGrabHandle = true;
1509 if( oldInputStyle != mInputStyle )
1511 // Updates the line height accordingly with the input style.
1514 EmitStyleChangedSignal();
1519 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1520 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1521 // otherwise the Grab handle will be shown when selecting.
1522 if ( createGrabHandle && IsGrabHandleEnabled() )
1524 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1528 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1529 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1530 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1531 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1536 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1538 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1540 // Ignore longpress if in selection mode already
1541 if( mHighlightMeshActor )
1546 if(longPress.state == Dali::Gesture::Started)
1548 // Start edit mode on long press
1549 if ( !mEditModeActive )
1554 // If text exists then select nearest word.
1555 if ( !mStyledText.empty())
1559 ShowGrabHandleAndSetVisibility( false );
1564 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1565 // converts the pre-edit word being displayed to a committed word.
1566 if ( !mUnderlinedPriorToPreEdit )
1569 style.SetUnderline( false );
1570 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1572 mPreEditFlag = false;
1573 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1574 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1575 PreEditReset( false );
1577 mCursorPosition = 0;
1579 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1580 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1582 std::size_t start = 0;
1583 std::size_t end = 0;
1584 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1586 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1588 ImfManager imfManager = ImfManager::Get();
1591 imfManager.SetCursorPosition ( mCursorPosition );
1592 imfManager.NotifyCursorPosition();
1595 SelectText( start, end );
1598 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1599 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1601 ShowPopupCutCopyPaste();
1606 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1608 const Text clipboardText( notifier.GetContent() );
1609 PasteText( clipboardText );
1611 SetCursorVisibility( true );
1612 StartCursorBlinkTimer();
1614 ShowGrabHandleAndSetVisibility( false );
1620 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1622 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1624 const std::string& name = button.GetName();
1626 if(name == TextInputPopup::OPTION_SELECT_WORD)
1628 std::size_t start = 0;
1629 std::size_t end = 0;
1630 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1632 SelectText( start, end );
1634 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1636 SetCursorVisibility(false);
1637 StopCursorBlinkTimer();
1639 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1640 std::size_t start = 0;
1642 SelectText( start, end );
1644 else if(name == TextInputPopup::OPTION_CUT)
1646 bool ret = CopySelectedTextToClipboard();
1650 DeleteHighlightedText( true );
1654 SetCursorVisibility( true );
1655 StartCursorBlinkTimer();
1659 else if(name == TextInputPopup::OPTION_COPY)
1661 CopySelectedTextToClipboard();
1665 SetCursorVisibility( true );
1666 StartCursorBlinkTimer();
1670 else if(name == TextInputPopup::OPTION_PASTE)
1672 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1674 PasteText(retrievedString);
1676 SetCursorVisibility( true );
1677 StartCursorBlinkTimer();
1679 ShowGrabHandleAndSetVisibility( false );
1683 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1685 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1686 // Hence pass the false parameter for signalFinished.
1687 HidePopup( true, false );
1688 mClipboard.ShowClipboard();
1694 bool TextInput::OnCursorBlinkTimerTick()
1697 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1698 if ( mCursorRTLEnabled )
1700 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1702 mCursorBlinkStatus = !mCursorBlinkStatus;
1707 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1709 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1711 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1712 if(mHighlightMeshActor && mState == StateEdit)
1714 ShowPopupCutCopyPaste();
1718 //FIXME this routine needs to be re-written as it contains too many branches.
1719 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1721 std::string keyName = event.keyPressedName;
1722 std::string keyString = event.keyPressed;
1724 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1726 // Do not consume "Tab" and "Escape" keys.
1727 if(keyName == "Tab" || keyName == "Escape")
1729 // Escape key to end the edit mode
1735 HidePopup(); // If Pop-up shown then hides it as editing text.
1737 // Update Flag, indicates whether to update the text-input contents or not.
1738 // Any key stroke that results in a visual change of the text-input should
1739 // set this flag to true.
1742 // Whether to scroll text to cursor position.
1743 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1744 bool scroll = false;
1746 if (keyName == "Return")
1748 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1750 bool preEditFlagPreviouslySet( mPreEditFlag );
1752 // replaces highlighted text with new line
1753 DeleteHighlightedText( false );
1755 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1757 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1758 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1761 mCommitByKeyInput = true;
1764 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1765 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1767 mPreEditFlag = true;
1768 mIgnoreCommitFlag = false;
1778 else if ( keyName == "space" )
1780 if ( mHighlightMeshActor )
1782 // Some text is selected so erase it before adding space.
1783 DeleteHighlightedText( true );
1787 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1789 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1790 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1793 mCommitByKeyInput = true;
1798 else if (keyName == "BackSpace")
1800 if ( mHighlightMeshActor )
1802 // Some text is selected so erase it
1803 DeleteHighlightedText( true );
1808 if ( mCursorPosition > 0 )
1810 DeleteCharacter( mCursorPosition );
1816 else if (keyName == "Right")
1821 else if (keyName == "Left")
1823 AdvanceCursor(true);
1826 else // event is a character
1828 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1829 if ( !keyString.empty() )
1831 // replaces highlighted text with new character
1832 DeleteHighlightedText( false );
1834 // Received key String
1835 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1841 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1842 // as this is a costly operation.
1848 if(update || scroll)
1850 if( IsScrollEnabled() )
1852 // Calculates the new cursor position (in actor coordinates)
1853 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1855 ScrollTextViewToMakeCursorVisible( cursorPosition );
1862 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1864 std::string keyName = event.keyPressedName;
1865 std::string keyString = event.keyPressed;
1867 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1869 // The selected text become deselected when the key code is DALI_KEY_BACK.
1870 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1879 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1881 // Updates the stored scroll position.
1882 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1884 const Vector3& controlSize = GetControlSize();
1885 Size cursorSize( CURSOR_THICKNESS, 0.f );
1887 // Updates the cursor and grab handle position and visibility.
1888 if( mGrabHandle || mCursor )
1890 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1891 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1893 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1895 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1899 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1900 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1905 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1906 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1910 // Updates the selection handles and highlighted text position and visibility.
1911 if( mSelectionHandleOne && mSelectionHandleTwo )
1913 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1914 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1915 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1916 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1917 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1918 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1920 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1921 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1923 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1924 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1925 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1926 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1928 if( mHighlightMeshActor )
1930 mHighlightMeshActor.SetVisible( true );
1936 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1938 // Scroll the text to make the cursor visible.
1939 const Size cursorSize( CURSOR_THICKNESS,
1940 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1942 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1944 const Vector3& controlSize = GetControlSize();
1946 // Calculates the new scroll position.
1947 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1948 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1950 scrollOffset.x += cursorPosition.x;
1953 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1955 scrollOffset.y += cursorPosition.y;
1958 // Sets the new scroll position.
1959 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1960 SetScrollPosition( scrollOffset );
1963 void TextInput::StartScrollTimer()
1967 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1968 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1971 if( !mScrollTimer.IsRunning() )
1973 mScrollTimer.Start();
1977 void TextInput::StopScrollTimer()
1981 mScrollTimer.Stop();
1985 bool TextInput::OnScrollTimerTick()
1987 // TODO: need to set the new style accordingly the new handle position.
1989 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1991 // nothing to do if all handles are invisible or doesn't exist.
1997 // Choose between the grab handle or the selection handles.
1998 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1999 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2000 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2002 std::size_t newCursorPosition = 0;
2003 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2005 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2006 // the new selection handle's position needs to be different of the other one.
2007 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2008 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2009 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2011 if( differentSelectionHandles )
2013 handlePosition = newCursorPosition;
2015 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2017 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2019 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2020 scrollPosition += scrollDelta;
2021 SetScrollPosition( scrollPosition );
2023 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2028 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2031 actualHandlePosition.x += mScrollDisplacement.x;
2032 actualHandlePosition.y += mScrollDisplacement.y;
2037 // Public Internal Methods (public for testing purpose)
2039 void TextInput::SetUpTouchEvents()
2041 if ( !mTapDetector )
2043 mTapDetector = TapGestureDetector::New();
2044 // Attach the actors and connect the signal
2045 mTapDetector.Attach(Self());
2047 // As contains children which may register for tap the default control detector is not used.
2048 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2051 if ( !mDoubleTapDetector )
2053 mDoubleTapDetector = TapGestureDetector::New();
2054 mDoubleTapDetector.SetTapsRequired( 2 );
2055 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2057 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2058 // so that we do not, unnecessarily, have a double tap request all the time
2061 if ( !mPanGestureDetector )
2063 mPanGestureDetector = PanGestureDetector::New();
2064 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2067 if ( !mLongPressDetector )
2069 mLongPressDetector = LongPressGestureDetector::New();
2070 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2071 mLongPressDetector.Attach(Self());
2075 void TextInput::CreateTextViewActor()
2077 mDisplayedTextView = Toolkit::TextView::New();
2078 mDisplayedTextView.SetName( "DisplayedTextView ");
2079 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2080 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2081 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2082 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2083 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2084 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2085 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2086 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2087 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2088 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2090 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2092 Self().Add( mDisplayedTextView );
2095 // Start a timer to initiate, used by the cursor to blink.
2096 void TextInput::StartCursorBlinkTimer()
2098 if ( !mCursorBlinkTimer )
2100 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2101 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2104 if ( !mCursorBlinkTimer.IsRunning() )
2106 mCursorBlinkTimer.Start();
2110 // Start a timer to initiate, used by the cursor to blink.
2111 void TextInput::StopCursorBlinkTimer()
2113 if ( mCursorBlinkTimer )
2115 mCursorBlinkTimer.Stop();
2119 void TextInput::StartEditMode()
2121 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2123 if(!mEditModeActive)
2128 if ( mDoubleTapDetector )
2130 mDoubleTapDetector.Attach( Self() );
2134 void TextInput::EndEditMode()
2136 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2138 ClearKeyInputFocus();
2140 if ( mDoubleTapDetector )
2142 mDoubleTapDetector.Detach( Self() );
2146 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2148 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2150 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2152 style.SetUnderline( true );
2153 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2157 void TextInput::RemovePreEditStyle()
2159 if ( !mUnderlinedPriorToPreEdit )
2162 style.SetUnderline( false );
2163 SetActiveStyle( style, TextStyle::UNDERLINE );
2167 // IMF related methods
2170 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2172 bool update( false );
2173 bool preeditResetRequired ( false );
2175 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2177 HidePopup(); // If Pop-up shown then hides it as editing text.
2180 switch ( imfEvent.eventName )
2182 case ImfManager::PREEDIT:
2184 mIgnoreFirstCommitFlag = false;
2186 // 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
2187 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2189 // replaces highlighted text with new character
2190 DeleteHighlightedText( false );
2193 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2195 if( IsScrollEnabled() )
2197 // Calculates the new cursor position (in actor coordinates)
2198 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2199 ScrollTextViewToMakeCursorVisible( cursorPosition );
2206 case ImfManager::COMMIT:
2208 if( mIgnoreFirstCommitFlag )
2210 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2211 mIgnoreFirstCommitFlag = false;
2215 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2217 // 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
2218 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2220 // replaces highlighted text with new character
2221 DeleteHighlightedText( false );
2224 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2225 // not needed, one such scenario is when the pre-edit word is too long to fit.
2226 if ( !mIgnoreCommitFlag )
2228 update = CommitReceived( imfEvent.predictiveString );
2232 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2238 if( IsScrollEnabled() )
2240 // Calculates the new cursor position (in actor coordinates)
2241 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2243 ScrollTextViewToMakeCursorVisible( cursorPosition );
2248 case ImfManager::DELETESURROUNDING:
2250 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2251 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2253 mPreEditFlag = false;
2255 std::size_t toDelete = 0;
2256 std::size_t numberOfCharacters = 0;
2258 if( mHighlightMeshActor )
2260 // delete highlighted text.
2261 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2262 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2266 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2268 toDelete = mCursorPosition + imfEvent.cursorOffset;
2270 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2272 numberOfCharacters = mStyledText.size() - toDelete;
2276 numberOfCharacters = imfEvent.numberOfChars;
2279 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2280 DeleteRange( toDelete, numberOfCharacters );
2282 mCursorPosition = toDelete;
2283 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2287 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2290 case ImfManager::GETSURROUNDING:
2292 // 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
2293 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2294 if (! ( mHighlightMeshActor || mSelectingText ) )
2296 std::string text( GetText() );
2297 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2299 imfManager.SetCursorPosition( mCursorPosition );
2300 imfManager.SetSurroundingText( text );
2303 if( 0 != mNumberOfSurroundingCharactersDeleted )
2305 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2306 mNumberOfSurroundingCharactersDeleted = 0;
2308 if( mStyledText.empty() )
2310 // Styled text is empty, so set the placeholder text.
2311 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2312 mPlaceHolderSet = true;
2317 case ImfManager::VOID:
2319 DALI_ASSERT_DEBUG( false );
2323 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2325 return callbackData;
2328 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2330 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2332 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2333 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2335 bool preeditResetRequest ( false );
2337 if( mPreEditFlag ) // Already in pre-edit state.
2339 if( mStyledText.size() >= mMaxStringLength )
2341 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2342 // Cannot fit these characters into field, clear pre-edit.
2343 if ( !mUnderlinedPriorToPreEdit )
2346 style.SetUnderline( false );
2347 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2349 mIgnoreCommitFlag = true;
2350 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2351 mPreEditFlag = false;
2352 EmitMaxInputCharactersReachedSignal();
2356 // delete existing pre-edit string
2357 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2359 // Store new pre-edit string
2360 mPreEditString.SetText( keyString );
2362 if ( keyString.empty() )
2364 mPreEditFlag = false;
2365 mCursorPosition = mPreEditStartPosition;
2367 if( mStyledText.empty() )
2369 // Styled text is empty, so set the placeholder text.
2370 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2371 mPlaceHolderSet = true;
2375 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2377 GetTextLayoutInfo();
2382 // Insert new pre-edit string. InsertAt updates the size and position table.
2383 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2384 // 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.
2385 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2386 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2387 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2390 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2394 else // mPreEditFlag not set
2396 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2398 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2399 // new pre-edit so move into pre-edit state by setting flag
2400 mPreEditFlag = true;
2401 mPreEditString.SetText( keyString ); // store new pre-edit string
2402 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2403 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2404 // 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.
2405 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2406 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2407 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2408 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2414 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2418 return preeditResetRequest;
2421 bool TextInput::CommitReceived(const std::string& keyString )
2423 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2424 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2426 bool update( false );
2428 RemovePreEditStyle();
2430 const std::size_t styledTextSize( mStyledText.size() );
2431 if( styledTextSize >= mMaxStringLength )
2433 // Cannot fit these characters into field, clear pre-edit.
2436 mIgnoreCommitFlag = true;
2437 mPreEditFlag = false;
2439 EmitMaxInputCharactersReachedSignal();
2445 // delete existing pre-edit string
2446 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2447 mPreEditFlag = false;
2449 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2450 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2452 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2454 // No need to update cursor position as Cursor location given by touch.
2455 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2456 mPreserveCursorPosition = false;
2460 // Cursor not set by touch so needs to be re-positioned to input more text
2461 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2463 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2464 if ( mCommitByKeyInput )
2466 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2467 mCommitByKeyInput = false;
2473 if ( mSelectTextOnCommit )
2475 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2480 else // mPreEditFlag not set
2482 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2484 if( mStyledText.empty() && mPlaceHolderSet )
2486 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2487 mDisplayedTextView.SetText( "" );
2488 mNumberOfSurroundingCharactersDeleted = 0;
2489 mPlaceHolderSet = false;
2491 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2493 mNumberOfSurroundingCharactersDeleted = 0;
2498 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2503 mSelectTextOnCommit = false;
2505 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2506 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2511 // End of IMF related methods
2513 std::size_t TextInput::DeletePreEdit()
2515 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2517 DALI_ASSERT_DEBUG( mPreEditFlag );
2519 const std::size_t preEditStringLength = mPreEditString.GetLength();
2520 const std::size_t styledTextSize = mStyledText.size();
2522 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2524 // Prevents erase items outside mStyledText bounds.
2525 if( mPreEditStartPosition > styledTextSize )
2527 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2528 mPreEditStartPosition = styledTextSize;
2531 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2533 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2534 endPosition = styledTextSize;
2537 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2539 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2540 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2542 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2544 return preEditStringLength;
2547 void TextInput::PreEditReset( bool preserveCursorPosition )
2549 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2550 preserveCursorPosition, mCursorPosition);
2552 // 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.
2553 mPreserveCursorPosition = preserveCursorPosition;
2555 // Reset incase we are in a pre-edit state.
2556 ImfManager imfManager = ImfManager::Get();
2559 imfManager.Reset(); // Will trigger a commit message
2563 void TextInput::CursorUpdate()
2567 ImfManager imfManager = ImfManager::Get();
2570 std::string text( GetText() );
2571 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2572 imfManager.SetCursorPosition ( mCursorPosition );
2573 imfManager.NotifyCursorPosition();
2577 /* Delete highlighted characters redisplay*/
2578 void TextInput::DeleteHighlightedText( bool inheritStyle )
2580 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2582 if( mHighlightMeshActor )
2584 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2586 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2587 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2589 // Get the styled text of the characters to be deleted as it may be needed if
2590 // the "exceed the text-input's boundaries" option is disabled.
2591 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2593 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2595 mStyledText.erase( start, end ); // erase range of characters
2597 // Remove text from TextView.
2599 if( mStyledText.empty() )
2601 // Styled text is empty, so set the placeholder text.
2602 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2603 mPlaceHolderSet = true;
2607 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2609 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2611 // It may happen than after removing a white space or a new line character,
2612 // two words merge, this new word could be big enough to not fit in its
2613 // current line, so moved to the next one, and make some part of the text to
2614 // exceed the text-input's boundary.
2615 if( !mExceedEnabled )
2617 // Get the new text layout after removing some characters.
2618 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2620 // Get text-input's size.
2621 const Vector3& size = GetControlSize();
2623 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2624 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2626 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2628 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2629 styledCharactersToDelete.begin(),
2630 styledCharactersToDelete.end() );
2634 GetTextLayoutInfo();
2642 const TextStyle oldInputStyle( mInputStyle );
2644 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2646 if( oldInputStyle != mInputStyle )
2648 // Updates the line height accordingly with the input style.
2651 EmitStyleChangedSignal();
2657 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2659 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2660 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2662 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2665 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2667 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2668 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2670 mStyledText.erase(itStart, itEnd);
2672 // update the selection handles if they are visible.
2673 if( mHighlightMeshActor )
2675 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2676 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2678 if( minHandle >= start + ncharacters )
2680 minHandle -= ncharacters;
2682 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2687 if( maxHandle >= start + ncharacters )
2689 maxHandle -= ncharacters;
2691 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2697 // 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.
2700 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2702 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2703 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2704 // Mean we do not re-draw the text more than we have too.
2707 /* Delete character at current cursor position and redisplay*/
2708 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2710 // Ensure positionToDelete is not out of bounds.
2711 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2712 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2713 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2715 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2718 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2720 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2722 // Get the styled text of the character to be deleted as it may be needed if
2723 // the "exceed the text-input's boundaries" option is disabled.
2724 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2726 mStyledText.erase(it); // erase the character left of positionToDelete
2728 if( mStyledText.empty() )
2730 // Styled text is empty, so set the placeholder text.
2731 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2732 mPlaceHolderSet = true;
2736 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2738 const Character characterToDelete = styledCharacterToDelete.mText[0];
2740 // It may happen than after removing a white space or a new line character,
2741 // two words merge, this new word could be big enough to not fit in its
2742 // current line, so moved to the next one, and make some part of the text to
2743 // exceed the text-input's boundary.
2744 if( !mExceedEnabled )
2746 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2748 // Get the new text layout after removing one character.
2749 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2751 // Get text-input's size.
2752 const Vector3& size = GetControlSize();
2754 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2755 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2757 MarkupProcessor::StyledTextArray array;
2758 array.push_back( styledCharacterToDelete );
2759 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2761 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2766 GetTextLayoutInfo();
2768 ShowGrabHandleAndSetVisibility( false );
2770 mCursorPosition = positionToDelete -1;
2772 const TextStyle oldInputStyle( mInputStyle );
2774 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2776 if( oldInputStyle != mInputStyle )
2778 // Updates the line height accordingly with the input style.
2781 EmitStyleChangedSignal();
2786 /*Insert new character into the string and (optionally) redisplay text-input*/
2787 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2789 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2791 // Ensure insertionPosition is not out of bounds.
2792 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2794 bool textExceedsMaximunNumberOfCharacters = false;
2795 bool textExceedsBoundary = false;
2796 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2798 ShowGrabHandleAndSetVisibility( false );
2800 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2804 mIgnoreCommitFlag = true;
2805 mPreEditFlag = false;
2806 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2807 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2810 if( textExceedsMaximunNumberOfCharacters )
2812 EmitMaxInputCharactersReachedSignal();
2815 if( textExceedsBoundary )
2817 EmitInputTextExceedsBoundariesSignal();
2818 PreEditReset( false );
2822 return insertedStringLength;
2825 ImageActor TextInput::CreateCursor( const Vector4& color)
2828 cursor = CreateSolidColorActor(color);
2829 cursor.SetName( "Cursor" );
2831 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2832 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2833 cursor.SetVisible(false);
2838 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2840 // As cursor is not moving due to grab handle, handle should be hidden.
2841 ShowGrabHandleAndSetVisibility( false );
2843 bool cursorPositionChanged = false;
2846 if ( mCursorPosition >= places )
2848 mCursorPosition = mCursorPosition - places;
2849 cursorPositionChanged = true;
2854 if ((mCursorPosition + places) <= mStyledText.size())
2856 mCursorPosition = mCursorPosition + places;
2857 cursorPositionChanged = true;
2861 if( cursorPositionChanged )
2863 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2865 const TextStyle oldInputStyle( mInputStyle );
2866 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2870 if( oldInputStyle != mInputStyle )
2872 // Updates the line height accordingly with the input style.
2875 EmitStyleChangedSignal();
2878 ImfManager imfManager = ImfManager::Get();
2881 imfManager.SetCursorPosition ( mCursorPosition );
2882 imfManager.NotifyCursorPosition();
2887 void TextInput::DrawCursor(const std::size_t nthChar)
2889 // Get height of cursor and set its size
2890 Size size( CURSOR_THICKNESS, 0.0f );
2891 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2893 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2897 // Measure Font so know how big text will be if no initial text to measure.
2898 size.height = mLineHeight;
2901 mCursor.SetSize(size);
2903 // If the character is italic then the cursor also tilts.
2904 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2906 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2908 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2910 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2911 bool altPositionValid; // Alternate cursor validity flag.
2912 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2913 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2915 SetAltCursorEnabled( altPositionValid );
2917 if(!altPositionValid)
2919 mCursor.SetPosition( position + UI_OFFSET );
2923 size.height *= 0.5f;
2924 mCursor.SetSize(size);
2925 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2927 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2928 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2929 size.height = rowSize.height * 0.5f;
2930 mCursorRTL.SetSize(size);
2931 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2934 if( IsScrollEnabled() )
2936 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2937 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2942 void TextInput::SetAltCursorEnabled( bool enabled )
2944 mCursorRTLEnabled = enabled;
2945 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2948 void TextInput::SetCursorVisibility( bool visible )
2950 mCursorVisibility = visible;
2951 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2952 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2955 void TextInput::CreateGrabHandle( Dali::Image image )
2961 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2965 mGrabHandleImage = image;
2968 mGrabHandle = ImageActor::New(mGrabHandleImage);
2969 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2970 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2972 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2974 ShowGrabHandleAndSetVisibility( false );
2976 CreateGrabArea( mGrabHandle );
2978 mActiveLayer.Add(mGrabHandle);
2982 void TextInput::CreateGrabArea( Actor& parent )
2984 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2985 mGrabArea.SetName( "GrabArea" );
2986 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2987 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2988 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2989 mTapDetector.Attach( mGrabArea );
2990 mPanGestureDetector.Attach( mGrabArea );
2991 mLongPressDetector.Attach( mGrabArea );
2993 parent.Add(mGrabArea);
2996 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2998 Vector3 actualHandlePosition;
3002 mActualGrabHandlePosition.x += displacement.x;
3003 mActualGrabHandlePosition.y += displacement.y;
3005 // Grab handle should jump to the nearest character and take cursor with it
3006 std::size_t newCursorPosition = 0;
3007 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3009 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3011 bool handleVisible = true;
3013 if( IsScrollEnabled() )
3015 const Vector3 controlSize = GetControlSize();
3016 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3017 // Scrolls the text if the handle is not in a visible position
3018 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3025 mCurrentHandlePosition = actualHandlePosition;
3026 mScrollDisplacement = Vector2::ZERO;
3030 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3032 mScrollDisplacement.x = -SCROLL_SPEED;
3034 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3036 mScrollDisplacement.x = SCROLL_SPEED;
3038 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3040 mScrollDisplacement.y = -SCROLL_SPEED;
3042 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3044 mScrollDisplacement.y = SCROLL_SPEED;
3050 if( handleVisible && // Only redraw cursor and do updates if position changed
3051 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3053 mCursorPosition = newCursorPosition;
3055 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3057 const TextStyle oldInputStyle( mInputStyle );
3059 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3061 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3063 if( oldInputStyle != mInputStyle )
3065 // Updates the line height accordingly with the input style.
3068 EmitStyleChangedSignal();
3073 return actualHandlePosition;
3076 void TextInput::ShowGrabHandle( bool visible )
3078 if ( IsGrabHandleEnabled() )
3082 mGrabHandle.SetVisible( mGrabHandleVisibility );
3084 StartMonitoringStageForTouch();
3088 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3090 mGrabHandleVisibility = visible;
3091 ShowGrabHandle( visible );
3094 // Callbacks connected to be Property notifications for Boundary checking.
3096 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3098 mIsSelectionHandleOneFlipped = true;
3099 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3100 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3103 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3105 mIsSelectionHandleOneFlipped = false;
3106 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3107 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3110 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3112 mIsSelectionHandleTwoFlipped = true;
3113 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3114 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3117 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3119 mIsSelectionHandleTwoFlipped = false;
3120 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3121 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3124 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3125 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3127 mSelectionHandleOne.SetOpacity(0.0f);
3130 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3132 mSelectionHandleOne.SetOpacity(1.0f);
3135 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3137 mSelectionHandleTwo.SetOpacity(0.0f);
3140 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3142 mSelectionHandleTwo.SetOpacity(1.0f);
3145 // End of Callbacks connected to be Property notifications for Boundary checking.
3147 void TextInput::SetUpHandlePropertyNotifications()
3149 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3151 Vector3 handlesize = GetSelectionHandleSize();
3153 // Exceeding horizontal boundary
3154 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3155 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3157 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3158 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3160 // Within horizontal boundary
3161 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3162 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3164 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3165 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3167 // Exceeding vertical boundary
3168 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3169 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3170 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3171 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3173 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3174 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3175 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3176 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3178 // Within vertical boundary
3179 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3180 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3181 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3182 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3184 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3185 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3186 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3187 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3190 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3192 mSelectionHandleOnePosition = start;
3193 mSelectionHandleTwoPosition = end;
3195 if ( !mSelectionHandleOne )
3197 // create normal and pressed images
3198 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3199 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3201 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3202 mSelectionHandleOne.SetName("SelectionHandleOne");
3203 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3204 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3205 mIsSelectionHandleOneFlipped = false;
3206 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3208 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3209 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3211 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3212 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3214 mTapDetector.Attach( mHandleOneGrabArea );
3215 mPanGestureDetector.Attach( mHandleOneGrabArea );
3217 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3219 mSelectionHandleOne.Add( mHandleOneGrabArea );
3220 mActiveLayer.Add( mSelectionHandleOne );
3223 if ( !mSelectionHandleTwo )
3225 // create normal and pressed images
3226 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3227 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3229 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3230 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3231 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3232 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3233 mIsSelectionHandleTwoFlipped = false;
3234 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3236 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3237 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3238 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3239 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3241 mTapDetector.Attach( mHandleTwoGrabArea );
3242 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3244 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3246 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3248 mActiveLayer.Add( mSelectionHandleTwo );
3251 SetUpHandlePropertyNotifications();
3253 // update table as text may have changed.
3254 GetTextLayoutInfo();
3256 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3257 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3259 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3260 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3262 // Calculates and set the visibility if the scroll mode is enabled.
3263 bool isSelectionHandleOneVisible = true;
3264 bool isSelectionHandleTwoVisible = true;
3265 if( IsScrollEnabled() )
3267 const Vector3& controlSize( GetControlSize() );
3268 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3269 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3270 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3271 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3274 CreateHighlight(); // function will only create highlight if not already created.
3277 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3279 Vector3 actualHandlePosition;
3281 if ( mSelectionHandleOne && mSelectionHandleTwo )
3283 const Vector3& controlSize = GetControlSize();
3285 Size cursorSize( CURSOR_THICKNESS, 0.f );
3287 // Get a reference of the wanted selection handle (handle one or two).
3288 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3290 // Get a reference for the current position of the handle and a copy of its pair
3291 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3292 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3294 // Get a handle of the selection handle actor
3295 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3297 // Selection handles should jump to the nearest character
3298 std::size_t newHandlePosition = 0;
3299 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3301 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3303 bool handleVisible = true;
3305 if( IsScrollEnabled() )
3307 mCurrentSelectionId = handleId;
3309 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3310 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3311 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3318 mCurrentSelectionHandlePosition = actualHandlePosition;
3319 mScrollDisplacement = Vector2::ZERO;
3323 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3325 mScrollDisplacement.x = -SCROLL_SPEED;
3327 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3329 mScrollDisplacement.x = SCROLL_SPEED;
3331 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3333 mScrollDisplacement.y = -SCROLL_SPEED;
3335 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3337 mScrollDisplacement.y = SCROLL_SPEED;
3343 if ( handleVisible && // Ensure the handle is visible.
3344 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3345 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3347 currentSelectionHandlePosition = newHandlePosition;
3349 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3350 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3354 if ( handleId == HandleOne )
3356 const TextStyle oldInputStyle( mInputStyle );
3358 // Set Active Style to that of first character in selection
3359 if( mSelectionHandleOnePosition < mStyledText.size() )
3361 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3364 if( oldInputStyle != mInputStyle )
3366 // Updates the line height accordingly with the input style.
3369 EmitStyleChangedSignal();
3375 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3378 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3381 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3382 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3384 if ( selectionHandleActor )
3386 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3387 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3388 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3390 if( IsScrollEnabled() )
3392 const Size cursorSize( CURSOR_THICKNESS,
3393 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3394 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3396 GetControlSize() ) );
3401 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3403 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3404 // For now the visual position of end of logical string will be end of visual string.
3405 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3407 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3410 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3412 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3413 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3414 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3415 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3417 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3419 // Deselect text prior to startSelectionIt
3420 for(;it!=startSelectionIt;++it)
3422 selectedVisualText[*it] = false;
3425 // Select text from startSelectionIt -> endSelectionIt
3426 for(;it!=endSelectionIt;++it)
3428 selectedVisualText[*it] = true;
3431 // Deselect text after endSelection
3434 selectedVisualText[*it] = false;
3437 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3440 // Calculate the dimensions of the quads they will make the highlight mesh
3441 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3443 // At the moment there is no public API to modify the block alignment option.
3444 const bool blockAlignEnabled = true;
3446 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3448 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3450 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3451 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3453 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3454 std::vector<bool> selectedVisualText;
3455 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3456 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3457 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3459 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3460 float rowLeft = 0.0f;
3461 float rowRight = 0.0f;
3462 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3463 float maxRowLeft = std::numeric_limits<float>::max();
3464 float maxRowRight = 0.0f;
3466 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3468 // Scan through entire text.
3471 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3473 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3474 bool charSelected( false );
3475 if( selectedIt != selectedEndIt )
3477 charSelected = *selectedIt++;
3480 if(selectionState == SelectionNone)
3484 selectionState = SelectionStarted;
3485 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3486 rowRight = rowLeft + charInfo.mSize.width;
3489 else if(selectionState == SelectionStarted)
3491 // break selection on:
3492 // 1. new line causing selection break. (\n or wordwrap)
3493 // 2. character not selected.
3494 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3497 // finished selection.
3498 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3499 // that it resides on. That way this enumeration is not necessary.
3501 if(lastIt->mIsNewParagraphChar)
3503 // 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.
3504 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3506 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3507 maxRowLeft = std::min(maxRowLeft, min.x);
3508 maxRowRight = std::max(maxRowRight, max.x);
3509 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3510 float rowTop = rowBottom - rowSize.height;
3512 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3513 if(charSelected && blockAlignEnabled)
3515 rowRight = std::numeric_limits<float>::max();
3517 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3519 selectionState = SelectionNone;
3521 // Still selected? start a new selection
3524 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3525 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3526 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3527 selectionState = SelectionStarted;
3532 // build up highlight(s) with this selection data.
3533 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3534 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3541 // If reached end, and still on selection, then close selection.
3544 if(selectionState == SelectionStarted)
3546 // finished selection.
3548 if(lastIt->mIsNewParagraphChar)
3550 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3552 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3553 maxRowLeft = std::min(maxRowLeft, min.x);
3554 maxRowRight = std::max(maxRowRight, max.x);
3555 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3556 float rowTop = rowBottom - rowSize.height;
3557 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3561 // Get the top left and bottom right corners.
3562 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3563 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3564 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3566 // Clamp quads so they appear to clip to borders of the whole text.
3567 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3569 // For block-align align Further Clamp quads to max left and right extents
3570 if(blockAlignEnabled)
3572 // BlockAlign: Will adjust highlight to block:
3574 // H[ello] (top row right = max of all rows right)
3575 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3576 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3577 // [text] (bottom row left = min of all rows left)
3578 // (common in SMS messaging selection)
3580 // As opposed to the default which is tight text highlighting.
3585 // (common in regular text editors/web browser selection)
3587 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3590 // Finally clamp quads again so they don't exceed the boundry of the control.
3591 const Vector3& controlSize = GetControlSize();
3592 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3595 return mNewHighlightInfo;
3598 void TextInput::UpdateHighlight()
3600 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3602 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3604 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3605 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3606 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3607 // [BOTTOM] [ MIDDLE ]
3610 // Each quad is created as 2 triangles.
3611 // Middle is just 1 quad regardless of its size.
3625 if ( mHighlightMeshActor )
3627 // vertex and triangle buffers should always be present if MeshActor is alive.
3628 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3629 MeshData::VertexContainer vertices;
3630 Dali::MeshData::FaceIndices faceIndices;
3632 if( !newHighlightInfo.mQuadList.empty() )
3634 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3635 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3637 // vertex position defaults to (0 0 0)
3638 MeshData::Vertex vertex;
3639 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3642 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3644 // Add each quad geometry (a sub-selection) to the mesh data.
3654 QuadCoordinates& quad = *iter;
3656 vertex.x = quad.min.x;
3657 vertex.y = quad.min.y;
3658 vertices.push_back( vertex );
3661 vertex.x = quad.max.x;
3662 vertex.y = quad.min.y;
3663 vertices.push_back( vertex );
3665 // bottom-left (v+2)
3666 vertex.x = quad.min.x;
3667 vertex.y = quad.max.y;
3668 vertices.push_back( vertex );
3670 // bottom-right (v+3)
3671 vertex.x = quad.max.x;
3672 vertex.y = quad.max.y;
3673 vertices.push_back( vertex );
3675 // triangle A (3, 1, 0)
3676 faceIndices.push_back( v + 3 );
3677 faceIndices.push_back( v + 1 );
3678 faceIndices.push_back( v );
3680 // triangle B (0, 2, 3)
3681 faceIndices.push_back( v );
3682 faceIndices.push_back( v + 2 );
3683 faceIndices.push_back( v + 3 );
3685 mMeshData.SetFaceIndices( faceIndices );
3688 BoneContainer bones(0); // passed empty as bones not required
3689 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3690 mHighlightMesh.UpdateMeshData(mMeshData);
3695 void TextInput::ClearPopup()
3697 mPopupPanel.Clear();
3700 void TextInput::AddPopupOptions()
3702 mPopupPanel.AddPopupOptions();
3705 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3707 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3709 Vector3 clampedPosition ( position );
3710 Vector3 tailOffsetPosition ( position );
3712 float xOffSet( 0.0f );
3714 Actor self = Self();
3715 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3717 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3718 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3720 // Clamp to left or right or of boundary
3721 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3723 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3725 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3727 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3730 clampedPosition.x = position.x + xOffSet;
3731 tailOffsetPosition.x = -xOffSet;
3733 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3734 bool flipTail( false );
3736 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3738 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3742 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3743 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3746 void TextInput::HidePopup(bool animate, bool signalFinished )
3748 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3750 mPopupPanel.Hide( animate );
3752 if( animate && signalFinished )
3754 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3759 void TextInput::ShowPopup( bool animate )
3762 Vector2 alternativePopupPosition;
3764 if(mHighlightMeshActor && mState == StateEdit)
3767 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3769 // When text is selected, show popup above top handle (and text), or below bottom handle.
3770 // topHandle: referring to the top most point of the handle or the top line of selection.
3771 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3773 topHandle = mSelectionHandleOneActualPosition;
3774 bottomHandle = mSelectionHandleTwoActualPosition;
3775 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3779 topHandle = mSelectionHandleTwoActualPosition;
3780 bottomHandle = mSelectionHandleOneActualPosition;
3781 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3783 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3784 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3786 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3788 position.x = xPosition;
3790 // Alternative position if no upper space
3791 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3792 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3796 // When no text is selected, show popup at world position of grab handle or cursor
3797 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3798 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3799 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3800 // if can't be positioned above, then position below row.
3801 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3804 // If grab handle enabled then position pop-up below the grab handle.
3805 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3809 SetPopupPosition( position, alternativePopupPosition );
3812 mPopupPanel.Show( Self(), animate );
3813 StartMonitoringStageForTouch();
3815 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3818 void TextInput::ShowPopupCutCopyPaste()
3822 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3823 // Check the selected text is whole text or not.
3824 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3826 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3829 if ( !mStyledText.empty() )
3831 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3832 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3835 if( mClipboard && mClipboard.NumberOfItems() )
3837 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3838 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3843 mPopupPanel.Hide(false);
3847 void TextInput::SetUpPopupSelection( bool showCutButton )
3850 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3851 // If no text exists then don't offer to select
3852 if ( !mStyledText.empty() )
3854 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3855 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3856 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, showCutButton );
3858 // if clipboard has valid contents then offer paste option
3859 if( mClipboard && mClipboard.NumberOfItems() )
3861 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3862 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3867 mPopupPanel.Hide(false);
3870 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3875 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3876 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3877 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3878 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3880 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3882 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3884 float closestYdifference = std::numeric_limits<float>::max();
3885 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3886 std::size_t numberOfMatchedCharacters = 0;
3888 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3889 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3891 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3893 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3894 float baselinePosition = info.mPosition.y - info.mDescender;
3896 if( info.mIsVisible )
3898 // store difference between source y point and the y position of the current character
3899 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3901 if( currentYdifference < closestYdifference )
3903 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3904 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3905 closestYdifference = currentYdifference;
3906 matchedCharacters.clear();
3907 numberOfMatchedCharacters = 0; // reset count
3910 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3911 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3913 // ignore new line character.
3914 if( !info.mIsNewParagraphChar )
3916 matchedCharacters.push_back( info );
3917 numberOfMatchedCharacters++;
3921 } // End of loop checking each character's y position in the character layout table
3923 // Check if last character is a newline, if it is
3924 // then need pretend there is an imaginary line afterwards,
3925 // and check if user is touching below previous line.
3926 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3928 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3930 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3934 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3935 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3937 bool matched( false );
3939 // 2 Iterate through matching list of y positions and find closest matching X position.
3940 for( ; it != endIt; ++it )
3942 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3944 if( info.mIsVisible )
3946 // stop when on left side of character's center.
3947 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3948 if( sourceScrollOffset.x < characterMidPointPosition )
3950 if(info.mIsRightToLeftCharacter)
3952 rightToLeftChar = true;
3954 glyphIntersection = info.mPosition.x;
3959 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3965 rightToLeftChar = lastRightToLeftChar;
3968 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3969 closestIndex = lineOffset + matchCharacterIndex;
3971 mClosestCursorPositionEOL = false; // reset
3972 if ( it == endIt && !matched )
3974 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3977 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3978 if( rightToLeftChar && lastRightToLeftChar )
3980 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3985 // closestIndex is the visual index, need to convert it to the logical index
3986 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3988 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3990 // Checks for situations where user is touching between LTR and RTL
3991 // characters. To identify if the user means the end of a LTR string
3992 // or the beginning of an RTL string, and vice versa.
3993 if( closestIndex > 0 )
3995 if( rightToLeftChar && !lastRightToLeftChar )
4000 // A: In this touch range, the user is indicating that they wish to place
4001 // the cursor at the end of the LTR text.
4002 // B: In this touch range, the user is indicating that they wish to place
4003 // the cursor at the end of the RTL text.
4005 // Result of touching A area:
4006 // [.....LTR]|[RTL......]+
4008 // |: primary cursor (for typing LTR chars)
4009 // +: secondary cursor (for typing RTL chars)
4011 // Result of touching B area:
4012 // [.....LTR]+[RTL......]|
4014 // |: primary cursor (for typing RTL chars)
4015 // +: secondary cursor (for typing LTR chars)
4017 if( sourceScrollOffset.x < glyphIntersection )
4022 else if( !rightToLeftChar && lastRightToLeftChar )
4024 if( sourceScrollOffset.x < glyphIntersection )
4031 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4032 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4033 // one further ahead
4034 if( rightToLeftChar && !lastRightToLeftChar )
4039 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4041 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4043 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4052 float TextInput::GetLineJustificationPosition() const
4054 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4055 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4056 float alignmentOffset = 0.f;
4058 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4059 if( alignment & Toolkit::Alignment::HorizontalLeft )
4061 alignmentOffset = 0.f;
4063 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4065 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4067 else if( alignment & Toolkit::Alignment::HorizontalRight )
4069 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4072 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4073 float justificationOffset = 0.f;
4075 switch( justification )
4077 case Toolkit::TextView::Left:
4079 justificationOffset = 0.f;
4082 case Toolkit::TextView::Center:
4084 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4087 case Toolkit::TextView::Right:
4089 justificationOffset = mTextLayoutInfo.mTextSize.width;
4092 case Toolkit::TextView::Justified:
4094 justificationOffset = 0.f;
4099 DALI_ASSERT_ALWAYS( false );
4103 return alignmentOffset + justificationOffset;
4106 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4108 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4109 A newline character is not inserted in this case */
4111 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4113 Vector3 cursorPosition;
4115 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4117 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4119 // end character so use
4120 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4121 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4125 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4128 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4130 // If previous character on a different line then use current characters position
4131 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4133 if ( mClosestCursorPositionEOL )
4135 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4139 cursorPosition = Vector3(currentCharInfo.mPosition);
4144 // Previous character is on same line so use position of previous character plus it's width.
4145 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4148 return cursorPosition;
4151 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4153 bool direction(false);
4154 Vector3 alternatePosition;
4155 bool alternatePositionValid(false);
4157 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4160 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4162 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4164 alternatePositionValid = false;
4165 directionRTL = false;
4167 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4169 std::size_t visualCharacterPosition;
4171 // When cursor is not at beginning, consider possibility of
4172 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4173 if(characterPosition > 0)
4175 // Cursor position should be the end of the last character.
4176 // If the last character is LTR, then the end is on the right side of the glyph.
4177 // If the last character is RTL, then the end is on the left side of the glyph.
4178 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4180 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4182 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4185 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4186 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4188 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4189 const Vector3& size = GetControlSize();
4191 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4193 --visualCharacterPosition;
4195 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4198 if(!info.mIsNewParagraphChar)
4200 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4204 // When cursor points to first character on new line, position cursor at the start of this glyph.
4205 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4207 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4208 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4209 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4211 cursorPosition.x = infoNext.mPosition.x + start;
4212 cursorPosition.y = infoNext.mPosition.y;
4216 // If cursor points to the end of text, then can only position
4217 // cursor where the new line starts based on the line-justification position.
4218 cursorPosition.x = GetLineJustificationPosition();
4220 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4222 // If this is after the last character, then we can assume that the new cursor
4223 // should be exactly one row below the current row.
4225 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4226 cursorPosition.y = info.mPosition.y + rowRect.height;
4230 // If this is not after last character, then we can use this row's height.
4231 // should be exactly one row below the current row.
4233 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4234 cursorPosition.y = info.mPosition.y + rowRect.height;
4239 directionRTL = info.mIsRightToLeftCharacter;
4241 // 1. When the cursor is neither at the beginning or the end,
4242 // we can show multiple cursors under situations when the cursor is
4243 // between RTL and LTR text...
4244 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4246 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4248 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4249 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4251 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4253 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4254 // Text: [...LTR...]|[...RTL...]
4256 // Alternate cursor pos: ^
4257 // In which case we need to display an alternate cursor for the RTL text.
4259 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4260 alternatePosition.y = infoAlt.mPosition.y;
4261 alternatePositionValid = true;
4263 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4265 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4266 // Text: |[...RTL...] [...LTR....]
4268 // Alternate cursor pos: ^
4269 // In which case we need to display an alternate cursor for the RTL text.
4271 alternatePosition.x = infoAlt.mPosition.x;
4272 alternatePosition.y = infoAlt.mPosition.y;
4273 alternatePositionValid = true;
4278 // 2. When the cursor is at the end of the text,
4279 // and we have multi-directional text,
4280 // we can also consider showing mulitple cursors.
4281 // The rule here is:
4282 // If first and last characters on row are different
4283 // Directions, then two cursors need to be displayed.
4285 // Get first logical glyph on row
4286 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4288 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4289 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4291 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4293 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4294 // Text: [...LTR...]|[...RTL...]
4296 // Alternate cursor pos: ^
4297 // In which case we need to display an alternate cursor for the RTL text, this cursor
4298 // should be at the end of the given line.
4300 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4301 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4302 alternatePosition.y = infoAlt.mPosition.y;
4303 alternatePositionValid = true;
4305 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4307 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4308 // Text: |[...RTL...] [...LTR....]
4310 // Alternate cursor pos: ^
4311 // In which case we need to display an alternate cursor for the RTL text.
4313 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4314 alternatePosition.x = infoAlt.mPosition.x;
4315 alternatePosition.y = infoAlt.mPosition.y;
4316 alternatePositionValid = true;
4319 } // characterPosition > 0
4320 else if(characterPosition == 0)
4322 // When the cursor position is at the beginning, it should be at the start of the current character.
4323 // If the current character is LTR, then the start is on the right side of the glyph.
4324 // If the current character is RTL, then the start is on the left side of the glyph.
4325 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4327 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4329 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4332 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4333 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4335 cursorPosition.x = info.mPosition.x + start;
4336 cursorPosition.y = info.mPosition.y;
4337 directionRTL = info.mIsRightToLeftCharacter;
4342 // If the character table is void, place the cursor accordingly the text alignment.
4343 const Vector3& size = GetControlSize();
4345 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4346 float alignmentOffset = 0.f;
4348 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4349 if( alignment & Toolkit::Alignment::HorizontalLeft )
4351 alignmentOffset = 0.f;
4353 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4355 alignmentOffset = 0.5f * ( size.width );
4357 else if( alignment & Toolkit::Alignment::HorizontalRight )
4359 alignmentOffset = size.width;
4362 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4363 cursorPosition.x = alignmentOffset;
4365 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4366 if( alignment & Toolkit::Alignment::VerticalTop )
4368 cursorPosition.y = mLineHeight;
4370 else if( alignment & Toolkit::Alignment::VerticalCenter )
4372 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4374 else if( alignment & Toolkit::Alignment::VerticalBottom )
4376 cursorPosition.y = size.height;
4380 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4381 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4382 if( alternatePositionValid )
4384 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4385 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4388 return cursorPosition;
4391 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4393 // scan string from current position to beginning of current line to note direction of line
4394 while(logicalPosition)
4397 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4398 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4405 return logicalPosition;
4408 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4412 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4415 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4417 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4418 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4420 min = Vector2::ZERO;
4421 max = Vector2(0.0f, mLineHeight);
4425 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4426 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4427 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4429 // If cursor is pointing to end of line, then start from last character.
4430 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4432 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4434 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4435 if( !it->mIsVisible )
4437 characterPosition = FindVisibleCharacter( Left, characterPosition );
4438 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4441 // Scan characters left and right of cursor, stopping when end of line/string reached or
4442 // y position greater than threshold of reference line.
4444 // 1. scan left until we reach the beginning or a different line.
4445 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4446 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4447 // min-x position is the left-most char's left (x)
4448 // max-x position is the right-most char's right (x)
4449 // min-y position is the minimum of all character's top (y)
4450 // max-y position is the maximum of all character's bottom (y+height)
4451 min.y = validCharIt->mPosition.y;
4452 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4457 min.y = std::min(min.y, validCharIt->mPosition.y);
4458 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4467 if( (it->mPosition.y < referenceLine) ||
4468 (it->mIsNewParagraphChar) ||
4475 // info refers to the first character on this line.
4476 min.x = validCharIt->mPosition.x;
4478 // 2. scan right until we reach end or a different line
4479 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4480 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4484 if( (it->mPosition.y > referenceLine) ||
4485 (it->mIsNewParagraphChar) ||
4492 min.y = std::min(min.y, validCharIt->mPosition.y);
4493 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4498 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4500 if ( validCharIt != end )
4502 // info refers to the last character on this line.
4503 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4506 return Size( max.x - min.x, max.y - min.y );
4509 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4511 Actor popUpPanel = mPopupPanel.GetRootActor();
4513 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4519 Dali::Actor parent( touchedActor.GetParent() );
4523 return WasTouchedCheck( parent );
4530 void TextInput::StartMonitoringStageForTouch()
4532 Stage stage = Stage::GetCurrent();
4533 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4536 void TextInput::EndMonitoringStageForTouch()
4538 Stage stage = Stage::GetCurrent();
4539 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4542 void TextInput::OnStageTouched(const TouchEvent& event)
4544 if( event.GetPointCount() > 0 )
4546 if ( TouchPoint::Down == event.GetPoint(0).state )
4548 const Actor touchedActor(event.GetPoint(0).hitActor);
4550 bool popUpShown( false );
4552 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4557 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4559 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4561 EndMonitoringStageForTouch();
4562 HidePopup( true, false );
4565 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4567 EndMonitoringStageForTouch();
4568 ShowGrabHandleAndSetVisibility( false );
4574 void TextInput::SelectText(std::size_t start, std::size_t end)
4576 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4577 IsGrabHandleEnabled()?"true":"false",
4578 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4579 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4580 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4582 StartMonitoringStageForTouch();
4584 if ( mEditModeActive ) // Only allow text selection when in edit mode
4586 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4587 mSelectingText = true;
4589 std::size_t selectionStartPosition = std::min( start, end );
4591 // Hide grab handle when selecting.
4592 ShowGrabHandleAndSetVisibility( false );
4594 if( start != end ) // something to select
4596 SetCursorVisibility( false );
4597 StopCursorBlinkTimer();
4599 CreateSelectionHandles(start, end);
4602 const TextStyle oldInputStyle( mInputStyle );
4603 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4605 if( oldInputStyle != mInputStyle )
4607 // Updates the line height accordingly with the input style.
4610 EmitStyleChangedSignal();
4616 mSelectingText = false;
4620 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4622 MarkupProcessor::StyledTextArray currentSelectedText;
4624 if ( IsTextSelected() )
4626 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4627 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4629 for(; it != end; ++it)
4631 MarkupProcessor::StyledText& styledText( *it );
4632 currentSelectedText.push_back( styledText );
4635 return currentSelectedText;
4638 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4640 const std::size_t beginIndex = std::min( begin, end );
4641 const std::size_t endIndex = std::max( begin, end );
4644 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4646 // Create a styled text array used to replace the text into the text-view.
4647 MarkupProcessor::StyledTextArray text;
4648 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4650 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4651 GetTextLayoutInfo();
4653 if( IsScrollEnabled() )
4655 // Need to set the scroll position as the text's size may have changed.
4656 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4659 ShowGrabHandleAndSetVisibility( false );
4665 // Set Handle positioning as the new style may have repositioned the characters.
4666 SetSelectionHandlePosition(HandleOne);
4667 SetSelectionHandlePosition(HandleTwo);
4670 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4672 // Just hide the grab handle when keyboard is hidden.
4673 if (!keyboardShown )
4675 ShowGrabHandleAndSetVisibility( false );
4677 // If the keyboard is not now being shown, then hide the popup panel
4678 mPopupPanel.Hide( true );
4682 // Removes highlight and resumes edit mode state
4683 void TextInput::RemoveHighlight( bool hidePopup )
4685 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4687 if ( mHighlightMeshActor )
4689 if ( mSelectionHandleOne )
4691 mActiveLayer.Remove( mSelectionHandleOne );
4692 mSelectionHandleOne.Reset();
4693 mSelectionHandleOneOffset.x = 0.0f;
4695 if ( mSelectionHandleTwo )
4697 mActiveLayer.Remove( mSelectionHandleTwo );
4698 mSelectionHandleTwo.Reset();
4699 mSelectionHandleTwoOffset.x = 0.0f;
4702 mNewHighlightInfo.mQuadList.clear();
4704 Self().Remove( mHighlightMeshActor );
4706 SetCursorVisibility( true );
4707 StartCursorBlinkTimer();
4709 mHighlightMeshActor.Reset();
4710 // NOTE: We cannot dereference mHighlightMesh, due
4711 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4719 mSelectionHandleOnePosition = 0;
4720 mSelectionHandleTwoPosition = 0;
4723 void TextInput::CreateHighlight()
4725 if ( !mHighlightMeshActor )
4727 mMeshData = MeshData( );
4728 mMeshData.SetHasNormals( true );
4730 mCustomMaterial = Material::New("CustomMaterial");
4731 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4733 mMeshData.SetMaterial( mCustomMaterial );
4735 mHighlightMesh = Mesh::New( mMeshData );
4737 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4738 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4739 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4740 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4741 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4742 mHighlightMeshActor.SetAffectedByLighting(false);
4744 Self().Add(mHighlightMeshActor);
4749 bool TextInput::CopySelectedTextToClipboard()
4751 mCurrentCopySelecton.clear();
4753 mCurrentCopySelecton = GetSelectedText();
4755 std::string stringToStore;
4757 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4758 * a marked up string.
4760 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4761 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4762 bool success = mClipboard.SetItem( stringToStore );
4766 void TextInput::PasteText( const Text& text )
4768 // Update Flag, indicates whether to update the text-input contents or not.
4769 // Any key stroke that results in a visual change of the text-input should
4770 // set this flag to true.
4771 bool update = false;
4772 if( mHighlightMeshActor )
4774 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4775 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4777 ImfManager imfManager = ImfManager::Get();
4780 imfManager.SetCursorPosition( mCursorPosition );
4781 imfManager.NotifyCursorPosition();
4783 DeleteHighlightedText( true );
4787 bool textExceedsMaximunNumberOfCharacters = false;
4788 bool textExceedsBoundary = false;
4790 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4792 mCursorPosition += insertedStringLength;
4793 ImfManager imfManager = ImfManager::Get();
4796 imfManager.SetCursorPosition ( mCursorPosition );
4797 imfManager.NotifyCursorPosition();
4800 update = update || ( insertedStringLength > 0 );
4807 if( insertedStringLength < text.GetLength() )
4809 EmitMaxInputCharactersReachedSignal();
4812 if( textExceedsBoundary )
4814 EmitInputTextExceedsBoundariesSignal();
4818 void TextInput::SetTextDirection()
4820 // Put the cursor to the right if we are empty and an RTL language is being used.
4821 if ( mStyledText.empty() )
4823 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4825 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4826 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4828 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4829 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4831 int alignment( mDisplayedTextView.GetTextAlignment() &
4832 ( Toolkit::Alignment::VerticalTop |
4833 Toolkit::Alignment::VerticalCenter |
4834 Toolkit::Alignment::VerticalBottom |
4835 Toolkit::Alignment::HorizontalCenter ) );
4836 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4838 // If our alignment is in the center, then do not change.
4839 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4841 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4844 // If our justification is in the center, then do not change.
4845 if ( justification != Toolkit::TextView::Center )
4847 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4850 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4851 mDisplayedTextView.SetLineJustification( justification );
4855 void TextInput::UpdateLineHeight()
4857 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4858 mLineHeight = font.GetLineHeight();
4860 // 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.
4862 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4864 if( !mExceedEnabled || shrink )
4866 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4870 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4872 std::size_t position = 0;
4874 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4880 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4882 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4884 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4890 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4891 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4893 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4899 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4904 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4911 void TextInput::SetSortModifier( float depthOffset )
4913 if(mDisplayedTextView)
4915 mDisplayedTextView.SetSortModifier(depthOffset);
4919 void TextInput::SetSnapshotModeEnabled( bool enable )
4921 if(mDisplayedTextView)
4923 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4927 bool TextInput::IsSnapshotModeEnabled() const
4929 bool snapshotEnabled = false;
4931 if(mDisplayedTextView)
4933 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4936 return snapshotEnabled;
4939 void TextInput::SetMarkupProcessingEnabled( bool enable )
4941 mMarkUpEnabled = enable;
4944 bool TextInput::IsMarkupProcessingEnabled() const
4946 return mMarkUpEnabled;
4949 void TextInput::SetScrollEnabled( bool enable )
4951 if( mDisplayedTextView )
4953 mDisplayedTextView.SetScrollEnabled( enable );
4958 // Don't set cursor's and handle's visibility to false if they are outside the
4959 // boundaries of the text-input.
4960 mIsCursorInScrollArea = true;
4961 mIsGrabHandleInScrollArea = true;
4962 if( mSelectionHandleOne && mSelectionHandleTwo )
4964 mSelectionHandleOne.SetVisible( true );
4965 mSelectionHandleTwo.SetVisible( true );
4967 if( mHighlightMeshActor )
4969 mHighlightMeshActor.SetVisible( true );
4975 bool TextInput::IsScrollEnabled() const
4977 bool scrollEnabled = false;
4979 if( mDisplayedTextView )
4981 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4984 return scrollEnabled;
4987 void TextInput::SetScrollPosition( const Vector2& position )
4989 if( mDisplayedTextView )
4991 mDisplayedTextView.SetScrollPosition( position );
4995 Vector2 TextInput::GetScrollPosition() const
4997 Vector2 scrollPosition;
4999 if( mDisplayedTextView )
5001 scrollPosition = mDisplayedTextView.GetScrollPosition();
5004 return scrollPosition;
5007 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5009 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5010 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5011 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5013 // Add style to the new input text.
5014 MarkupProcessor::StyledTextArray textToInsert;
5015 for( std::size_t i = 0; i < insertedStringLength; ++i )
5017 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5018 textToInsert.push_back( newStyledCharacter );
5021 //Insert text to the TextView.
5022 const bool emptyTextView = mStyledText.empty();
5023 if( emptyTextView && mPlaceHolderSet )
5025 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5026 mDisplayedTextView.SetText( textToInsert );
5030 if( 0 == numberOfCharactersToReplace )
5032 mDisplayedTextView.InsertTextAt( position, textToInsert );
5036 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5039 mPlaceHolderSet = false;
5041 if( textToInsert.empty() )
5043 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5044 GetTextLayoutInfo();
5048 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5049 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5052 textExceedsBoundary = false;
5054 if( !mExceedEnabled )
5056 const Vector3& size = GetControlSize();
5058 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5060 // If new text does not fit within TextView
5061 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5062 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5063 GetTextLayoutInfo();
5064 textExceedsBoundary = true;
5065 insertedStringLength = 0;
5068 if( textExceedsBoundary )
5070 // Add the part of the text which fits on the text-input.
5072 // Split the text which doesn't fit in two halves.
5073 MarkupProcessor::StyledTextArray firstHalf;
5074 MarkupProcessor::StyledTextArray secondHalf;
5075 SplitText( textToInsert, firstHalf, secondHalf );
5077 // Clear text. This text will be filled with the text inserted.
5078 textToInsert.clear();
5080 // Where to insert the text.
5081 std::size_t positionToInsert = position;
5083 bool end = text.GetLength() <= 1;
5086 // Insert text and check ...
5087 const std::size_t textLength = firstHalf.size();
5088 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5089 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5091 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5093 // Inserted text doesn't fit.
5095 // Remove inserted text
5096 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5097 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5099 // The iteration finishes when only one character doesn't fit.
5100 end = textLength <= 1;
5104 // Prepare next two halves for next iteration.
5105 MarkupProcessor::StyledTextArray copyText = firstHalf;
5106 SplitText( copyText, firstHalf, secondHalf );
5113 // store text to be inserted in mStyledText.
5114 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5116 // Increase the inserted characters counter.
5117 insertedStringLength += textLength;
5119 // Prepare next two halves for next iteration.
5120 MarkupProcessor::StyledTextArray copyText = secondHalf;
5121 SplitText( copyText, firstHalf, secondHalf );
5123 // Update where next text has to be inserted
5124 positionToInsert += textLength;
5130 if( textToInsert.empty() && emptyTextView )
5132 // No character has been added and the text-view was empty.
5133 // Set the placeholder text.
5134 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5135 mPlaceHolderSet = true;
5139 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5140 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5141 mPlaceHolderSet = false;
5144 return insertedStringLength;
5147 void TextInput::GetTextLayoutInfo()
5149 if( mStyledText.empty() )
5151 // The text-input has no text, clear the text-view's layout info.
5152 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5156 if( mDisplayedTextView )
5158 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5162 // There is no text-view.
5163 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5168 void TextInput::SetOffsetFromText( const Vector4& offset )
5170 mPopupOffsetFromText = offset;
5173 const Vector4& TextInput::GetOffsetFromText() const
5175 return mPopupOffsetFromText;
5178 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5180 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5184 TextInput& textInputImpl( GetImpl( textInput ) );
5186 switch ( propertyIndex )
5188 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5190 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5193 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5195 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5198 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5200 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5203 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5205 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5208 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5210 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5213 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5215 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5218 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5220 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5223 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5225 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5228 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5230 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5233 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5235 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5238 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5240 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5243 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5245 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5248 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5250 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5253 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5255 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5258 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5260 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5263 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5265 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5271 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5273 Property::Value value;
5275 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5279 TextInput& textInputImpl( GetImpl( textInput ) );
5281 switch ( propertyIndex )
5283 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5285 value = textInputImpl.GetMaterialDiffuseColor();
5288 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5290 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5293 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5295 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5298 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5300 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5303 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5305 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5308 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5310 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5313 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5315 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5318 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5320 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5323 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5325 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5328 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5330 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5333 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5335 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5338 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5340 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5343 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5345 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5348 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5350 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5353 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5355 value = textInputImpl.GetOffsetFromText();
5358 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5360 value = textInputImpl.mCursor.GetCurrentColor();
5367 void TextInput::EmitStyleChangedSignal()
5369 // emit signal if input style changes.
5370 Toolkit::TextInput handle( GetOwner() );
5371 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5374 void TextInput::EmitTextModified()
5376 // emit signal when text changes.
5377 Toolkit::TextInput handle( GetOwner() );
5378 mTextModifiedSignal.Emit( handle );
5382 void TextInput::EmitMaxInputCharactersReachedSignal()
5384 // emit signal if max characters is reached during text input.
5385 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5387 Toolkit::TextInput handle( GetOwner() );
5388 mMaxInputCharactersReachedSignalV2.Emit( handle );
5391 void TextInput::EmitInputTextExceedsBoundariesSignal()
5393 // Emit a signal when the input text exceeds the boundaries of the text input.
5395 Toolkit::TextInput handle( GetOwner() );
5396 mInputTextExceedBoundariesSignalV2.Emit( handle );
5399 } // namespace Internal
5401 } // namespace Toolkit