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 ImfManager imfManager = ImfManager::Get();
1394 imfManager.SetCursorPosition ( mCursorPosition );
1395 imfManager.NotifyCursorPosition();
1398 std::size_t start = 0;
1399 std::size_t end = 0;
1400 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1402 SelectText( start, end );
1404 // if no text but clipboard has content then show paste option
1405 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1407 ShowPopupCutCopyPaste();
1410 // If no text and clipboard empty then do nothing
1413 // TODO: Change the function name to be more general.
1414 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1416 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1417 , (mEditOnTouch)?"true":"false"
1418 , (mEditModeActive)?"true":"false");
1420 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1425 if( mGrabArea == actor )
1427 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1429 SetUpPopupSelection();
1439 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1441 // Initially don't create the grab handle.
1442 bool createGrabHandle = false;
1444 if ( !mEditModeActive )
1446 // update line height before calculate the actual position.
1449 // Only start edit mode if TextInput configured to edit on touch
1452 // Set the initial cursor position in the tap point.
1453 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1455 // Create the grab handle.
1456 // TODO Make this a re-usable function.
1457 if ( IsGrabHandleEnabled() )
1459 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1463 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1464 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1465 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1466 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1470 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1471 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1472 // otherwise the Grab handle will be shown when selecting.
1479 // Show the keyboard if it was hidden.
1480 if (!VirtualKeyboard::IsVisible())
1482 VirtualKeyboard::Show();
1485 // Reset keyboard as tap event has occurred.
1486 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1487 PreEditReset( true );
1489 GetTextLayoutInfo();
1491 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1493 // 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.
1495 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1497 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1499 // Notify keyboard so it can 're-capture' word for predictive text.
1500 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1501 ImfManager imfManager = ImfManager::Get();
1504 imfManager.SetCursorPosition ( mCursorPosition );
1505 imfManager.NotifyCursorPosition();
1507 const TextStyle oldInputStyle( mInputStyle );
1509 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1513 // Create the grab handle.
1514 // Grab handle is created later.
1515 createGrabHandle = true;
1517 if( oldInputStyle != mInputStyle )
1519 // Updates the line height accordingly with the input style.
1522 EmitStyleChangedSignal();
1527 if ( createGrabHandle && IsGrabHandleEnabled() )
1529 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1533 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1534 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1535 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1536 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1541 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1543 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1545 // Ignore longpress if in selection mode already
1546 if( mHighlightMeshActor )
1551 if(longPress.state == Dali::Gesture::Started)
1553 // Start edit mode on long press
1554 if ( !mEditModeActive )
1559 // If text exists then select nearest word.
1560 if ( !mStyledText.empty())
1564 ShowGrabHandleAndSetVisibility( false );
1569 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1570 // converts the pre-edit word being displayed to a committed word.
1571 if ( !mUnderlinedPriorToPreEdit )
1574 style.SetUnderline( false );
1575 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1577 mPreEditFlag = false;
1578 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1579 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1580 PreEditReset( false );
1582 mCursorPosition = 0;
1584 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1585 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1587 ImfManager imfManager = ImfManager::Get();
1590 imfManager.SetCursorPosition ( mCursorPosition );
1591 imfManager.NotifyCursorPosition();
1593 std::size_t start = 0;
1594 std::size_t end = 0;
1595 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1597 SelectText( start, end );
1600 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1601 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1603 ShowPopupCutCopyPaste();
1608 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1610 const Text clipboardText( notifier.GetContent() );
1611 PasteText( clipboardText );
1613 SetCursorVisibility( true );
1614 StartCursorBlinkTimer();
1616 ShowGrabHandleAndSetVisibility( false );
1622 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1624 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1626 const std::string& name = button.GetName();
1628 if(name == TextInputPopup::OPTION_SELECT_WORD)
1630 std::size_t start = 0;
1631 std::size_t end = 0;
1632 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1634 SelectText( start, end );
1636 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1638 SetCursorVisibility(false);
1639 StopCursorBlinkTimer();
1641 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1642 std::size_t start = 0;
1644 SelectText( start, end );
1646 else if(name == TextInputPopup::OPTION_CUT)
1648 bool ret = CopySelectedTextToClipboard();
1652 DeleteHighlightedText( true );
1656 SetCursorVisibility( true );
1657 StartCursorBlinkTimer();
1661 else if(name == TextInputPopup::OPTION_COPY)
1663 CopySelectedTextToClipboard();
1667 SetCursorVisibility( true );
1668 StartCursorBlinkTimer();
1672 else if(name == TextInputPopup::OPTION_PASTE)
1674 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1676 PasteText(retrievedString);
1678 SetCursorVisibility( true );
1679 StartCursorBlinkTimer();
1681 ShowGrabHandleAndSetVisibility( false );
1685 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1687 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1688 // Hence pass the false parameter for signalFinished.
1689 HidePopup( true, false );
1690 mClipboard.ShowClipboard();
1696 bool TextInput::OnCursorBlinkTimerTick()
1699 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1700 if ( mCursorRTLEnabled )
1702 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1704 mCursorBlinkStatus = !mCursorBlinkStatus;
1709 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1711 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1713 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1714 if(mHighlightMeshActor && mState == StateEdit)
1716 ShowPopupCutCopyPaste();
1720 //FIXME this routine needs to be re-written as it contains too many branches.
1721 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1723 std::string keyName = event.keyPressedName;
1724 std::string keyString = event.keyPressed;
1726 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1728 // Do not consume "Tab" and "Escape" keys.
1729 if(keyName == "Tab" || keyName == "Escape")
1731 // Escape key to end the edit mode
1737 HidePopup(); // If Pop-up shown then hides it as editing text.
1739 // Update Flag, indicates whether to update the text-input contents or not.
1740 // Any key stroke that results in a visual change of the text-input should
1741 // set this flag to true.
1744 // Whether to scroll text to cursor position.
1745 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1746 bool scroll = false;
1748 if (keyName == "Return")
1750 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1752 bool preEditFlagPreviouslySet( mPreEditFlag );
1754 if (mHighlightMeshActor)
1756 // replaces highlighted text with new line
1757 DeleteHighlightedText( false );
1759 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1761 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1762 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1765 mCommitByKeyInput = true;
1768 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1769 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1771 mPreEditFlag = true;
1772 mIgnoreCommitFlag = false;
1782 else if ( keyName == "space" )
1784 if ( mHighlightMeshActor )
1786 // Some text is selected so erase it before adding space.
1787 DeleteHighlightedText( true );
1791 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1793 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1794 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1797 mCommitByKeyInput = true;
1802 else if (keyName == "BackSpace")
1804 if ( mHighlightMeshActor )
1806 // Some text is selected so erase it
1807 DeleteHighlightedText( true );
1812 if ( mCursorPosition > 0 )
1814 DeleteCharacter( mCursorPosition );
1820 else if (keyName == "Right")
1825 else if (keyName == "Left")
1827 AdvanceCursor(true);
1830 else // event is a character
1832 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1833 if ( !keyString.empty() )
1835 if ( mHighlightMeshActor )
1837 // replaces highlighted text with new character
1838 DeleteHighlightedText( false );
1842 // Received key String
1843 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1849 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1850 // as this is a costly operation.
1856 if(update || scroll)
1858 if( IsScrollEnabled() )
1860 // Calculates the new cursor position (in actor coordinates)
1861 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1863 ScrollTextViewToMakeCursorVisible( cursorPosition );
1870 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1872 std::string keyName = event.keyPressedName;
1873 std::string keyString = event.keyPressed;
1875 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1877 // The selected text become deselected when the key code is DALI_KEY_BACK.
1878 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1887 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1889 // Updates the stored scroll position.
1890 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1892 const Vector3& controlSize = GetControlSize();
1893 Size cursorSize( CURSOR_THICKNESS, 0.f );
1895 // Updates the cursor and grab handle position and visibility.
1896 if( mGrabHandle || mCursor )
1898 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1899 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1901 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1903 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1907 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1908 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1913 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1914 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1918 // Updates the selection handles and highlighted text position and visibility.
1919 if( mSelectionHandleOne && mSelectionHandleTwo )
1921 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1922 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1923 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1924 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1925 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1926 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1928 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1929 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1931 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1932 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1933 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1934 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1936 if( mHighlightMeshActor )
1938 mHighlightMeshActor.SetVisible( true );
1944 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1946 // Scroll the text to make the cursor visible.
1947 const Size cursorSize( CURSOR_THICKNESS,
1948 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1950 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1952 const Vector3& controlSize = GetControlSize();
1954 // Calculates the new scroll position.
1955 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1956 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1958 scrollOffset.x += cursorPosition.x;
1961 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1963 scrollOffset.y += cursorPosition.y;
1966 // Sets the new scroll position.
1967 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1968 SetScrollPosition( scrollOffset );
1971 void TextInput::StartScrollTimer()
1975 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1976 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1979 if( !mScrollTimer.IsRunning() )
1981 mScrollTimer.Start();
1985 void TextInput::StopScrollTimer()
1989 mScrollTimer.Stop();
1993 bool TextInput::OnScrollTimerTick()
1995 // TODO: need to set the new style accordingly the new handle position.
1997 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1999 // nothing to do if all handles are invisible or doesn't exist.
2005 // Choose between the grab handle or the selection handles.
2006 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2007 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2008 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2010 std::size_t newCursorPosition = 0;
2011 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2013 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2014 // the new selection handle's position needs to be different of the other one.
2015 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2016 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2017 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2019 if( differentSelectionHandles )
2021 handlePosition = newCursorPosition;
2023 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2025 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2027 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2028 scrollPosition += scrollDelta;
2029 SetScrollPosition( scrollPosition );
2031 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2036 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2039 actualHandlePosition.x += mScrollDisplacement.x;
2040 actualHandlePosition.y += mScrollDisplacement.y;
2045 // Public Internal Methods (public for testing purpose)
2047 void TextInput::SetUpTouchEvents()
2049 if ( !mTapDetector )
2051 mTapDetector = TapGestureDetector::New();
2052 // Attach the actors and connect the signal
2053 mTapDetector.Attach(Self());
2055 // As contains children which may register for tap the default control detector is not used.
2056 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2059 if ( !mDoubleTapDetector )
2061 mDoubleTapDetector = TapGestureDetector::New();
2062 mDoubleTapDetector.SetTapsRequired( 2 );
2063 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2065 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2066 // so that we do not, unnecessarily, have a double tap request all the time
2069 if ( !mPanGestureDetector )
2071 mPanGestureDetector = PanGestureDetector::New();
2072 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2075 if ( !mLongPressDetector )
2077 mLongPressDetector = LongPressGestureDetector::New();
2078 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2079 mLongPressDetector.Attach(Self());
2083 void TextInput::CreateTextViewActor()
2085 mDisplayedTextView = Toolkit::TextView::New();
2086 mDisplayedTextView.SetName( "TextView ");
2087 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2088 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2089 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2090 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2091 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2092 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2093 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2094 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2095 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2096 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2098 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2100 Self().Add( mDisplayedTextView );
2103 // Start a timer to initiate, used by the cursor to blink.
2104 void TextInput::StartCursorBlinkTimer()
2106 if ( !mCursorBlinkTimer )
2108 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2109 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2112 if ( !mCursorBlinkTimer.IsRunning() )
2114 mCursorBlinkTimer.Start();
2118 // Start a timer to initiate, used by the cursor to blink.
2119 void TextInput::StopCursorBlinkTimer()
2121 if ( mCursorBlinkTimer )
2123 mCursorBlinkTimer.Stop();
2127 void TextInput::StartEditMode()
2129 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2131 if(!mEditModeActive)
2136 if ( mDoubleTapDetector )
2138 mDoubleTapDetector.Attach( Self() );
2142 void TextInput::EndEditMode()
2144 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2146 ClearKeyInputFocus();
2148 if ( mDoubleTapDetector )
2150 mDoubleTapDetector.Detach( Self() );
2154 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2156 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2158 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2160 style.SetUnderline( true );
2161 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2165 void TextInput::RemovePreEditStyle()
2167 if ( !mUnderlinedPriorToPreEdit )
2170 style.SetUnderline( false );
2171 SetActiveStyle( style, TextStyle::UNDERLINE );
2175 // IMF related methods
2178 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2180 bool update( false );
2181 bool preeditResetRequired ( false );
2183 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2185 HidePopup(); // If Pop-up shown then hides it as editing text.
2188 switch ( imfEvent.eventName )
2190 case ImfManager::PREEDIT:
2192 mIgnoreFirstCommitFlag = false;
2194 // 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
2195 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2197 // replaces highlighted text with new character
2198 DeleteHighlightedText( false );
2201 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2203 if( IsScrollEnabled() )
2205 // Calculates the new cursor position (in actor coordinates)
2206 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2207 ScrollTextViewToMakeCursorVisible( cursorPosition );
2214 case ImfManager::COMMIT:
2216 if( mIgnoreFirstCommitFlag )
2218 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2219 mIgnoreFirstCommitFlag = false;
2223 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2225 // 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
2226 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2228 // replaces highlighted text with new character
2229 DeleteHighlightedText( false );
2232 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2233 // not needed, one such scenario is when the pre-edit word is too long to fit.
2234 if ( !mIgnoreCommitFlag )
2236 update = CommitReceived( imfEvent.predictiveString );
2240 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2246 if( IsScrollEnabled() )
2248 // Calculates the new cursor position (in actor coordinates)
2249 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2251 ScrollTextViewToMakeCursorVisible( cursorPosition );
2256 case ImfManager::DELETESURROUNDING:
2258 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2259 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2261 mPreEditFlag = false;
2263 std::size_t toDelete = 0;
2264 std::size_t numberOfCharacters = 0;
2266 if( mHighlightMeshActor )
2268 // delete highlighted text.
2269 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2270 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2274 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2276 toDelete = mCursorPosition + imfEvent.cursorOffset;
2278 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2280 numberOfCharacters = mStyledText.size() - toDelete;
2284 numberOfCharacters = imfEvent.numberOfChars;
2287 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2288 DeleteRange( toDelete, numberOfCharacters );
2290 mCursorPosition = toDelete;
2291 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2295 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2298 case ImfManager::GETSURROUNDING:
2300 // 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
2301 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2302 if (! ( mHighlightMeshActor || mSelectingText ) )
2304 std::string text( GetText() );
2305 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2307 imfManager.SetCursorPosition( mCursorPosition );
2308 imfManager.SetSurroundingText( text );
2311 if( 0 != mNumberOfSurroundingCharactersDeleted )
2313 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2314 mNumberOfSurroundingCharactersDeleted = 0;
2316 if( mStyledText.empty() )
2318 // Styled text is empty, so set the placeholder text.
2319 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2320 mPlaceHolderSet = true;
2325 case ImfManager::VOID:
2327 DALI_ASSERT_DEBUG( false );
2331 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2333 return callbackData;
2336 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2338 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2340 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2341 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2343 bool preeditResetRequest ( false );
2345 if( mPreEditFlag ) // Already in pre-edit state.
2347 if( mStyledText.size() >= mMaxStringLength )
2349 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2350 // Cannot fit these characters into field, clear pre-edit.
2351 if ( !mUnderlinedPriorToPreEdit )
2354 style.SetUnderline( false );
2355 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2357 mIgnoreCommitFlag = true;
2358 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2359 mPreEditFlag = false;
2360 EmitMaxInputCharactersReachedSignal();
2364 // delete existing pre-edit string
2365 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2367 // Store new pre-edit string
2368 mPreEditString.SetText( keyString );
2370 if ( keyString.empty() )
2372 mPreEditFlag = false;
2373 mCursorPosition = mPreEditStartPosition;
2375 if( mStyledText.empty() )
2377 // Styled text is empty, so set the placeholder text.
2378 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2379 mPlaceHolderSet = true;
2383 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2385 GetTextLayoutInfo();
2390 // Insert new pre-edit string. InsertAt updates the size and position table.
2391 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2392 // 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.
2393 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2394 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2395 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2398 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2402 else // mPreEditFlag not set
2404 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2406 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2407 // new pre-edit so move into pre-edit state by setting flag
2408 mPreEditFlag = true;
2409 mPreEditString.SetText( keyString ); // store new pre-edit string
2410 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2411 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2412 // 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.
2413 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2414 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2415 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2416 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2422 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2426 return preeditResetRequest;
2429 bool TextInput::CommitReceived(const std::string& keyString )
2431 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2432 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2434 bool update( false );
2436 RemovePreEditStyle();
2438 const std::size_t styledTextSize( mStyledText.size() );
2439 if( styledTextSize >= mMaxStringLength )
2441 // Cannot fit these characters into field, clear pre-edit.
2444 mIgnoreCommitFlag = true;
2445 mPreEditFlag = false;
2447 EmitMaxInputCharactersReachedSignal();
2453 // delete existing pre-edit string
2454 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2455 mPreEditFlag = false;
2457 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2458 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2460 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2462 // No need to update cursor position as Cursor location given by touch.
2463 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2464 mPreserveCursorPosition = false;
2468 // Cursor not set by touch so needs to be re-positioned to input more text
2469 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2471 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2472 if ( mCommitByKeyInput )
2474 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2475 mCommitByKeyInput = false;
2481 if ( mSelectTextOnCommit )
2483 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2488 else // mPreEditFlag not set
2490 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2492 if( mStyledText.empty() && mPlaceHolderSet )
2494 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2495 mDisplayedTextView.SetText( "" );
2496 mNumberOfSurroundingCharactersDeleted = 0;
2497 mPlaceHolderSet = false;
2499 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2501 mNumberOfSurroundingCharactersDeleted = 0;
2506 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2511 mSelectTextOnCommit = false;
2513 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2514 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2519 // End of IMF related methods
2521 std::size_t TextInput::DeletePreEdit()
2523 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2525 DALI_ASSERT_DEBUG( mPreEditFlag );
2527 const std::size_t preEditStringLength = mPreEditString.GetLength();
2528 const std::size_t styledTextSize = mStyledText.size();
2530 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2532 // Prevents erase items outside mStyledText bounds.
2533 if( mPreEditStartPosition > styledTextSize )
2535 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2536 mPreEditStartPosition = styledTextSize;
2539 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2541 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2542 endPosition = styledTextSize;
2545 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2547 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2548 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2550 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2552 return preEditStringLength;
2555 void TextInput::PreEditReset( bool preserveCursorPosition )
2557 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2558 preserveCursorPosition, mCursorPosition);
2560 // 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.
2561 mPreserveCursorPosition = preserveCursorPosition;
2563 // Reset incase we are in a pre-edit state.
2564 ImfManager imfManager = ImfManager::Get();
2567 imfManager.Reset(); // Will trigger a commit message
2571 void TextInput::CursorUpdate()
2575 ImfManager imfManager = ImfManager::Get();
2578 std::string text( GetText() );
2579 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2580 imfManager.SetCursorPosition ( mCursorPosition );
2581 imfManager.NotifyCursorPosition();
2585 /* Delete highlighted characters redisplay*/
2586 void TextInput::DeleteHighlightedText( bool inheritStyle )
2588 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2590 if(mHighlightMeshActor)
2592 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2594 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2595 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2597 // Get the styled text of the characters to be deleted as it may be needed if
2598 // the "exceed the text-input's boundaries" option is disabled.
2599 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2601 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2603 mStyledText.erase( start, end ); // erase range of characters
2605 // Remove text from TextView.
2607 if( mStyledText.empty() )
2609 // Styled text is empty, so set the placeholder text.
2610 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2611 mPlaceHolderSet = true;
2615 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2617 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2619 // It may happen than after removing a white space or a new line character,
2620 // two words merge, this new word could be big enough to not fit in its
2621 // current line, so moved to the next one, and make some part of the text to
2622 // exceed the text-input's boundary.
2623 if( !mExceedEnabled )
2625 // Get the new text layout after removing some characters.
2626 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2628 // Get text-input's size.
2629 const Vector3& size = GetControlSize();
2631 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2632 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2634 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2636 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2637 styledCharactersToDelete.begin(),
2638 styledCharactersToDelete.end() );
2642 GetTextLayoutInfo();
2650 const TextStyle oldInputStyle( mInputStyle );
2652 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2654 if( oldInputStyle != mInputStyle )
2656 // Updates the line height accordingly with the input style.
2659 EmitStyleChangedSignal();
2665 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2667 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2668 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2670 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2673 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2675 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2676 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2678 mStyledText.erase(itStart, itEnd);
2680 // update the selection handles if they are visible.
2681 if( mHighlightMeshActor )
2683 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2684 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2686 if( minHandle >= start + ncharacters )
2688 minHandle -= ncharacters;
2690 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2695 if( maxHandle >= start + ncharacters )
2697 maxHandle -= ncharacters;
2699 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2705 // 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.
2708 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2710 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2711 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2712 // Mean we do not re-draw the text more than we have too.
2715 /* Delete character at current cursor position and redisplay*/
2716 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2718 // Ensure positionToDelete is not out of bounds.
2719 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2720 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2721 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2723 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2726 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2728 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2730 // Get the styled text of the character to be deleted as it may be needed if
2731 // the "exceed the text-input's boundaries" option is disabled.
2732 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2734 mStyledText.erase(it); // erase the character left of positionToDelete
2736 if( mStyledText.empty() )
2738 // Styled text is empty, so set the placeholder text.
2739 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2740 mPlaceHolderSet = true;
2744 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2746 const Character characterToDelete = styledCharacterToDelete.mText[0];
2748 // It may happen than after removing a white space or a new line character,
2749 // two words merge, this new word could be big enough to not fit in its
2750 // current line, so moved to the next one, and make some part of the text to
2751 // exceed the text-input's boundary.
2752 if( !mExceedEnabled )
2754 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2756 // Get the new text layout after removing one character.
2757 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2759 // Get text-input's size.
2760 const Vector3& size = GetControlSize();
2762 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2763 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2765 MarkupProcessor::StyledTextArray array;
2766 array.push_back( styledCharacterToDelete );
2767 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2769 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2774 GetTextLayoutInfo();
2776 ShowGrabHandleAndSetVisibility( false );
2778 mCursorPosition = positionToDelete -1;
2780 const TextStyle oldInputStyle( mInputStyle );
2782 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2784 if( oldInputStyle != mInputStyle )
2786 // Updates the line height accordingly with the input style.
2789 EmitStyleChangedSignal();
2794 /*Insert new character into the string and (optionally) redisplay text-input*/
2795 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2797 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2799 // Ensure insertionPosition is not out of bounds.
2800 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2802 bool textExceedsMaximunNumberOfCharacters = false;
2803 bool textExceedsBoundary = false;
2804 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2806 ShowGrabHandleAndSetVisibility( false );
2808 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2812 mIgnoreCommitFlag = true;
2813 mPreEditFlag = false;
2814 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2815 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2818 if( textExceedsMaximunNumberOfCharacters )
2820 EmitMaxInputCharactersReachedSignal();
2823 if( textExceedsBoundary )
2825 EmitInputTextExceedsBoundariesSignal();
2826 PreEditReset( false );
2830 return insertedStringLength;
2833 ImageActor TextInput::CreateCursor( const Vector4& color)
2836 cursor = CreateSolidColorActor(color);
2838 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2839 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2840 cursor.SetVisible(false);
2845 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2847 // As cursor is not moving due to grab handle, handle should be hidden.
2848 ShowGrabHandleAndSetVisibility( false );
2850 bool cursorPositionChanged = false;
2853 if ( mCursorPosition >= places )
2855 mCursorPosition = mCursorPosition - places;
2856 cursorPositionChanged = true;
2861 if ((mCursorPosition + places) <= mStyledText.size())
2863 mCursorPosition = mCursorPosition + places;
2864 cursorPositionChanged = true;
2868 if( cursorPositionChanged )
2870 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2872 const TextStyle oldInputStyle( mInputStyle );
2873 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2877 if( oldInputStyle != mInputStyle )
2879 // Updates the line height accordingly with the input style.
2882 EmitStyleChangedSignal();
2885 ImfManager imfManager = ImfManager::Get();
2888 imfManager.SetCursorPosition ( mCursorPosition );
2889 imfManager.NotifyCursorPosition();
2894 void TextInput::DrawCursor(const std::size_t nthChar)
2896 // Get height of cursor and set its size
2897 Size size( CURSOR_THICKNESS, 0.0f );
2898 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2900 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2904 // Measure Font so know how big text will be if no initial text to measure.
2905 size.height = mLineHeight;
2908 mCursor.SetSize(size);
2910 // If the character is italic then the cursor also tilts.
2911 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2913 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2915 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2917 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2918 bool altPositionValid; // Alternate cursor validity flag.
2919 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2920 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2922 SetAltCursorEnabled( altPositionValid );
2924 if(!altPositionValid)
2926 mCursor.SetPosition( position + UI_OFFSET );
2930 size.height *= 0.5f;
2931 mCursor.SetSize(size);
2932 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2934 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2935 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2936 size.height = rowSize.height * 0.5f;
2937 mCursorRTL.SetSize(size);
2938 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2941 if( IsScrollEnabled() )
2943 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2944 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2949 void TextInput::SetAltCursorEnabled( bool enabled )
2951 mCursorRTLEnabled = enabled;
2952 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2955 void TextInput::SetCursorVisibility( bool visible )
2957 mCursorVisibility = visible;
2958 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2959 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2962 void TextInput::CreateGrabHandle( Dali::Image image )
2968 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2972 mGrabHandleImage = image;
2975 mGrabHandle = ImageActor::New(mGrabHandleImage);
2976 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2977 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2979 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2981 ShowGrabHandleAndSetVisibility( false );
2983 CreateGrabArea( mGrabHandle );
2985 mActiveLayer.Add(mGrabHandle);
2989 void TextInput::CreateGrabArea( Actor& parent )
2991 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2992 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2993 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2994 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2995 mTapDetector.Attach( mGrabArea );
2996 mPanGestureDetector.Attach( mGrabArea );
2998 parent.Add(mGrabArea);
3001 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3003 Vector3 actualHandlePosition;
3007 mActualGrabHandlePosition.x += displacement.x;
3008 mActualGrabHandlePosition.y += displacement.y;
3010 // Grab handle should jump to the nearest character and take cursor with it
3011 std::size_t newCursorPosition = 0;
3012 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3014 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3016 bool handleVisible = true;
3018 if( IsScrollEnabled() )
3020 const Vector3 controlSize = GetControlSize();
3021 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3022 // Scrolls the text if the handle is not in a visible position
3023 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3030 mCurrentHandlePosition = actualHandlePosition;
3031 mScrollDisplacement = Vector2::ZERO;
3035 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3037 mScrollDisplacement.x = -SCROLL_SPEED;
3039 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3041 mScrollDisplacement.x = SCROLL_SPEED;
3043 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3045 mScrollDisplacement.y = -SCROLL_SPEED;
3047 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3049 mScrollDisplacement.y = SCROLL_SPEED;
3055 if( handleVisible && // Only redraw cursor and do updates if position changed
3056 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3058 mCursorPosition = newCursorPosition;
3060 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3062 const TextStyle oldInputStyle( mInputStyle );
3064 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3066 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3068 if( oldInputStyle != mInputStyle )
3070 // Updates the line height accordingly with the input style.
3073 EmitStyleChangedSignal();
3078 return actualHandlePosition;
3081 void TextInput::ShowGrabHandle( bool visible )
3083 if ( IsGrabHandleEnabled() )
3087 mGrabHandle.SetVisible( mGrabHandleVisibility );
3089 StartMonitoringStageForTouch();
3093 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3095 mGrabHandleVisibility = visible;
3096 ShowGrabHandle( visible );
3099 // Callbacks connected to be Property notifications for Boundary checking.
3101 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3103 mIsSelectionHandleOneFlipped = true;
3104 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3105 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3108 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3110 mIsSelectionHandleOneFlipped = false;
3111 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3112 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3115 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3117 mIsSelectionHandleTwoFlipped = true;
3118 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3119 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3122 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3124 mIsSelectionHandleTwoFlipped = false;
3125 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3126 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3129 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3130 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3132 mSelectionHandleOne.SetOpacity(0.0f);
3135 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3137 mSelectionHandleOne.SetOpacity(1.0f);
3140 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3142 mSelectionHandleTwo.SetOpacity(0.0f);
3145 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3147 mSelectionHandleTwo.SetOpacity(1.0f);
3150 // End of Callbacks connected to be Property notifications for Boundary checking.
3152 void TextInput::SetUpHandlePropertyNotifications()
3154 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3156 Vector3 handlesize = GetSelectionHandleSize();
3158 // Exceeding horizontal boundary
3159 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3160 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3162 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3163 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3165 // Within horizontal boundary
3166 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3167 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3169 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3170 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3172 // Exceeding vertical boundary
3173 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3174 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3175 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3176 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3178 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3179 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3180 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3181 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3183 // Within vertical boundary
3184 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3185 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3186 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3187 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3189 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3190 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3191 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3192 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3195 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3197 mSelectionHandleOnePosition = start;
3198 mSelectionHandleTwoPosition = end;
3200 if ( !mSelectionHandleOne )
3202 // create normal and pressed images
3203 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3204 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3206 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3207 mSelectionHandleOne.SetName("SelectionHandleOne");
3208 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3209 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3210 mIsSelectionHandleOneFlipped = false;
3211 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3213 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3214 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3216 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3217 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3219 mTapDetector.Attach( mHandleOneGrabArea );
3220 mPanGestureDetector.Attach( mHandleOneGrabArea );
3222 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3224 mSelectionHandleOne.Add( mHandleOneGrabArea );
3225 mActiveLayer.Add( mSelectionHandleOne );
3228 if ( !mSelectionHandleTwo )
3230 // create normal and pressed images
3231 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3232 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3234 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3235 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3236 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3237 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3238 mIsSelectionHandleTwoFlipped = false;
3239 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3241 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3242 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3243 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3244 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3246 mTapDetector.Attach( mHandleTwoGrabArea );
3247 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3249 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3251 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3253 mActiveLayer.Add( mSelectionHandleTwo );
3256 SetUpHandlePropertyNotifications();
3258 // update table as text may have changed.
3259 GetTextLayoutInfo();
3261 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3262 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3264 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3265 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3267 // Calculates and set the visibility if the scroll mode is enabled.
3268 bool isSelectionHandleOneVisible = true;
3269 bool isSelectionHandleTwoVisible = true;
3270 if( IsScrollEnabled() )
3272 const Vector3& controlSize( GetControlSize() );
3273 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3274 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3275 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3276 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3279 CreateHighlight(); // function will only create highlight if not already created.
3282 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3284 Vector3 actualHandlePosition;
3286 if ( mSelectionHandleOne && mSelectionHandleTwo )
3288 const Vector3& controlSize = GetControlSize();
3290 Size cursorSize( CURSOR_THICKNESS, 0.f );
3292 // Get a reference of the wanted selection handle (handle one or two).
3293 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3295 // Get a reference for the current position of the handle and a copy of its pair
3296 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3297 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3299 // Get a handle of the selection handle actor
3300 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3302 // Selection handles should jump to the nearest character
3303 std::size_t newHandlePosition = 0;
3304 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3306 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3308 bool handleVisible = true;
3310 if( IsScrollEnabled() )
3312 mCurrentSelectionId = handleId;
3314 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3315 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3316 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3323 mCurrentSelectionHandlePosition = actualHandlePosition;
3324 mScrollDisplacement = Vector2::ZERO;
3328 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3330 mScrollDisplacement.x = -SCROLL_SPEED;
3332 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3334 mScrollDisplacement.x = SCROLL_SPEED;
3336 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3338 mScrollDisplacement.y = -SCROLL_SPEED;
3340 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3342 mScrollDisplacement.y = SCROLL_SPEED;
3348 if ( handleVisible && // Ensure the handle is visible.
3349 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3350 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3352 currentSelectionHandlePosition = newHandlePosition;
3354 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3355 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3359 if ( handleId == HandleOne )
3361 const TextStyle oldInputStyle( mInputStyle );
3363 // Set Active Style to that of first character in selection
3364 if( mSelectionHandleOnePosition < mStyledText.size() )
3366 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3369 if( oldInputStyle != mInputStyle )
3371 // Updates the line height accordingly with the input style.
3374 EmitStyleChangedSignal();
3380 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3383 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3386 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3387 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3389 if ( selectionHandleActor )
3391 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3392 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3393 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3395 if( IsScrollEnabled() )
3397 const Size cursorSize( CURSOR_THICKNESS,
3398 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3399 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3401 GetControlSize() ) );
3406 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3408 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3409 // For now the visual position of end of logical string will be end of visual string.
3410 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3412 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3415 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3417 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3418 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3419 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3420 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3422 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3424 // Deselect text prior to startSelectionIt
3425 for(;it!=startSelectionIt;++it)
3427 selectedVisualText[*it] = false;
3430 // Select text from startSelectionIt -> endSelectionIt
3431 for(;it!=endSelectionIt;++it)
3433 selectedVisualText[*it] = true;
3436 // Deselect text after endSelection
3439 selectedVisualText[*it] = false;
3442 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3445 // Calculate the dimensions of the quads they will make the highlight mesh
3446 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3448 // At the moment there is no public API to modify the block alignment option.
3449 const bool blockAlignEnabled = true;
3451 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3453 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3455 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3456 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3458 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3459 std::vector<bool> selectedVisualText;
3460 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3461 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3462 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3464 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3465 float rowLeft = 0.0f;
3466 float rowRight = 0.0f;
3467 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3468 float maxRowLeft = std::numeric_limits<float>::max();
3469 float maxRowRight = 0.0f;
3471 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3473 // Scan through entire text.
3476 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3478 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3479 bool charSelected( false );
3480 if( selectedIt != selectedEndIt )
3482 charSelected = *selectedIt++;
3485 if(selectionState == SelectionNone)
3489 selectionState = SelectionStarted;
3490 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3491 rowRight = rowLeft + charInfo.mSize.width;
3494 else if(selectionState == SelectionStarted)
3496 // break selection on:
3497 // 1. new line causing selection break. (\n or wordwrap)
3498 // 2. character not selected.
3499 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3502 // finished selection.
3503 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3504 // that it resides on. That way this enumeration is not necessary.
3506 if(lastIt->mIsNewParagraphChar)
3508 // 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.
3509 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3511 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3512 maxRowLeft = std::min(maxRowLeft, min.x);
3513 maxRowRight = std::max(maxRowRight, max.x);
3514 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3515 float rowTop = rowBottom - rowSize.height;
3517 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3518 if(charSelected && blockAlignEnabled)
3520 rowRight = std::numeric_limits<float>::max();
3522 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3524 selectionState = SelectionNone;
3526 // Still selected? start a new selection
3529 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3530 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3531 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3532 selectionState = SelectionStarted;
3537 // build up highlight(s) with this selection data.
3538 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3539 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3546 // If reached end, and still on selection, then close selection.
3549 if(selectionState == SelectionStarted)
3551 // finished selection.
3553 if(lastIt->mIsNewParagraphChar)
3555 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3557 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3558 maxRowLeft = std::min(maxRowLeft, min.x);
3559 maxRowRight = std::max(maxRowRight, max.x);
3560 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3561 float rowTop = rowBottom - rowSize.height;
3562 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3566 // Get the top left and bottom right corners.
3567 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3568 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3569 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3571 // Clamp quads so they appear to clip to borders of the whole text.
3572 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3574 // For block-align align Further Clamp quads to max left and right extents
3575 if(blockAlignEnabled)
3577 // BlockAlign: Will adjust highlight to block:
3579 // H[ello] (top row right = max of all rows right)
3580 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3581 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3582 // [text] (bottom row left = min of all rows left)
3583 // (common in SMS messaging selection)
3585 // As opposed to the default which is tight text highlighting.
3590 // (common in regular text editors/web browser selection)
3592 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3595 // Finally clamp quads again so they don't exceed the boundry of the control.
3596 const Vector3& controlSize = GetControlSize();
3597 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3600 return mNewHighlightInfo;
3603 void TextInput::UpdateHighlight()
3605 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3607 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3609 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3610 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3611 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3612 // [BOTTOM] [ MIDDLE ]
3615 // Each quad is created as 2 triangles.
3616 // Middle is just 1 quad regardless of its size.
3630 if ( mHighlightMeshActor )
3632 // vertex and triangle buffers should always be present if MeshActor is alive.
3633 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3634 MeshData::VertexContainer vertices;
3635 Dali::MeshData::FaceIndices faceIndices;
3637 if( !newHighlightInfo.mQuadList.empty() )
3639 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3640 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3642 // vertex position defaults to (0 0 0)
3643 MeshData::Vertex vertex;
3644 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3647 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3649 // Add each quad geometry (a sub-selection) to the mesh data.
3659 QuadCoordinates& quad = *iter;
3661 vertex.x = quad.min.x;
3662 vertex.y = quad.min.y;
3663 vertices.push_back( vertex );
3666 vertex.x = quad.max.x;
3667 vertex.y = quad.min.y;
3668 vertices.push_back( vertex );
3670 // bottom-left (v+2)
3671 vertex.x = quad.min.x;
3672 vertex.y = quad.max.y;
3673 vertices.push_back( vertex );
3675 // bottom-right (v+3)
3676 vertex.x = quad.max.x;
3677 vertex.y = quad.max.y;
3678 vertices.push_back( vertex );
3680 // triangle A (3, 1, 0)
3681 faceIndices.push_back( v + 3 );
3682 faceIndices.push_back( v + 1 );
3683 faceIndices.push_back( v );
3685 // triangle B (0, 2, 3)
3686 faceIndices.push_back( v );
3687 faceIndices.push_back( v + 2 );
3688 faceIndices.push_back( v + 3 );
3690 mMeshData.SetFaceIndices( faceIndices );
3693 BoneContainer bones(0); // passed empty as bones not required
3694 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3695 mHighlightMesh.UpdateMeshData(mMeshData);
3700 void TextInput::ClearPopup()
3702 mPopupPanel.Clear();
3705 void TextInput::AddPopupOptions()
3707 mPopupPanel.AddPopupOptions();
3710 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3712 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3714 Vector3 clampedPosition ( position );
3715 Vector3 tailOffsetPosition ( position );
3717 float xOffSet( 0.0f );
3719 Actor self = Self();
3720 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3722 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3723 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3725 // Clamp to left or right or of boundary
3726 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3728 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3730 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3732 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3735 clampedPosition.x = position.x + xOffSet;
3736 tailOffsetPosition.x = -xOffSet;
3738 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3739 bool flipTail( false );
3741 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3743 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3747 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3748 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3751 void TextInput::HidePopup(bool animate, bool signalFinished )
3753 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3755 mPopupPanel.Hide( animate );
3757 if( animate && signalFinished )
3759 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3764 void TextInput::ShowPopup( bool animate )
3767 Vector2 alternativePopupPosition;
3769 if(mHighlightMeshActor && mState == StateEdit)
3772 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3774 // When text is selected, show popup above top handle (and text), or below bottom handle.
3775 // topHandle: referring to the top most point of the handle or the top line of selection.
3776 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3778 topHandle = mSelectionHandleOneActualPosition;
3779 bottomHandle = mSelectionHandleTwoActualPosition;
3780 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3784 topHandle = mSelectionHandleTwoActualPosition;
3785 bottomHandle = mSelectionHandleOneActualPosition;
3786 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3788 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3789 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3791 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3793 position.x = xPosition;
3795 // Alternative position if no upper space
3796 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3797 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3801 // When no text is selected, show popup at world position of grab handle or cursor
3802 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3803 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3804 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3805 // if can't be positioned above, then position below row.
3806 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3809 // If grab handle enabled then position pop-up below the grab handle.
3810 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3814 SetPopupPosition( position, alternativePopupPosition );
3817 mPopupPanel.Show( Self(), animate );
3818 StartMonitoringStageForTouch();
3820 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3823 void TextInput::ShowPopupCutCopyPaste()
3827 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3828 // Check the selected text is whole text or not.
3829 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3831 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3834 if ( !mStyledText.empty() )
3836 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3837 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3840 if( mClipboard && mClipboard.NumberOfItems() )
3842 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3843 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3848 mPopupPanel.Hide(false);
3852 void TextInput::SetUpPopupSelection()
3855 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3856 // If no text exists then don't offer to select
3857 if ( !mStyledText.empty() )
3859 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3860 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3861 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3863 // if clipboard has valid contents then offer paste option
3864 if( mClipboard && mClipboard.NumberOfItems() )
3866 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3867 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3872 mPopupPanel.Hide(false);
3875 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3880 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3881 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3882 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3883 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3885 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3887 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3889 float closestYdifference = std::numeric_limits<float>::max();
3890 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3891 std::size_t numberOfMatchedCharacters = 0;
3893 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3894 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3896 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3898 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3899 float baselinePosition = info.mPosition.y - info.mDescender;
3901 if( info.mIsVisible )
3903 // store difference between source y point and the y position of the current character
3904 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3906 if( currentYdifference < closestYdifference )
3908 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3909 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3910 closestYdifference = currentYdifference;
3911 matchedCharacters.clear();
3912 numberOfMatchedCharacters = 0; // reset count
3915 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3916 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3918 // ignore new line character.
3919 if( !info.mIsNewParagraphChar )
3921 matchedCharacters.push_back( info );
3922 numberOfMatchedCharacters++;
3926 } // End of loop checking each character's y position in the character layout table
3928 // Check if last character is a newline, if it is
3929 // then need pretend there is an imaginary line afterwards,
3930 // and check if user is touching below previous line.
3931 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3933 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3935 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3939 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3940 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3942 bool matched( false );
3944 // 2 Iterate through matching list of y positions and find closest matching X position.
3945 for( ; it != endIt; ++it )
3947 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3949 if( info.mIsVisible )
3951 // stop when on left side of character's center.
3952 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3953 if( sourceScrollOffset.x < characterMidPointPosition )
3955 if(info.mIsRightToLeftCharacter)
3957 rightToLeftChar = true;
3959 glyphIntersection = info.mPosition.x;
3964 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3970 rightToLeftChar = lastRightToLeftChar;
3973 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3974 closestIndex = lineOffset + matchCharacterIndex;
3976 mClosestCursorPositionEOL = false; // reset
3977 if ( it == endIt && !matched )
3979 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3982 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3983 if( rightToLeftChar && lastRightToLeftChar )
3985 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3990 // closestIndex is the visual index, need to convert it to the logical index
3991 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3993 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3995 // Checks for situations where user is touching between LTR and RTL
3996 // characters. To identify if the user means the end of a LTR string
3997 // or the beginning of an RTL string, and vice versa.
3998 if( closestIndex > 0 )
4000 if( rightToLeftChar && !lastRightToLeftChar )
4005 // A: In this touch range, the user is indicating that they wish to place
4006 // the cursor at the end of the LTR text.
4007 // B: In this touch range, the user is indicating that they wish to place
4008 // the cursor at the end of the RTL text.
4010 // Result of touching A area:
4011 // [.....LTR]|[RTL......]+
4013 // |: primary cursor (for typing LTR chars)
4014 // +: secondary cursor (for typing RTL chars)
4016 // Result of touching B area:
4017 // [.....LTR]+[RTL......]|
4019 // |: primary cursor (for typing RTL chars)
4020 // +: secondary cursor (for typing LTR chars)
4022 if( sourceScrollOffset.x < glyphIntersection )
4027 else if( !rightToLeftChar && lastRightToLeftChar )
4029 if( sourceScrollOffset.x < glyphIntersection )
4036 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4037 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4038 // one further ahead
4039 if( rightToLeftChar && !lastRightToLeftChar )
4044 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4046 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4048 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4057 float TextInput::GetLineJustificationPosition() const
4059 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4060 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4061 float alignmentOffset = 0.f;
4063 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4064 if( alignment & Toolkit::Alignment::HorizontalLeft )
4066 alignmentOffset = 0.f;
4068 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4070 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4072 else if( alignment & Toolkit::Alignment::HorizontalRight )
4074 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4077 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4078 float justificationOffset = 0.f;
4080 switch( justification )
4082 case Toolkit::TextView::Left:
4084 justificationOffset = 0.f;
4087 case Toolkit::TextView::Center:
4089 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4092 case Toolkit::TextView::Right:
4094 justificationOffset = mTextLayoutInfo.mTextSize.width;
4097 case Toolkit::TextView::Justified:
4099 justificationOffset = 0.f;
4104 DALI_ASSERT_ALWAYS( false );
4108 return alignmentOffset + justificationOffset;
4111 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4113 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4114 A newline character is not inserted in this case */
4116 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4118 Vector3 cursorPosition;
4120 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4122 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4124 // end character so use
4125 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4126 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4130 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4133 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4135 // If previous character on a different line then use current characters position
4136 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4138 if ( mClosestCursorPositionEOL )
4140 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4144 cursorPosition = Vector3(currentCharInfo.mPosition);
4149 // Previous character is on same line so use position of previous character plus it's width.
4150 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4153 return cursorPosition;
4156 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4158 bool direction(false);
4159 Vector3 alternatePosition;
4160 bool alternatePositionValid(false);
4162 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4165 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4167 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4169 alternatePositionValid = false;
4170 directionRTL = false;
4172 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4174 std::size_t visualCharacterPosition;
4176 // When cursor is not at beginning, consider possibility of
4177 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4178 if(characterPosition > 0)
4180 // Cursor position should be the end of the last character.
4181 // If the last character is LTR, then the end is on the right side of the glyph.
4182 // If the last character is RTL, then the end is on the left side of the glyph.
4183 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4185 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4187 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4190 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4191 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4193 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4194 const Vector3& size = GetControlSize();
4196 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4198 --visualCharacterPosition;
4200 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4203 if(!info.mIsNewParagraphChar)
4205 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4209 // When cursor points to first character on new line, position cursor at the start of this glyph.
4210 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4212 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4213 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4214 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4216 cursorPosition.x = infoNext.mPosition.x + start;
4217 cursorPosition.y = infoNext.mPosition.y;
4221 // If cursor points to the end of text, then can only position
4222 // cursor where the new line starts based on the line-justification position.
4223 cursorPosition.x = GetLineJustificationPosition();
4225 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4227 // If this is after the last character, then we can assume that the new cursor
4228 // should be exactly one row below the current row.
4230 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4231 cursorPosition.y = info.mPosition.y + rowRect.height;
4235 // If this is not after last character, then we can use this row's height.
4236 // should be exactly one row below the current row.
4238 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4239 cursorPosition.y = info.mPosition.y + rowRect.height;
4244 directionRTL = info.mIsRightToLeftCharacter;
4246 // 1. When the cursor is neither at the beginning or the end,
4247 // we can show multiple cursors under situations when the cursor is
4248 // between RTL and LTR text...
4249 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4251 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4253 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4254 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4256 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4258 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4259 // Text: [...LTR...]|[...RTL...]
4261 // Alternate cursor pos: ^
4262 // In which case we need to display an alternate cursor for the RTL text.
4264 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4265 alternatePosition.y = infoAlt.mPosition.y;
4266 alternatePositionValid = true;
4268 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4270 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4271 // Text: |[...RTL...] [...LTR....]
4273 // Alternate cursor pos: ^
4274 // In which case we need to display an alternate cursor for the RTL text.
4276 alternatePosition.x = infoAlt.mPosition.x;
4277 alternatePosition.y = infoAlt.mPosition.y;
4278 alternatePositionValid = true;
4283 // 2. When the cursor is at the end of the text,
4284 // and we have multi-directional text,
4285 // we can also consider showing mulitple cursors.
4286 // The rule here is:
4287 // If first and last characters on row are different
4288 // Directions, then two cursors need to be displayed.
4290 // Get first logical glyph on row
4291 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4293 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4294 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4296 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4298 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4299 // Text: [...LTR...]|[...RTL...]
4301 // Alternate cursor pos: ^
4302 // In which case we need to display an alternate cursor for the RTL text, this cursor
4303 // should be at the end of the given line.
4305 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4306 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4307 alternatePosition.y = infoAlt.mPosition.y;
4308 alternatePositionValid = true;
4310 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4312 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4313 // Text: |[...RTL...] [...LTR....]
4315 // Alternate cursor pos: ^
4316 // In which case we need to display an alternate cursor for the RTL text.
4318 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4319 alternatePosition.x = infoAlt.mPosition.x;
4320 alternatePosition.y = infoAlt.mPosition.y;
4321 alternatePositionValid = true;
4324 } // characterPosition > 0
4325 else if(characterPosition == 0)
4327 // When the cursor position is at the beginning, it should be at the start of the current character.
4328 // If the current character is LTR, then the start is on the right side of the glyph.
4329 // If the current character is RTL, then the start is on the left side of the glyph.
4330 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4332 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4334 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4337 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4338 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4340 cursorPosition.x = info.mPosition.x + start;
4341 cursorPosition.y = info.mPosition.y;
4342 directionRTL = info.mIsRightToLeftCharacter;
4347 // If the character table is void, place the cursor accordingly the text alignment.
4348 const Vector3& size = GetControlSize();
4350 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4351 float alignmentOffset = 0.f;
4353 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4354 if( alignment & Toolkit::Alignment::HorizontalLeft )
4356 alignmentOffset = 0.f;
4358 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4360 alignmentOffset = 0.5f * ( size.width );
4362 else if( alignment & Toolkit::Alignment::HorizontalRight )
4364 alignmentOffset = size.width;
4367 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4368 cursorPosition.x = alignmentOffset;
4370 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4371 if( alignment & Toolkit::Alignment::VerticalTop )
4373 cursorPosition.y = mLineHeight;
4375 else if( alignment & Toolkit::Alignment::VerticalCenter )
4377 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4379 else if( alignment & Toolkit::Alignment::VerticalBottom )
4381 cursorPosition.y = size.height;
4385 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4386 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4387 if( alternatePositionValid )
4389 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4390 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4393 return cursorPosition;
4396 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4398 // scan string from current position to beginning of current line to note direction of line
4399 while(logicalPosition)
4402 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4403 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4410 return logicalPosition;
4413 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4417 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4420 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4422 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4423 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4425 min = Vector2::ZERO;
4426 max = Vector2(0.0f, mLineHeight);
4430 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4431 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4432 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4434 // If cursor is pointing to end of line, then start from last character.
4435 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4437 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4439 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4440 if( !it->mIsVisible )
4442 characterPosition = FindVisibleCharacter( Left, characterPosition );
4443 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4446 // Scan characters left and right of cursor, stopping when end of line/string reached or
4447 // y position greater than threshold of reference line.
4449 // 1. scan left until we reach the beginning or a different line.
4450 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4451 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4452 // min-x position is the left-most char's left (x)
4453 // max-x position is the right-most char's right (x)
4454 // min-y position is the minimum of all character's top (y)
4455 // max-y position is the maximum of all character's bottom (y+height)
4456 min.y = validCharIt->mPosition.y;
4457 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4462 min.y = std::min(min.y, validCharIt->mPosition.y);
4463 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4472 if( (it->mPosition.y < referenceLine) ||
4473 (it->mIsNewParagraphChar) ||
4480 // info refers to the first character on this line.
4481 min.x = validCharIt->mPosition.x;
4483 // 2. scan right until we reach end or a different line
4484 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4485 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4489 if( (it->mPosition.y > referenceLine) ||
4490 (it->mIsNewParagraphChar) ||
4497 min.y = std::min(min.y, validCharIt->mPosition.y);
4498 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4503 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4505 if ( validCharIt != end )
4507 // info refers to the last character on this line.
4508 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4511 return Size( max.x - min.x, max.y - min.y );
4514 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4516 Actor popUpPanel = mPopupPanel.GetRootActor();
4518 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4524 Dali::Actor parent( touchedActor.GetParent() );
4528 return WasTouchedCheck( parent );
4535 void TextInput::StartMonitoringStageForTouch()
4537 Stage stage = Stage::GetCurrent();
4538 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4541 void TextInput::EndMonitoringStageForTouch()
4543 Stage stage = Stage::GetCurrent();
4544 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4547 void TextInput::OnStageTouched(const TouchEvent& event)
4549 if( event.GetPointCount() > 0 )
4551 if ( TouchPoint::Down == event.GetPoint(0).state )
4553 const Actor touchedActor(event.GetPoint(0).hitActor);
4555 bool popUpShown( false );
4557 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4562 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4564 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4566 EndMonitoringStageForTouch();
4567 HidePopup( true, false );
4570 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4572 EndMonitoringStageForTouch();
4573 ShowGrabHandleAndSetVisibility( false );
4579 void TextInput::SelectText(std::size_t start, std::size_t end)
4581 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4582 IsGrabHandleEnabled()?"true":"false",
4583 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4584 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4585 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4587 StartMonitoringStageForTouch();
4589 if ( mEditModeActive ) // Only allow text selection when in edit mode
4591 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4592 mSelectingText = true;
4594 mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4596 ImfManager imfManager = ImfManager::Get();
4599 imfManager.SetCursorPosition ( mCursorPosition );
4600 imfManager.SetSurroundingText( GetText() );
4601 imfManager.NotifyCursorPosition();
4603 // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead.
4605 // Hide grab handle when selecting.
4606 ShowGrabHandleAndSetVisibility( false );
4608 if( start != end ) // something to select
4610 SetCursorVisibility( false );
4611 StopCursorBlinkTimer();
4613 CreateSelectionHandles(start, end);
4616 const TextStyle oldInputStyle( mInputStyle );
4617 mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4619 if( oldInputStyle != mInputStyle )
4621 // Updates the line height accordingly with the input style.
4624 EmitStyleChangedSignal();
4630 mSelectingText = false;
4634 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4636 MarkupProcessor::StyledTextArray currentSelectedText;
4638 if ( IsTextSelected() )
4640 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4641 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4643 for(; it != end; ++it)
4645 MarkupProcessor::StyledText& styledText( *it );
4646 currentSelectedText.push_back( styledText );
4649 return currentSelectedText;
4652 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4654 const std::size_t beginIndex = std::min( begin, end );
4655 const std::size_t endIndex = std::max( begin, end );
4658 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4660 // Create a styled text array used to replace the text into the text-view.
4661 MarkupProcessor::StyledTextArray text;
4662 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4664 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4665 GetTextLayoutInfo();
4667 if( IsScrollEnabled() )
4669 // Need to set the scroll position as the text's size may have changed.
4670 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4673 ShowGrabHandleAndSetVisibility( false );
4679 // Set Handle positioning as the new style may have repositioned the characters.
4680 SetSelectionHandlePosition(HandleOne);
4681 SetSelectionHandlePosition(HandleTwo);
4684 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4686 // Just hide the grab handle when keyboard is hidden.
4687 if (!keyboardShown )
4689 ShowGrabHandleAndSetVisibility( false );
4691 // If the keyboard is not now being shown, then hide the popup panel
4692 mPopupPanel.Hide( true );
4696 // Removes highlight and resumes edit mode state
4697 void TextInput::RemoveHighlight()
4699 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4701 if ( mHighlightMeshActor )
4703 if ( mSelectionHandleOne )
4705 mActiveLayer.Remove( mSelectionHandleOne );
4706 mSelectionHandleOne.Reset();
4707 mSelectionHandleOneOffset.x = 0.0f;
4709 if ( mSelectionHandleTwo )
4711 mActiveLayer.Remove( mSelectionHandleTwo );
4712 mSelectionHandleTwo.Reset();
4713 mSelectionHandleTwoOffset.x = 0.0f;
4716 mNewHighlightInfo.mQuadList.clear();
4718 Self().Remove( mHighlightMeshActor );
4720 SetCursorVisibility( true );
4721 StartCursorBlinkTimer();
4723 mHighlightMeshActor.Reset();
4724 // NOTE: We cannot dereference mHighlightMesh, due
4725 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4730 mSelectionHandleOnePosition = 0;
4731 mSelectionHandleTwoPosition = 0;
4734 void TextInput::CreateHighlight()
4736 if ( !mHighlightMeshActor )
4738 mMeshData = MeshData( );
4739 mMeshData.SetHasNormals( true );
4741 mCustomMaterial = Material::New("CustomMaterial");
4742 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4744 mMeshData.SetMaterial( mCustomMaterial );
4746 mHighlightMesh = Mesh::New( mMeshData );
4748 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4749 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4750 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4751 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4752 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4753 mHighlightMeshActor.SetAffectedByLighting(false);
4755 Self().Add(mHighlightMeshActor);
4760 bool TextInput::CopySelectedTextToClipboard()
4762 mCurrentCopySelecton.clear();
4764 mCurrentCopySelecton = GetSelectedText();
4766 std::string stringToStore;
4768 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4769 * a marked up string.
4771 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4772 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4773 bool success = mClipboard.SetItem( stringToStore );
4777 void TextInput::PasteText( const Text& text )
4779 // Update Flag, indicates whether to update the text-input contents or not.
4780 // Any key stroke that results in a visual change of the text-input should
4781 // set this flag to true.
4782 bool update = false;
4783 if( mHighlightMeshActor )
4785 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4786 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4788 ImfManager imfManager = ImfManager::Get();
4791 imfManager.SetCursorPosition( mCursorPosition );
4792 imfManager.NotifyCursorPosition();
4794 DeleteHighlightedText( true );
4798 bool textExceedsMaximunNumberOfCharacters = false;
4799 bool textExceedsBoundary = false;
4801 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4803 mCursorPosition += insertedStringLength;
4804 ImfManager imfManager = ImfManager::Get();
4807 imfManager.SetCursorPosition ( mCursorPosition );
4808 imfManager.NotifyCursorPosition();
4811 update = update || ( insertedStringLength > 0 );
4818 if( insertedStringLength < text.GetLength() )
4820 EmitMaxInputCharactersReachedSignal();
4823 if( textExceedsBoundary )
4825 EmitInputTextExceedsBoundariesSignal();
4829 void TextInput::SetTextDirection()
4831 // Put the cursor to the right if we are empty and an RTL language is being used.
4832 if ( mStyledText.empty() )
4834 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4836 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4837 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4839 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4840 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4842 int alignment( mDisplayedTextView.GetTextAlignment() &
4843 ( Toolkit::Alignment::VerticalTop |
4844 Toolkit::Alignment::VerticalCenter |
4845 Toolkit::Alignment::VerticalBottom |
4846 Toolkit::Alignment::HorizontalCenter ) );
4847 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4849 // If our alignment is in the center, then do not change.
4850 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4852 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4855 // If our justification is in the center, then do not change.
4856 if ( justification != Toolkit::TextView::Center )
4858 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4861 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4862 mDisplayedTextView.SetLineJustification( justification );
4866 void TextInput::UpdateLineHeight()
4868 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4869 mLineHeight = font.GetLineHeight();
4871 // 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.
4873 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4875 if( !mExceedEnabled || shrink )
4877 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4881 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4883 std::size_t position = 0;
4885 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4891 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4893 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4895 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4901 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4902 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4904 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4910 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4915 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4922 void TextInput::SetSortModifier( float depthOffset )
4924 if(mDisplayedTextView)
4926 mDisplayedTextView.SetSortModifier(depthOffset);
4930 void TextInput::SetSnapshotModeEnabled( bool enable )
4932 if(mDisplayedTextView)
4934 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4938 bool TextInput::IsSnapshotModeEnabled() const
4940 bool snapshotEnabled = false;
4942 if(mDisplayedTextView)
4944 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4947 return snapshotEnabled;
4950 void TextInput::SetMarkupProcessingEnabled( bool enable )
4952 mMarkUpEnabled = enable;
4955 bool TextInput::IsMarkupProcessingEnabled() const
4957 return mMarkUpEnabled;
4960 void TextInput::SetScrollEnabled( bool enable )
4962 if( mDisplayedTextView )
4964 mDisplayedTextView.SetScrollEnabled( enable );
4969 // Don't set cursor's and handle's visibility to false if they are outside the
4970 // boundaries of the text-input.
4971 mIsCursorInScrollArea = true;
4972 mIsGrabHandleInScrollArea = true;
4973 if( mSelectionHandleOne && mSelectionHandleTwo )
4975 mSelectionHandleOne.SetVisible( true );
4976 mSelectionHandleTwo.SetVisible( true );
4978 if( mHighlightMeshActor )
4980 mHighlightMeshActor.SetVisible( true );
4986 bool TextInput::IsScrollEnabled() const
4988 bool scrollEnabled = false;
4990 if( mDisplayedTextView )
4992 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4995 return scrollEnabled;
4998 void TextInput::SetScrollPosition( const Vector2& position )
5000 if( mDisplayedTextView )
5002 mDisplayedTextView.SetScrollPosition( position );
5006 Vector2 TextInput::GetScrollPosition() const
5008 Vector2 scrollPosition;
5010 if( mDisplayedTextView )
5012 scrollPosition = mDisplayedTextView.GetScrollPosition();
5015 return scrollPosition;
5018 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5020 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5021 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5022 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5024 // Add style to the new input text.
5025 MarkupProcessor::StyledTextArray textToInsert;
5026 for( std::size_t i = 0; i < insertedStringLength; ++i )
5028 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5029 textToInsert.push_back( newStyledCharacter );
5032 //Insert text to the TextView.
5033 const bool emptyTextView = mStyledText.empty();
5034 if( emptyTextView && mPlaceHolderSet )
5036 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5037 mDisplayedTextView.SetText( textToInsert );
5041 if( 0 == numberOfCharactersToReplace )
5043 mDisplayedTextView.InsertTextAt( position, textToInsert );
5047 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5050 mPlaceHolderSet = false;
5052 if( textToInsert.empty() )
5054 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5055 GetTextLayoutInfo();
5059 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5060 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5063 textExceedsBoundary = false;
5065 if( !mExceedEnabled )
5067 const Vector3& size = GetControlSize();
5069 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5071 // If new text does not fit within TextView
5072 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5073 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5074 GetTextLayoutInfo();
5075 textExceedsBoundary = true;
5076 insertedStringLength = 0;
5079 if( textExceedsBoundary )
5081 // Add the part of the text which fits on the text-input.
5083 // Split the text which doesn't fit in two halves.
5084 MarkupProcessor::StyledTextArray firstHalf;
5085 MarkupProcessor::StyledTextArray secondHalf;
5086 SplitText( textToInsert, firstHalf, secondHalf );
5088 // Clear text. This text will be filled with the text inserted.
5089 textToInsert.clear();
5091 // Where to insert the text.
5092 std::size_t positionToInsert = position;
5094 bool end = text.GetLength() <= 1;
5097 // Insert text and check ...
5098 const std::size_t textLength = firstHalf.size();
5099 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5100 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5102 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5104 // Inserted text doesn't fit.
5106 // Remove inserted text
5107 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5108 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5110 // The iteration finishes when only one character doesn't fit.
5111 end = textLength <= 1;
5115 // Prepare next two halves for next iteration.
5116 MarkupProcessor::StyledTextArray copyText = firstHalf;
5117 SplitText( copyText, firstHalf, secondHalf );
5124 // store text to be inserted in mStyledText.
5125 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5127 // Increase the inserted characters counter.
5128 insertedStringLength += textLength;
5130 // Prepare next two halves for next iteration.
5131 MarkupProcessor::StyledTextArray copyText = secondHalf;
5132 SplitText( copyText, firstHalf, secondHalf );
5134 // Update where next text has to be inserted
5135 positionToInsert += textLength;
5141 if( textToInsert.empty() && emptyTextView )
5143 // No character has been added and the text-view was empty.
5144 // Set the placeholder text.
5145 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5146 mPlaceHolderSet = true;
5150 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5151 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5152 mPlaceHolderSet = false;
5155 return insertedStringLength;
5158 void TextInput::GetTextLayoutInfo()
5160 if( mStyledText.empty() )
5162 // The text-input has no text, clear the text-view's layout info.
5163 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5167 if( mDisplayedTextView )
5169 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5173 // There is no text-view.
5174 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5179 void TextInput::SetOffsetFromText( const Vector4& offset )
5181 mPopupOffsetFromText = offset;
5184 const Vector4& TextInput::GetOffsetFromText() const
5186 return mPopupOffsetFromText;
5189 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5191 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5195 TextInput& textInputImpl( GetImpl( textInput ) );
5197 switch ( propertyIndex )
5199 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5201 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5204 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5206 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5209 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5211 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5214 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5216 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5219 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5221 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5224 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5226 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5229 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5231 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5234 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5236 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5239 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5241 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5244 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5246 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5249 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5251 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5254 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5256 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5259 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5261 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5264 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5266 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5269 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5271 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5274 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5276 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5282 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5284 Property::Value value;
5286 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5290 TextInput& textInputImpl( GetImpl( textInput ) );
5292 switch ( propertyIndex )
5294 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5296 value = textInputImpl.GetMaterialDiffuseColor();
5299 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5301 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5304 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5306 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5309 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5311 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5314 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5316 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5319 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5321 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5324 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5326 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5329 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5331 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5334 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5336 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5339 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5341 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5344 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5346 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5349 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5351 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5354 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5356 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5359 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5361 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5364 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5366 value = textInputImpl.GetOffsetFromText();
5369 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5371 value = textInputImpl.mCursor.GetCurrentColor();
5378 void TextInput::EmitStyleChangedSignal()
5380 // emit signal if input style changes.
5381 Toolkit::TextInput handle( GetOwner() );
5382 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5385 void TextInput::EmitTextModified()
5387 // emit signal when text changes.
5388 Toolkit::TextInput handle( GetOwner() );
5389 mTextModifiedSignal.Emit( handle );
5393 void TextInput::EmitMaxInputCharactersReachedSignal()
5395 // emit signal if max characters is reached during text input.
5396 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5398 Toolkit::TextInput handle( GetOwner() );
5399 mMaxInputCharactersReachedSignalV2.Emit( handle );
5402 void TextInput::EmitInputTextExceedsBoundariesSignal()
5404 // Emit a signal when the input text exceeds the boundaries of the text input.
5406 Toolkit::TextInput handle( GetOwner() );
5407 mInputTextExceedBoundariesSignalV2.Emit( handle );
5410 } // namespace Internal
5412 } // namespace Toolkit