2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
25 #include <dali/integration-api/debug.h>
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
58 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
72 const std::string NEWLINE( "\n" );
74 const TextStyle DEFAULT_TEXT_STYLE;
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
81 * Selection state enumeration (FSM)
85 SelectionNone, ///< Currently not encountered selected section.
86 SelectionStarted, ///< Encountered selected section
87 SelectionFinished ///< Finished selected section
91 * Whether the given style is the default style or not.
92 * @param[in] style The given style.
93 * @return \e true if the given style is the default. Otherwise it returns \e false.
95 bool IsDefaultStyle( const TextStyle& style )
97 return DEFAULT_TEXT_STYLE == style;
101 * Whether the given styled text is using the default style or not.
102 * @param[in] textArray The given text.
103 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
107 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
109 const TextStyle& style( (*it).mStyle );
111 if( !IsDefaultStyle( style ) )
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
122 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
126 if( ( *it ).mIsVisible )
128 return --cursorPosition;
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
139 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
141 if( ( *it ).mIsVisible )
143 return cursorPosition;
149 return cursorPosition;
153 * Whether the given position plus the cursor size offset is inside the given boundary.
155 * @param[in] position The given position.
156 * @param[in] cursorSize The cursor size.
157 * @param[in] controlSize The given boundary.
159 * @return whether the given position is inside the given boundary.
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
163 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
170 * Splits a text in two halves.
172 * If the text's number of characters is odd, firstHalf has one more character.
174 * @param[in] text The text to be split.
175 * @param[out] firstHalf The first half of the text.
176 * @param[out] secondHalf The second half of the text.
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
185 const std::size_t textLength = text.size();
186 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
188 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
192 } // end of namespace
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
205 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
207 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
208 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
209 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
210 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
211 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
212 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
213 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
215 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
255 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
257 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
259 QuadCoordinates quad(x1, y1, x2, y2);
260 mQuadList.push_back( quad );
263 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
265 for(std::size_t i = 0;i < mQuadList.size(); i++)
267 QuadCoordinates& quad = mQuadList[i];
269 quad.min.Clamp(min, max);
270 quad.max.Clamp(min, max);
274 // [TextInput] ////////////////////////////////////////////////////////////////
276 Dali::Toolkit::TextInput TextInput::New()
278 // Create the implementation
279 TextInputPtr textInput(new TextInput());
280 // Pass ownership to CustomActor via derived handle
281 Dali::Toolkit::TextInput handle(*textInput);
283 textInput->Initialize();
288 TextInput::TextInput()
289 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
294 mDisplayedTextView(),
295 mStyledPlaceHolderText(),
296 mMaxStringLength( DEFAULT_MAX_SIZE ),
297 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
298 mCursorPosition( 0 ),
299 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
300 mIsSelectionHandleOneFlipped( false ),
301 mIsSelectionHandleTwoFlipped( false ),
302 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
303 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
304 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
305 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
306 mSelectionHandleOnePosition( 0 ),
307 mSelectionHandleTwoPosition( 0 ),
309 mPreEditStartPosition( 0 ),
310 mPreEditLength ( 0 ),
311 mNumberOfSurroundingCharactersDeleted( 0 ),
312 mTouchStartTime( 0 ),
314 mCurrentCopySelecton(),
317 mScrollDisplacement(),
318 mCurrentHandlePosition(),
319 mCurrentSelectionId(),
320 mCurrentSelectionHandlePosition(),
321 mRequestedSelection( 0, 0 ),
322 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
323 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mMaterialColor( LIGHTBLUE ),
326 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
327 mOverrideAutomaticAlignment( false ),
328 mCursorRTLEnabled( false ),
329 mClosestCursorPositionEOL ( false ),
330 mCursorBlinkStatus( true ),
331 mCursorVisibility( false ),
332 mGrabHandleVisibility( false ),
333 mIsCursorInScrollArea( true ),
334 mIsGrabHandleInScrollArea( true ),
335 mEditModeActive( false ),
336 mEditOnTouch( true ),
337 mTextSelection( true ),
338 mExceedEnabled( true ),
339 mGrabHandleEnabled( true ),
340 mIsSelectionHandleFlipEnabled( true ),
341 mPreEditFlag( false ),
342 mIgnoreCommitFlag( false ),
343 mIgnoreFirstCommitFlag( false ),
344 mSelectingText( false ),
345 mPreserveCursorPosition( false ),
346 mSelectTextOnCommit( false ),
347 mUnderlinedPriorToPreEdit ( false ),
348 mCommitByKeyInput( false ),
349 mPlaceHolderSet( false ),
350 mMarkUpEnabled( false )
352 // Updates the line height accordingly with the input style.
356 TextInput::~TextInput()
358 StopCursorBlinkTimer();
363 std::string TextInput::GetText() const
367 // Return text-view's text only if the text-input's text is not empty
368 // in order to not to return the placeholder text.
369 if( !mStyledText.empty() )
371 text = mDisplayedTextView.GetText();
377 std::string TextInput::GetMarkupText() const
379 std::string markupString;
380 MarkupProcessor::GetMarkupString( mStyledText, markupString );
385 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
387 // Get the placeholder styled text array from the markup string.
388 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
390 if( mStyledText.empty() )
392 // Set the placeholder text only if the styled text is empty.
393 mDisplayedTextView.SetText( mStyledPlaceHolderText );
394 mPlaceHolderSet = true;
398 std::string TextInput::GetPlaceholderText()
400 // Traverses the styled placeholder array getting only the text.
401 // Note that for some languages a 'character' could be represented by more than one 'char'
403 std::string placeholderText;
404 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
406 placeholderText.append( (*it).mText.GetText() );
409 return placeholderText ;
412 void TextInput::SetInitialText(const std::string& initialText)
414 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
416 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
418 mPreEditFlag = false;
419 mIgnoreCommitFlag = true;
422 SetText( initialText );
423 PreEditReset( false ); // Reset keyboard as text changed
426 void TextInput::SetText(const std::string& initialText)
428 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
430 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
432 if( mStyledText.empty() )
434 // If the initial text is empty, set the placeholder text.
435 mDisplayedTextView.SetText( mStyledPlaceHolderText );
436 mPlaceHolderSet = true;
440 mDisplayedTextView.SetText( mStyledText );
441 mPlaceHolderSet = false;
446 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
448 ImfManager imfManager = ImfManager::Get();
451 imfManager.SetCursorPosition( mCursorPosition );
452 imfManager.SetSurroundingText( initialText );
453 imfManager.NotifyCursorPosition();
456 if( IsScrollEnabled() )
458 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
461 ShowGrabHandleAndSetVisibility( false );
470 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
472 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
474 mDisplayedTextView.SetText( styleText );
475 mPlaceHolderSet = false;
477 // If text alignment hasn't been manually set by application developer, then we
478 // automatically determine the alignment based on the content of the text i.e. what
479 // language the text begins with.
480 // TODO: This should determine different alignments for each line (broken by '\n') of text.
481 if(!mOverrideAutomaticAlignment)
483 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
484 bool leftToRight(true);
486 if( !styleText.empty() )
488 bool breakOut(false);
490 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
492 const Text& text = textIter->mText;
494 for( std::size_t i = 0; i < text.GetLength(); ++i )
496 Character character( text[i] );
497 if( character.GetCharacterDirection() != Character::Neutral )
499 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
507 // Based on this direction, either left or right align text if not manually set by application developer.
508 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
509 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
510 Toolkit::Alignment::VerticalTop ) );
511 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
517 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
519 mMaxStringLength = maxChars;
522 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
524 DALI_ASSERT_DEBUG( maxLines > 0 )
528 mNumberOflinesLimit = maxLines;
532 std::size_t TextInput::GetNumberOfLinesLimit() const
534 return mNumberOflinesLimit;
537 std::size_t TextInput::GetNumberOfCharacters() const
539 return mStyledText.size();
543 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
545 mMaterialColor = color;
546 if ( mCustomMaterial )
548 mCustomMaterial.SetDiffuseColor( mMaterialColor );
549 mMeshData.SetMaterial( mCustomMaterial );
553 const Vector4& TextInput::GetMaterialDiffuseColor() const
555 return mMaterialColor;
560 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
562 return mInputStartedSignalV2;
565 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
567 return mInputFinishedSignalV2;
570 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
572 return mCutAndPasteToolBarDisplayedV2;
575 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
577 return mStyleChangedSignalV2;
580 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
582 return mTextModifiedSignal;
585 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
587 return mMaxInputCharactersReachedSignalV2;
590 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
592 return mInputTextExceedBoundariesSignalV2;
595 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
597 Dali::BaseHandle handle( object );
599 bool connected( true );
600 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
602 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
604 textInput.InputStartedSignal().Connect( tracker, functor );
606 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
608 textInput.InputFinishedSignal().Connect( tracker, functor );
610 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
612 textInput.StyleChangedSignal().Connect( tracker, functor );
614 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
616 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
618 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
620 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
624 // signalName does not match any signal
631 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
635 // update line height before calculate the actual position.
640 if( setCursorOnTouchPoint )
642 // Sets the cursor position for the given touch point.
643 ReturnClosestIndex( touchPoint, mCursorPosition );
645 // Creates the grab handle.
646 if( IsGrabHandleEnabled() )
648 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
652 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
653 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
654 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
655 ShowGrabHandleAndSetVisibility( true );
657 // Scrolls the text-view if needed.
658 if( IsScrollEnabled() )
660 ScrollTextViewToMakeCursorVisible( cursorPosition );
666 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
678 bool TextInput::IsEditable() const
680 return mEditModeActive;
683 void TextInput::SetEditOnTouch( bool editOnTouch )
685 mEditOnTouch = editOnTouch;
688 bool TextInput::IsEditOnTouch() const
693 void TextInput::SetTextSelectable( bool textSelectable )
695 mTextSelection = textSelectable;
698 bool TextInput::IsTextSelectable() const
700 return mTextSelection;
703 bool TextInput::IsTextSelected() const
705 return mHighlightMeshActor;
708 void TextInput::DeSelectText()
715 void TextInput::SetGrabHandleImage(Dali::Image image )
719 CreateGrabHandle(image);
723 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
725 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
729 mCursor.SetImage( image );
730 mCursor.SetNinePatchBorder( border );
734 Vector3 TextInput::GetSelectionHandleSize()
736 return DEFAULT_SELECTION_HANDLE_SIZE;
739 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
741 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
745 mCursorRTL.SetImage( image);
746 mCursorRTL.SetNinePatchBorder( border );
750 void TextInput::EnableGrabHandle(bool toggle)
752 // enables grab handle with will in turn de-activate magnifier
753 mGrabHandleEnabled = toggle;
756 bool TextInput::IsGrabHandleEnabled()
758 // if false then magnifier will be shown instead.
759 return mGrabHandleEnabled;
762 void TextInput::EnableSelectionHandleFlip( bool toggle )
764 // Deprecated function. To be removed.
765 mIsSelectionHandleFlipEnabled = toggle;
768 bool TextInput::IsSelectionHandleFlipEnabled()
770 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
774 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
776 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
777 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
778 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
780 mSelectionHandleFlipMargin = margin;
783 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
785 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
786 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
788 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
789 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
791 const Vector4 boundary( originX,
793 originX + boundingRectangle.width,
794 originY + boundingRectangle.height );
796 mBoundingRectangleWorldCoordinates = boundary;
799 const Rect<float> TextInput::GetBoundingRectangle() const
801 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
803 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
804 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
806 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
811 const Vector4& TextInput::GetSelectionHandleFlipMargin()
813 return mSelectionHandleFlipMargin;
816 void TextInput::SetTextColor( const Vector4& color )
818 mDisplayedTextView.SetColor( color );
821 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
823 if( style != mInputStyle )
826 bool emitSignal = false;
828 // mask: modify style according to mask, if different emit signal.
829 const TextStyle oldInputStyle( mInputStyle );
831 // Copy the new style.
832 mInputStyle.Copy( style, mask );
834 // if style has changed, emit signal.
835 if( oldInputStyle != mInputStyle )
840 // Updates the line height accordingly with the input style.
843 // Changing font point size will require the cursor to be re-sized
848 EmitStyleChangedSignal();
853 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
855 if ( IsTextSelected() )
857 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
858 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
860 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
862 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
865 // Keeps the old style to be compared with the new one.
866 const TextStyle oldInputStyle( mInputStyle );
868 // Copy only those parameters from the style which are set in the mask.
869 mInputStyle.Copy( style, mask );
871 if( mInputStyle != oldInputStyle )
873 // Updates the line height accordingly with the input style.
876 EmitStyleChangedSignal();
881 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
883 if( !mStyledText.empty() )
885 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
889 TextStyle TextInput::GetStyleAtCursor() const
893 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
895 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
896 style = mStyledText.at( mCursorPosition-1 ).mStyle;
902 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
904 Dali::Font defaultFont = Dali::Font::New();
905 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
912 TextStyle TextInput::GetStyleAt( std::size_t position ) const
914 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
916 if( position >= mStyledText.size() )
918 position = mStyledText.size() - 1;
921 return mStyledText.at( position ).mStyle;
924 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
926 mDisplayedTextView.SetTextAlignment( align );
927 mOverrideAutomaticAlignment = true;
930 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
932 mDisplayedTextView.SetLineJustification( justification );
933 mOverrideAutomaticAlignment = true;
936 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
938 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
941 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
943 return mDisplayedTextView.GetFadeBoundary();
946 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
948 return mDisplayedTextView.GetTextAlignment();
951 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
953 mDisplayedTextView.SetMultilinePolicy( policy );
956 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
958 return mDisplayedTextView.GetMultilinePolicy();
961 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
963 mDisplayedTextView.SetWidthExceedPolicy( policy );
966 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
968 return mDisplayedTextView.GetWidthExceedPolicy();
971 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
973 mDisplayedTextView.SetHeightExceedPolicy( policy );
976 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
978 return mDisplayedTextView.GetHeightExceedPolicy();
981 void TextInput::SetExceedEnabled( bool enable )
983 mExceedEnabled = enable;
986 bool TextInput::GetExceedEnabled() const
988 return mExceedEnabled;
991 void TextInput::SetBackground(Dali::Image image )
993 // TODO Should add this function and add public api to match.
996 bool TextInput::OnTouchEvent(const TouchEvent& event)
1001 bool TextInput::OnKeyEvent(const KeyEvent& event)
1003 switch( event.state )
1005 case KeyEvent::Down:
1007 return OnKeyDownEvent(event);
1013 return OnKeyUpEvent(event);
1025 void TextInput::OnKeyInputFocusGained()
1027 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1029 mEditModeActive = true;
1031 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1033 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1035 // Updates the line height accordingly with the input style.
1038 // Connect the signals to use in text input.
1039 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1040 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1042 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1045 GetTextLayoutInfo();
1048 SetCursorVisibility( true );
1049 StartCursorBlinkTimer();
1051 Toolkit::TextInput handle( GetOwner() );
1052 mInputStartedSignalV2.Emit( handle );
1054 ImfManager imfManager = ImfManager::Get();
1058 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1060 // Notify that the text editing start.
1061 imfManager.Activate();
1063 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1064 imfManager.SetRestoreAferFocusLost( true );
1066 imfManager.SetCursorPosition( mCursorPosition );
1067 imfManager.NotifyCursorPosition();
1070 mClipboard = Clipboard::Get(); // Store handle to clipboard
1072 // Now in edit mode we can accept string to paste from clipboard
1073 if( Adaptor::IsAvailable() )
1075 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1078 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1083 void TextInput::OnKeyInputFocusLost()
1085 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1089 // If key input focus is lost, it removes the
1090 // underline from the last pre-edit text.
1091 RemovePreEditStyle();
1092 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1093 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1097 ImfManager imfManager = ImfManager::Get();
1100 // The text editing is finished. Therefore the imf manager don't have restore activation.
1101 imfManager.SetRestoreAferFocusLost( false );
1103 // Notify that the text editing finish.
1104 imfManager.Deactivate();
1106 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1108 // Disconnect signal used the text input.
1109 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1111 Toolkit::TextInput handle( GetOwner() );
1112 mInputFinishedSignalV2.Emit( handle );
1113 mEditModeActive = false;
1114 mPreEditFlag = false;
1116 SetCursorVisibility( false );
1117 StopCursorBlinkTimer();
1119 ShowGrabHandleAndSetVisibility( false );
1122 // No longer in edit mode so do not want to receive string from clipboard
1123 if( Adaptor::IsAvailable() )
1125 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1128 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1130 Clipboard clipboard = Clipboard::Get();
1134 clipboard.HideClipboard();
1139 void TextInput::OnControlStageConnection()
1141 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1143 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1145 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1149 void TextInput::CreateActiveLayer()
1151 Actor self = Self();
1152 mActiveLayer = Layer::New();
1154 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1155 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1156 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1158 self.Add( mActiveLayer );
1159 mActiveLayer.RaiseToTop();
1162 void TextInput::OnInitialize()
1164 CreateTextViewActor();
1168 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1169 // different positions depending on language)
1170 Image mCursorImage = Image::New( DEFAULT_CURSOR );
1171 mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1172 mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1174 Actor self = Self();
1175 self.Add( mCursor );
1176 self.Add( mCursorRTL );
1178 mCursorVisibility = false;
1180 CreateActiveLayer(); // todo move this so layer only created when needed.
1182 // Assign names to image actors
1183 mCursor.SetName("mainCursor");
1184 mCursorRTL.SetName("rtlCursor");
1187 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1189 mDisplayedTextView.SetSize( targetSize );
1190 GetTextLayoutInfo();
1191 mActiveLayer.SetSize(targetSize);
1194 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1196 Relayout( mDisplayedTextView, size, container );
1197 Relayout( mPopupPanel.GetRootActor(), size, container );
1199 GetTextLayoutInfo();
1204 Vector3 TextInput::GetNaturalSize()
1206 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1208 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1210 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1211 naturalSize.height = mLineHeight;
1217 float TextInput::GetHeightForWidth( float width )
1219 float height = mDisplayedTextView.GetHeightForWidth( width );
1221 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1223 // If the height is zero, it means there is no text. Let's return the cursor height.
1224 height = mLineHeight;
1230 /*end of Virtual methods from parent*/
1232 // Private Internal methods
1234 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1236 switch (gesture.state)
1238 case Gesture::Started:
1239 // fall through so code not duplicated
1240 case Gesture::Continuing:
1242 if (actor == mGrabArea)
1244 SetCursorVisibility( true );
1245 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1246 MoveGrabHandle( gesture.displacement );
1247 HidePopup(); // Do not show popup whilst handle is moving
1249 else if (actor == mHandleOneGrabArea)
1251 // the displacement in PanGesture is affected by the actor's rotation.
1252 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1253 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1255 MoveSelectionHandle( HandleOne, gesture.displacement );
1257 mState = StateDraggingHandle;
1260 else if (actor == mHandleTwoGrabArea)
1262 // the displacement in PanGesture is affected by the actor's rotation.
1263 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1264 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1266 MoveSelectionHandle( HandleTwo, gesture.displacement );
1268 mState = StateDraggingHandle;
1274 case Gesture::Finished:
1276 // Revert back to non-pressed selection handle images
1277 if (actor == mGrabArea)
1279 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1280 SetCursorVisibility( true );
1281 SetUpPopupSelection();
1284 if (actor == mHandleOneGrabArea)
1286 // the displacement in PanGesture is affected by the actor's rotation.
1287 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1288 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1290 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1292 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1294 ShowPopupCutCopyPaste();
1296 if (actor == mHandleTwoGrabArea)
1298 // the displacement in PanGesture is affected by the actor's rotation.
1299 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1300 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1302 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1304 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1306 ShowPopupCutCopyPaste();
1315 // Stop the flashing animation so easy to see when moved.
1316 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1318 if (touch.GetPoint(0).state == TouchPoint::Down)
1320 SetCursorVisibility( true );
1321 StopCursorBlinkTimer();
1323 else if (touch.GetPoint(0).state == TouchPoint::Up)
1325 SetCursorVisibility( true );
1326 StartCursorBlinkTimer();
1331 // selection handle one
1332 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1334 if (touch.GetPoint(0).state == TouchPoint::Down)
1336 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1338 else if (touch.GetPoint(0).state == TouchPoint::Up)
1340 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1345 // selection handle two
1346 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1348 if (touch.GetPoint(0).state == TouchPoint::Down)
1350 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1352 else if (touch.GetPoint(0).state == TouchPoint::Up)
1354 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1359 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1361 // If text exists then select nearest word.
1362 if ( !mStyledText.empty())
1366 ShowGrabHandleAndSetVisibility( false );
1371 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1372 // converts the pre-edit word being displayed to a committed word.
1373 if ( !mUnderlinedPriorToPreEdit )
1376 style.SetUnderline( false );
1377 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1379 mPreEditFlag = false;
1380 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1381 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1382 PreEditReset( false );
1384 mCursorPosition = 0;
1386 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1387 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1389 ImfManager imfManager = ImfManager::Get();
1392 imfManager.SetCursorPosition ( mCursorPosition );
1393 imfManager.NotifyCursorPosition();
1396 std::size_t start = 0;
1397 std::size_t end = 0;
1398 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1400 SelectText( start, end );
1402 // if no text but clipboard has content then show paste option
1403 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1405 ShowPopupCutCopyPaste();
1408 // If no text and clipboard empty then do nothing
1411 // TODO: Change the function name to be more general.
1412 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1414 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1415 , (mEditOnTouch)?"true":"false"
1416 , (mEditModeActive)?"true":"false");
1418 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1423 if( mGrabArea == actor )
1425 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1427 SetUpPopupSelection();
1437 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1439 // Initially don't create the grab handle.
1440 bool createGrabHandle = false;
1442 if ( !mEditModeActive )
1444 // update line height before calculate the actual position.
1447 // Only start edit mode if TextInput configured to edit on touch
1450 // Set the initial cursor position in the tap point.
1451 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1453 // Create the grab handle.
1454 // TODO Make this a re-usable function.
1455 if ( IsGrabHandleEnabled() )
1457 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1461 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1462 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1463 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1464 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1468 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1469 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1470 // otherwise the Grab handle will be shown when selecting.
1477 // Show the keyboard if it was hidden.
1478 if (!VirtualKeyboard::IsVisible())
1480 VirtualKeyboard::Show();
1483 // Reset keyboard as tap event has occurred.
1484 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1485 PreEditReset( true );
1487 GetTextLayoutInfo();
1489 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1491 // 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.
1493 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1495 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1497 // Notify keyboard so it can 're-capture' word for predictive text.
1498 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1499 ImfManager imfManager = ImfManager::Get();
1502 imfManager.SetCursorPosition ( mCursorPosition );
1503 imfManager.NotifyCursorPosition();
1505 const TextStyle oldInputStyle( mInputStyle );
1507 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1511 // Create the grab handle.
1512 // Grab handle is created later.
1513 createGrabHandle = true;
1515 if( oldInputStyle != mInputStyle )
1517 // Updates the line height accordingly with the input style.
1520 EmitStyleChangedSignal();
1525 if ( createGrabHandle && IsGrabHandleEnabled() )
1527 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1531 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1532 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1533 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1534 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1539 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1541 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1543 if(longPress.state == Dali::Gesture::Started)
1545 // Start edit mode on long press
1546 if ( !mEditModeActive )
1551 // If text exists then select nearest word.
1552 if ( !mStyledText.empty())
1556 ShowGrabHandleAndSetVisibility( false );
1561 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1562 // converts the pre-edit word being displayed to a committed word.
1563 if ( !mUnderlinedPriorToPreEdit )
1566 style.SetUnderline( false );
1567 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1569 mPreEditFlag = false;
1570 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1571 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1572 PreEditReset( false );
1574 mCursorPosition = 0;
1576 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1577 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1579 ImfManager imfManager = ImfManager::Get();
1582 imfManager.SetCursorPosition ( mCursorPosition );
1583 imfManager.NotifyCursorPosition();
1585 std::size_t start = 0;
1586 std::size_t end = 0;
1587 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1589 SelectText( start, end );
1592 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1593 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1595 ShowPopupCutCopyPaste();
1600 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1602 const Text clipboardText( notifier.GetContent() );
1603 PasteText( clipboardText );
1605 SetCursorVisibility( true );
1606 StartCursorBlinkTimer();
1608 ShowGrabHandleAndSetVisibility( false );
1614 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1616 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1618 const std::string& name = button.GetName();
1620 if(name == TextInputPopup::OPTION_SELECT_WORD)
1622 std::size_t start = 0;
1623 std::size_t end = 0;
1624 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1626 SelectText( start, end );
1628 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1630 SetCursorVisibility(false);
1631 StopCursorBlinkTimer();
1633 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1634 std::size_t start = 0;
1636 SelectText( start, end );
1638 else if(name == TextInputPopup::OPTION_CUT)
1640 bool ret = CopySelectedTextToClipboard();
1644 DeleteHighlightedText( true );
1648 SetCursorVisibility( true );
1649 StartCursorBlinkTimer();
1653 else if(name == TextInputPopup::OPTION_COPY)
1655 CopySelectedTextToClipboard();
1659 SetCursorVisibility( true );
1660 StartCursorBlinkTimer();
1664 else if(name == TextInputPopup::OPTION_PASTE)
1666 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1668 PasteText(retrievedString);
1670 SetCursorVisibility( true );
1671 StartCursorBlinkTimer();
1673 ShowGrabHandleAndSetVisibility( false );
1677 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1679 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1680 // Hence pass the false parameter for signalFinished.
1681 HidePopup( true, false );
1682 mClipboard.ShowClipboard();
1688 bool TextInput::OnCursorBlinkTimerTick()
1691 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1692 if ( mCursorRTLEnabled )
1694 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1696 mCursorBlinkStatus = !mCursorBlinkStatus;
1701 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1703 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1705 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1706 if(mHighlightMeshActor && mState == StateEdit)
1708 ShowPopupCutCopyPaste();
1712 //FIXME this routine needs to be re-written as it contains too many branches.
1713 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1715 std::string keyName = event.keyPressedName;
1716 std::string keyString = event.keyPressed;
1718 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1720 // Do not consume "Tab" and "Escape" keys.
1721 if(keyName == "Tab" || keyName == "Escape")
1723 // Escape key to end the edit mode
1729 HidePopup(); // If Pop-up shown then hides it as editing text.
1731 // Update Flag, indicates whether to update the text-input contents or not.
1732 // Any key stroke that results in a visual change of the text-input should
1733 // set this flag to true.
1736 // Whether to scroll text to cursor position.
1737 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1738 bool scroll = false;
1740 if (keyName == "Return")
1742 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1744 bool preEditFlagPreviouslySet( mPreEditFlag );
1746 if (mHighlightMeshActor)
1748 // replaces highlighted text with new line
1749 DeleteHighlightedText( false );
1751 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1753 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1754 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1757 mCommitByKeyInput = true;
1760 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1761 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1763 mPreEditFlag = true;
1764 mIgnoreCommitFlag = false;
1774 else if ( keyName == "space" )
1776 if ( mHighlightMeshActor )
1778 // Some text is selected so erase it before adding space.
1779 DeleteHighlightedText( true );
1783 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1785 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1786 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1789 mCommitByKeyInput = true;
1794 else if (keyName == "BackSpace")
1796 if ( mHighlightMeshActor )
1798 // Some text is selected so erase it
1799 DeleteHighlightedText( true );
1804 if ( mCursorPosition > 0 )
1806 DeleteCharacter( mCursorPosition );
1812 else if (keyName == "Right")
1817 else if (keyName == "Left")
1819 AdvanceCursor(true);
1822 else // event is a character
1824 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1825 if ( !keyString.empty() )
1827 if ( mHighlightMeshActor )
1829 // replaces highlighted text with new character
1830 DeleteHighlightedText( false );
1834 // Received key String
1835 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1841 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1842 // as this is a costly operation.
1848 if(update || scroll)
1850 if( IsScrollEnabled() )
1852 // Calculates the new cursor position (in actor coordinates)
1853 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1855 ScrollTextViewToMakeCursorVisible( cursorPosition );
1862 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1864 std::string keyName = event.keyPressedName;
1865 std::string keyString = event.keyPressed;
1867 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1869 // The selected text become deselected when the key code is DALI_KEY_BACK.
1870 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1879 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1881 // Updates the stored scroll position.
1882 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1884 const Vector3& controlSize = GetControlSize();
1885 Size cursorSize( CURSOR_THICKNESS, 0.f );
1887 // Updates the cursor and grab handle position and visibility.
1888 if( mGrabHandle || mCursor )
1890 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1891 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1893 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1895 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1899 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1900 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1905 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1906 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1910 // Updates the selection handles and highlighted text position and visibility.
1911 if( mSelectionHandleOne && mSelectionHandleTwo )
1913 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1914 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1915 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1916 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1917 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1918 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1920 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1921 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1923 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1924 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1925 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1926 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1928 if( mHighlightMeshActor )
1930 mHighlightMeshActor.SetVisible( true );
1936 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1938 // Scroll the text to make the cursor visible.
1939 const Size cursorSize( CURSOR_THICKNESS,
1940 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1942 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1944 const Vector3& controlSize = GetControlSize();
1946 // Calculates the new scroll position.
1947 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1948 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1950 scrollOffset.x += cursorPosition.x;
1953 if( cursorPosition.y - cursorSize.height < 0.f )
1955 scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1957 else if( cursorPosition.y > controlSize.height )
1959 scrollOffset.y += cursorPosition.y;
1962 // Sets the new scroll position.
1963 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1964 SetScrollPosition( scrollOffset );
1967 void TextInput::StartScrollTimer()
1971 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1972 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1975 if( !mScrollTimer.IsRunning() )
1977 mScrollTimer.Start();
1981 void TextInput::StopScrollTimer()
1985 mScrollTimer.Stop();
1989 bool TextInput::OnScrollTimerTick()
1991 // TODO: need to set the new style accordingly the new handle position.
1993 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1995 // nothing to do if all handles are invisible or doesn't exist.
2001 // Choose between the grab handle or the selection handles.
2002 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2003 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2004 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2006 std::size_t newCursorPosition = 0;
2007 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2009 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2010 // the new selection handle's position needs to be different of the other one.
2011 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2012 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2013 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2015 if( differentSelectionHandles )
2017 handlePosition = newCursorPosition;
2019 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2021 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2023 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2024 scrollPosition += scrollDelta;
2025 SetScrollPosition( scrollPosition );
2027 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2032 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2035 actualHandlePosition.x += mScrollDisplacement.x;
2036 actualHandlePosition.y += mScrollDisplacement.y;
2041 // Public Internal Methods (public for testing purpose)
2043 void TextInput::SetUpTouchEvents()
2045 if ( !mTapDetector )
2047 mTapDetector = TapGestureDetector::New();
2048 // Attach the actors and connect the signal
2049 mTapDetector.Attach(Self());
2051 // As contains children which may register for tap the default control detector is not used.
2052 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2055 if ( !mDoubleTapDetector )
2057 mDoubleTapDetector = TapGestureDetector::New();
2058 mDoubleTapDetector.SetTapsRequired( 2 );
2059 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2061 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2062 // so that we do not, unnecessarily, have a double tap request all the time
2065 if ( !mPanGestureDetector )
2067 mPanGestureDetector = PanGestureDetector::New();
2068 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2071 if ( !mLongPressDetector )
2073 mLongPressDetector = LongPressGestureDetector::New();
2074 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2075 mLongPressDetector.Attach(Self());
2079 void TextInput::CreateTextViewActor()
2081 mDisplayedTextView = Toolkit::TextView::New();
2082 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2083 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2084 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2085 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2086 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2087 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2088 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2089 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2090 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2091 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2093 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2095 Self().Add( mDisplayedTextView );
2098 // Start a timer to initiate, used by the cursor to blink.
2099 void TextInput::StartCursorBlinkTimer()
2101 if ( !mCursorBlinkTimer )
2103 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2104 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2107 if ( !mCursorBlinkTimer.IsRunning() )
2109 mCursorBlinkTimer.Start();
2113 // Start a timer to initiate, used by the cursor to blink.
2114 void TextInput::StopCursorBlinkTimer()
2116 if ( mCursorBlinkTimer )
2118 mCursorBlinkTimer.Stop();
2122 void TextInput::StartEditMode()
2124 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2126 if(!mEditModeActive)
2131 if ( mDoubleTapDetector )
2133 mDoubleTapDetector.Attach( Self() );
2137 void TextInput::EndEditMode()
2139 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2141 ClearKeyInputFocus();
2143 if ( mDoubleTapDetector )
2145 mDoubleTapDetector.Detach( Self() );
2149 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2151 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2153 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2155 style.SetUnderline( true );
2156 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2160 void TextInput::RemovePreEditStyle()
2162 if ( !mUnderlinedPriorToPreEdit )
2165 style.SetUnderline( false );
2166 SetActiveStyle( style, TextStyle::UNDERLINE );
2170 // IMF related methods
2173 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2175 bool update( false );
2176 bool preeditResetRequired ( false );
2178 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2180 HidePopup(); // If Pop-up shown then hides it as editing text.
2183 switch ( imfEvent.eventName )
2185 case ImfManager::PREEDIT:
2187 mIgnoreFirstCommitFlag = false;
2189 // 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
2190 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2192 // replaces highlighted text with new character
2193 DeleteHighlightedText( false );
2196 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2198 if( IsScrollEnabled() )
2200 // Calculates the new cursor position (in actor coordinates)
2201 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2202 ScrollTextViewToMakeCursorVisible( cursorPosition );
2209 case ImfManager::COMMIT:
2211 if( mIgnoreFirstCommitFlag )
2213 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2214 mIgnoreFirstCommitFlag = false;
2218 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2220 // 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
2221 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2223 // replaces highlighted text with new character
2224 DeleteHighlightedText( false );
2227 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2228 // not needed, one such scenario is when the pre-edit word is too long to fit.
2229 if ( !mIgnoreCommitFlag )
2231 update = CommitReceived( imfEvent.predictiveString );
2235 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2241 if( IsScrollEnabled() )
2243 // Calculates the new cursor position (in actor coordinates)
2244 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2246 ScrollTextViewToMakeCursorVisible( cursorPosition );
2251 case ImfManager::DELETESURROUNDING:
2253 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2254 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2256 mPreEditFlag = false;
2258 std::size_t toDelete = 0;
2259 std::size_t numberOfCharacters = 0;
2261 if( mHighlightMeshActor )
2263 // delete highlighted text.
2264 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2265 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2269 if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2271 toDelete = mCursorPosition + imfEvent.cursorOffset;
2273 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2275 numberOfCharacters = mStyledText.size() - toDelete;
2279 numberOfCharacters = imfEvent.numberOfChars;
2282 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2283 DeleteRange( toDelete, numberOfCharacters );
2285 mCursorPosition = toDelete;
2286 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2290 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2293 case ImfManager::GETSURROUNDING:
2295 // 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
2296 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2297 if (! ( mHighlightMeshActor || mSelectingText ) )
2299 std::string text( GetText() );
2300 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2302 imfManager.SetCursorPosition( mCursorPosition );
2303 imfManager.SetSurroundingText( text );
2306 if( 0 != mNumberOfSurroundingCharactersDeleted )
2308 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2309 mNumberOfSurroundingCharactersDeleted = 0;
2311 if( mStyledText.empty() )
2313 // Styled text is empty, so set the placeholder text.
2314 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2315 mPlaceHolderSet = true;
2320 case ImfManager::VOID:
2322 DALI_ASSERT_DEBUG( false );
2326 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2328 return callbackData;
2331 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2333 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2335 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2336 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2338 bool preeditResetRequest ( false );
2340 if( mPreEditFlag ) // Already in pre-edit state.
2342 if( mStyledText.size() >= mMaxStringLength )
2344 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2345 // Cannot fit these characters into field, clear pre-edit.
2346 if ( !mUnderlinedPriorToPreEdit )
2349 style.SetUnderline( false );
2350 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2352 mIgnoreCommitFlag = true;
2353 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2354 mPreEditFlag = false;
2355 EmitMaxInputCharactersReachedSignal();
2359 // delete existing pre-edit string
2360 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2362 // Store new pre-edit string
2363 mPreEditString.SetText( keyString );
2365 if ( keyString.empty() )
2367 mPreEditFlag = false;
2368 mCursorPosition = mPreEditStartPosition;
2370 if( mStyledText.empty() )
2372 // Styled text is empty, so set the placeholder text.
2373 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2374 mPlaceHolderSet = true;
2378 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2380 GetTextLayoutInfo();
2385 // Insert new pre-edit string. InsertAt updates the size and position table.
2386 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2387 // 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.
2388 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2389 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2390 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2393 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2397 else // mPreEditFlag not set
2399 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2401 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2402 // new pre-edit so move into pre-edit state by setting flag
2403 mPreEditFlag = true;
2404 mPreEditString.SetText( keyString ); // store new pre-edit string
2405 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2406 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2407 // 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.
2408 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2409 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2410 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2411 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2417 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2421 return preeditResetRequest;
2424 bool TextInput::CommitReceived(const std::string& keyString )
2426 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2427 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2429 bool update( false );
2431 RemovePreEditStyle();
2433 const std::size_t styledTextSize( mStyledText.size() );
2434 if( styledTextSize >= mMaxStringLength )
2436 // Cannot fit these characters into field, clear pre-edit.
2439 mIgnoreCommitFlag = true;
2440 mPreEditFlag = false;
2442 EmitMaxInputCharactersReachedSignal();
2448 // delete existing pre-edit string
2449 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2450 mPreEditFlag = false;
2452 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2453 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2455 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2457 // No need to update cursor position as Cursor location given by touch.
2458 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2459 mPreserveCursorPosition = false;
2463 // Cursor not set by touch so needs to be re-positioned to input more text
2464 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2466 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2467 if ( mCommitByKeyInput )
2469 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2470 mCommitByKeyInput = false;
2476 if ( mSelectTextOnCommit )
2478 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2483 else // mPreEditFlag not set
2485 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2487 if( mStyledText.empty() && mPlaceHolderSet )
2489 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2490 mDisplayedTextView.SetText( "" );
2491 mNumberOfSurroundingCharactersDeleted = 0;
2492 mPlaceHolderSet = false;
2494 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2496 mNumberOfSurroundingCharactersDeleted = 0;
2501 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2506 mSelectTextOnCommit = false;
2508 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2509 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2514 // End of IMF related methods
2516 std::size_t TextInput::DeletePreEdit()
2518 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2520 DALI_ASSERT_DEBUG( mPreEditFlag );
2522 const std::size_t preEditStringLength = mPreEditString.GetLength();
2523 const std::size_t styledTextSize = mStyledText.size();
2525 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2527 // Prevents erase items outside mStyledText bounds.
2528 if( mPreEditStartPosition > styledTextSize )
2530 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2531 mPreEditStartPosition = styledTextSize;
2534 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2536 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2537 endPosition = styledTextSize;
2540 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2542 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2543 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2545 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2547 return preEditStringLength;
2550 void TextInput::PreEditReset( bool preserveCursorPosition )
2552 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2553 preserveCursorPosition, mCursorPosition);
2555 // 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.
2556 mPreserveCursorPosition = preserveCursorPosition;
2558 // Reset incase we are in a pre-edit state.
2559 ImfManager imfManager = ImfManager::Get();
2562 imfManager.Reset(); // Will trigger a commit message
2566 void TextInput::CursorUpdate()
2570 ImfManager imfManager = ImfManager::Get();
2573 std::string text( GetText() );
2574 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2575 imfManager.SetCursorPosition ( mCursorPosition );
2576 imfManager.NotifyCursorPosition();
2580 /* Delete highlighted characters redisplay*/
2581 void TextInput::DeleteHighlightedText( bool inheritStyle )
2583 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2585 if(mHighlightMeshActor)
2587 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2589 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2590 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2592 // Get the styled text of the characters to be deleted as it may be needed if
2593 // the "exceed the text-input's boundaries" option is disabled.
2594 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2596 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2598 mStyledText.erase( start, end ); // erase range of characters
2600 // Remove text from TextView.
2602 if( mStyledText.empty() )
2604 // Styled text is empty, so set the placeholder text.
2605 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2606 mPlaceHolderSet = true;
2610 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2612 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2614 // It may happen than after removing a white space or a new line character,
2615 // two words merge, this new word could be big enough to not fit in its
2616 // current line, so moved to the next one, and make some part of the text to
2617 // exceed the text-input's boundary.
2618 if( !mExceedEnabled )
2620 // Get the new text layout after removing some characters.
2621 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2623 // Get text-input's size.
2624 const Vector3& size = GetControlSize();
2626 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2627 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2629 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2631 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2632 styledCharactersToDelete.begin(),
2633 styledCharactersToDelete.end() );
2637 GetTextLayoutInfo();
2643 const TextStyle oldInputStyle( mInputStyle );
2645 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2647 if( oldInputStyle != mInputStyle )
2649 // Updates the line height accordingly with the input style.
2652 EmitStyleChangedSignal();
2658 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2660 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2661 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2663 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2666 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2668 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2669 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2671 mStyledText.erase(itStart, itEnd);
2673 // update the selection handles if they are visible.
2674 if( mHighlightMeshActor )
2676 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2677 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2679 if( minHandle >= start + ncharacters )
2681 minHandle -= ncharacters;
2683 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2688 if( maxHandle >= start + ncharacters )
2690 maxHandle -= ncharacters;
2692 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2698 // 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.
2701 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2703 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2704 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2705 // Mean we do not re-draw the text more than we have too.
2708 /* Delete character at current cursor position and redisplay*/
2709 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2711 // Ensure positionToDelete is not out of bounds.
2712 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2713 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2714 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2716 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2719 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2721 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2723 // Get the styled text of the character to be deleted as it may be needed if
2724 // the "exceed the text-input's boundaries" option is disabled.
2725 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2727 mStyledText.erase(it); // erase the character left of positionToDelete
2729 if( mStyledText.empty() )
2731 // Styled text is empty, so set the placeholder text.
2732 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2733 mPlaceHolderSet = true;
2737 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2739 const Character characterToDelete = styledCharacterToDelete.mText[0];
2741 // It may happen than after removing a white space or a new line character,
2742 // two words merge, this new word could be big enough to not fit in its
2743 // current line, so moved to the next one, and make some part of the text to
2744 // exceed the text-input's boundary.
2745 if( !mExceedEnabled )
2747 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2749 // Get the new text layout after removing one character.
2750 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2752 // Get text-input's size.
2753 const Vector3& size = GetControlSize();
2755 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2756 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2758 MarkupProcessor::StyledTextArray array;
2759 array.push_back( styledCharacterToDelete );
2760 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2762 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2767 GetTextLayoutInfo();
2769 ShowGrabHandleAndSetVisibility( false );
2771 mCursorPosition = positionToDelete -1;
2773 const TextStyle oldInputStyle( mInputStyle );
2775 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2777 if( oldInputStyle != mInputStyle )
2779 // Updates the line height accordingly with the input style.
2782 EmitStyleChangedSignal();
2787 /*Insert new character into the string and (optionally) redisplay text-input*/
2788 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2790 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2792 // Ensure insertionPosition is not out of bounds.
2793 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2795 bool textExceedsMaximunNumberOfCharacters = false;
2796 bool textExceedsBoundary = false;
2797 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2799 ShowGrabHandleAndSetVisibility( false );
2801 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2805 mIgnoreCommitFlag = true;
2806 mPreEditFlag = false;
2807 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2808 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2811 if( textExceedsMaximunNumberOfCharacters )
2813 EmitMaxInputCharactersReachedSignal();
2816 if( textExceedsBoundary )
2818 EmitInputTextExceedsBoundariesSignal();
2819 PreEditReset( false );
2823 return insertedStringLength;
2826 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2832 cursor = ImageActor::New( cursorImage );
2836 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2839 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2840 cursor.SetNinePatchBorder( border );
2842 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2843 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2844 cursor.SetVisible(false);
2849 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2851 // As cursor is not moving due to grab handle, handle should be hidden.
2852 ShowGrabHandleAndSetVisibility( false );
2854 bool cursorPositionChanged = false;
2857 if ( mCursorPosition >= places )
2859 mCursorPosition = mCursorPosition - places;
2860 cursorPositionChanged = true;
2865 if ((mCursorPosition + places) <= mStyledText.size())
2867 mCursorPosition = mCursorPosition + places;
2868 cursorPositionChanged = true;
2872 if( cursorPositionChanged )
2874 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2876 const TextStyle oldInputStyle( mInputStyle );
2877 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2881 if( oldInputStyle != mInputStyle )
2883 // Updates the line height accordingly with the input style.
2886 EmitStyleChangedSignal();
2889 ImfManager imfManager = ImfManager::Get();
2892 imfManager.SetCursorPosition ( mCursorPosition );
2893 imfManager.NotifyCursorPosition();
2898 void TextInput::DrawCursor(const std::size_t nthChar)
2900 // Get height of cursor and set its size
2901 Size size( CURSOR_THICKNESS, 0.0f );
2902 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2904 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2908 // Measure Font so know how big text will be if no initial text to measure.
2909 size.height = mLineHeight;
2912 mCursor.SetSize(size);
2914 // If the character is italic then the cursor also tilts.
2915 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2917 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2919 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2921 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2922 bool altPositionValid; // Alternate cursor validity flag.
2923 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2924 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2926 SetAltCursorEnabled( altPositionValid );
2928 if(!altPositionValid)
2930 mCursor.SetPosition( position + UI_OFFSET );
2934 size.height *= 0.5f;
2935 mCursor.SetSize(size);
2936 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2938 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2939 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2940 size.height = rowSize.height * 0.5f;
2941 mCursorRTL.SetSize(size);
2942 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2945 if( IsScrollEnabled() )
2947 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2948 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2953 void TextInput::SetAltCursorEnabled( bool enabled )
2955 mCursorRTLEnabled = enabled;
2956 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2959 void TextInput::SetCursorVisibility( bool visible )
2961 mCursorVisibility = visible;
2962 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2963 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2966 void TextInput::CreateGrabHandle( Dali::Image image )
2972 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2976 mGrabHandleImage = image;
2979 mGrabHandle = ImageActor::New(mGrabHandleImage);
2980 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2981 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2983 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2985 ShowGrabHandleAndSetVisibility( false );
2987 CreateGrabArea( mGrabHandle );
2989 mActiveLayer.Add(mGrabHandle);
2993 void TextInput::CreateGrabArea( Actor& parent )
2995 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2996 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2997 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2998 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2999 mTapDetector.Attach( mGrabArea );
3000 mPanGestureDetector.Attach( mGrabArea );
3002 parent.Add(mGrabArea);
3005 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3007 Vector3 actualHandlePosition;
3011 mActualGrabHandlePosition.x += displacement.x;
3012 mActualGrabHandlePosition.y += displacement.y;
3014 // Grab handle should jump to the nearest character and take cursor with it
3015 std::size_t newCursorPosition = 0;
3016 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3018 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3020 bool handleVisible = true;
3022 if( IsScrollEnabled() )
3024 const Vector3 controlSize = GetControlSize();
3025 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3026 // Scrolls the text if the handle is not in a visible position
3027 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3034 mCurrentHandlePosition = actualHandlePosition;
3035 mScrollDisplacement = Vector2::ZERO;
3039 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3041 mScrollDisplacement.x = -SCROLL_SPEED;
3043 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3045 mScrollDisplacement.x = SCROLL_SPEED;
3047 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3049 mScrollDisplacement.y = -SCROLL_SPEED;
3051 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3053 mScrollDisplacement.y = SCROLL_SPEED;
3059 if( handleVisible && // Only redraw cursor and do updates if position changed
3060 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3062 mCursorPosition = newCursorPosition;
3064 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3066 const TextStyle oldInputStyle( mInputStyle );
3068 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3070 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3072 if( oldInputStyle != mInputStyle )
3074 // Updates the line height accordingly with the input style.
3077 EmitStyleChangedSignal();
3082 return actualHandlePosition;
3085 void TextInput::ShowGrabHandle( bool visible )
3087 if ( IsGrabHandleEnabled() )
3091 mGrabHandle.SetVisible( mGrabHandleVisibility );
3093 StartMonitoringStageForTouch();
3097 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3099 mGrabHandleVisibility = visible;
3100 ShowGrabHandle( visible );
3103 // Callbacks connected to be Property notifications for Boundary checking.
3105 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3107 mIsSelectionHandleOneFlipped = true;
3108 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3109 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3112 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3114 mIsSelectionHandleOneFlipped = false;
3115 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3116 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3119 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3121 mIsSelectionHandleTwoFlipped = true;
3122 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3123 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3126 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3128 mIsSelectionHandleTwoFlipped = false;
3129 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3130 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3133 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3134 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3136 mSelectionHandleOne.SetOpacity(0.0f);
3139 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3141 mSelectionHandleOne.SetOpacity(1.0f);
3144 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3146 mSelectionHandleTwo.SetOpacity(0.0f);
3149 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3151 mSelectionHandleTwo.SetOpacity(1.0f);
3154 // End of Callbacks connected to be Property notifications for Boundary checking.
3156 void TextInput::SetUpHandlePropertyNotifications()
3158 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3160 Vector3 handlesize = GetSelectionHandleSize();
3162 // Exceeding horizontal boundary
3163 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3164 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3166 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3167 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3169 // Within horizontal boundary
3170 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3171 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3173 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3174 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3176 // Exceeding vertical boundary
3177 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3178 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3179 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3180 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3182 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3183 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3184 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3185 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3187 // Within vertical boundary
3188 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3189 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3190 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3191 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3193 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3194 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3195 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3196 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3199 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3201 mSelectionHandleOnePosition = start;
3202 mSelectionHandleTwoPosition = end;
3204 if ( !mSelectionHandleOne )
3206 // create normal and pressed images
3207 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3208 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3210 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3211 mSelectionHandleOne.SetName("SelectionHandleOne");
3212 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3213 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3214 mIsSelectionHandleOneFlipped = false;
3215 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3217 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3218 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3220 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3221 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3223 mTapDetector.Attach( mHandleOneGrabArea );
3224 mPanGestureDetector.Attach( mHandleOneGrabArea );
3226 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3228 mSelectionHandleOne.Add( mHandleOneGrabArea );
3229 mActiveLayer.Add( mSelectionHandleOne );
3232 if ( !mSelectionHandleTwo )
3234 // create normal and pressed images
3235 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3236 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3238 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3239 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3240 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3241 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3242 mIsSelectionHandleTwoFlipped = false;
3243 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3245 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3246 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3247 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3248 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3250 mTapDetector.Attach( mHandleTwoGrabArea );
3251 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3253 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3255 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3257 mActiveLayer.Add( mSelectionHandleTwo );
3260 SetUpHandlePropertyNotifications();
3262 // update table as text may have changed.
3263 GetTextLayoutInfo();
3265 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3266 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3268 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3269 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3271 // Calculates and set the visibility if the scroll mode is enabled.
3272 bool isSelectionHandleOneVisible = true;
3273 bool isSelectionHandleTwoVisible = true;
3274 if( IsScrollEnabled() )
3276 const Vector3& controlSize( GetControlSize() );
3277 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3278 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3279 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3280 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3283 CreateHighlight(); // function will only create highlight if not already created.
3286 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3288 Vector3 actualHandlePosition;
3290 if ( mSelectionHandleOne && mSelectionHandleTwo )
3292 const Vector3& controlSize = GetControlSize();
3294 Size cursorSize( CURSOR_THICKNESS, 0.f );
3296 // Get a reference of the wanted selection handle (handle one or two).
3297 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3299 // Get a reference for the current position of the handle and a copy of its pair
3300 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3301 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3303 // Get a handle of the selection handle actor
3304 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3306 // Selection handles should jump to the nearest character
3307 std::size_t newHandlePosition = 0;
3308 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3310 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3312 bool handleVisible = true;
3314 if( IsScrollEnabled() )
3316 mCurrentSelectionId = handleId;
3318 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3319 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3320 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3327 mCurrentSelectionHandlePosition = actualHandlePosition;
3328 mScrollDisplacement = Vector2::ZERO;
3332 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3334 mScrollDisplacement.x = -SCROLL_SPEED;
3336 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3338 mScrollDisplacement.x = SCROLL_SPEED;
3340 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3342 mScrollDisplacement.y = -SCROLL_SPEED;
3344 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3346 mScrollDisplacement.y = SCROLL_SPEED;
3352 if ( handleVisible && // Ensure the handle is visible.
3353 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3354 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3356 currentSelectionHandlePosition = newHandlePosition;
3358 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3359 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3363 if ( handleId == HandleOne )
3365 const TextStyle oldInputStyle( mInputStyle );
3367 // Set Active Style to that of first character in selection
3368 if( mSelectionHandleOnePosition < mStyledText.size() )
3370 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3373 if( oldInputStyle != mInputStyle )
3375 // Updates the line height accordingly with the input style.
3378 EmitStyleChangedSignal();
3384 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3387 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3390 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3391 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3393 if ( selectionHandleActor )
3395 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3396 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3397 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3399 if( IsScrollEnabled() )
3401 const Size cursorSize( CURSOR_THICKNESS,
3402 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3403 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3405 GetControlSize() ) );
3410 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3412 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3413 // For now the visual position of end of logical string will be end of visual string.
3414 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3416 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3419 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3421 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3422 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3423 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3424 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3426 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3428 // Deselect text prior to startSelectionIt
3429 for(;it!=startSelectionIt;++it)
3431 selectedVisualText[*it] = false;
3434 // Select text from startSelectionIt -> endSelectionIt
3435 for(;it!=endSelectionIt;++it)
3437 selectedVisualText[*it] = true;
3440 // Deselect text after endSelection
3443 selectedVisualText[*it] = false;
3446 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3449 // Calculate the dimensions of the quads they will make the highlight mesh
3450 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3452 // At the moment there is no public API to modify the block alignment option.
3453 const bool blockAlignEnabled = true;
3455 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3457 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3459 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3460 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3462 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3463 std::vector<bool> selectedVisualText;
3464 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3465 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3466 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3468 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3469 float rowLeft = 0.0f;
3470 float rowRight = 0.0f;
3471 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3472 float maxRowLeft = std::numeric_limits<float>::max();
3473 float maxRowRight = 0.0f;
3475 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3477 // Scan through entire text.
3480 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3482 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3483 bool charSelected( false );
3484 if( selectedIt != selectedEndIt )
3486 charSelected = *selectedIt++;
3489 if(selectionState == SelectionNone)
3493 selectionState = SelectionStarted;
3494 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3495 rowRight = rowLeft + charInfo.mSize.width;
3498 else if(selectionState == SelectionStarted)
3500 // break selection on:
3501 // 1. new line causing selection break. (\n or wordwrap)
3502 // 2. character not selected.
3503 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3506 // finished selection.
3507 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3508 // that it resides on. That way this enumeration is not necessary.
3510 if(lastIt->mIsNewLineChar)
3512 // 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.
3513 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3515 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3516 maxRowLeft = std::min(maxRowLeft, min.x);
3517 maxRowRight = std::max(maxRowRight, max.x);
3518 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3519 float rowTop = rowBottom - rowSize.height;
3521 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3522 if(charSelected && blockAlignEnabled)
3524 rowRight = std::numeric_limits<float>::max();
3526 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3528 selectionState = SelectionNone;
3530 // Still selected? start a new selection
3533 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3534 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3535 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3536 selectionState = SelectionStarted;
3541 // build up highlight(s) with this selection data.
3542 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3543 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3550 // If reached end, and still on selection, then close selection.
3553 if(selectionState == SelectionStarted)
3555 // finished selection.
3557 if(lastIt->mIsNewLineChar)
3559 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3561 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3562 maxRowLeft = std::min(maxRowLeft, min.x);
3563 maxRowRight = std::max(maxRowRight, max.x);
3564 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3565 float rowTop = rowBottom - rowSize.height;
3566 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3570 // Get the top left and bottom right corners.
3571 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3572 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3573 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3575 // Clamp quads so they appear to clip to borders of the whole text.
3576 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3578 // For block-align align Further Clamp quads to max left and right extents
3579 if(blockAlignEnabled)
3581 // BlockAlign: Will adjust highlight to block:
3583 // H[ello] (top row right = max of all rows right)
3584 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3585 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3586 // [text] (bottom row left = min of all rows left)
3587 // (common in SMS messaging selection)
3589 // As opposed to the default which is tight text highlighting.
3594 // (common in regular text editors/web browser selection)
3596 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3599 // Finally clamp quads again so they don't exceed the boundry of the control.
3600 const Vector3& controlSize = GetControlSize();
3601 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3604 return mNewHighlightInfo;
3607 void TextInput::UpdateHighlight()
3609 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3611 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3613 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3614 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3615 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3616 // [BOTTOM] [ MIDDLE ]
3619 // Each quad is created as 2 triangles.
3620 // Middle is just 1 quad regardless of its size.
3634 if ( mHighlightMeshActor )
3636 // vertex and triangle buffers should always be present if MeshActor is alive.
3637 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3638 MeshData::VertexContainer vertices;
3639 Dali::MeshData::FaceIndices faceIndices;
3641 if( !newHighlightInfo.mQuadList.empty() )
3643 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3644 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3646 // vertex position defaults to (0 0 0)
3647 MeshData::Vertex vertex;
3648 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3651 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3653 // Add each quad geometry (a sub-selection) to the mesh data.
3663 QuadCoordinates& quad = *iter;
3665 vertex.x = quad.min.x;
3666 vertex.y = quad.min.y;
3667 vertices.push_back( vertex );
3670 vertex.x = quad.max.x;
3671 vertex.y = quad.min.y;
3672 vertices.push_back( vertex );
3674 // bottom-left (v+2)
3675 vertex.x = quad.min.x;
3676 vertex.y = quad.max.y;
3677 vertices.push_back( vertex );
3679 // bottom-right (v+3)
3680 vertex.x = quad.max.x;
3681 vertex.y = quad.max.y;
3682 vertices.push_back( vertex );
3684 // triangle A (3, 1, 0)
3685 faceIndices.push_back( v + 3 );
3686 faceIndices.push_back( v + 1 );
3687 faceIndices.push_back( v );
3689 // triangle B (0, 2, 3)
3690 faceIndices.push_back( v );
3691 faceIndices.push_back( v + 2 );
3692 faceIndices.push_back( v + 3 );
3694 mMeshData.SetFaceIndices( faceIndices );
3697 BoneContainer bones(0); // passed empty as bones not required
3698 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3699 mHighlightMesh.UpdateMeshData(mMeshData);
3704 void TextInput::ClearPopup()
3706 mPopupPanel.Clear();
3709 void TextInput::AddPopupOptions()
3711 mPopupPanel.AddPopupOptions();
3714 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3716 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3718 Vector3 clampedPosition ( position );
3719 Vector3 tailOffsetPosition ( position );
3721 float xOffSet( 0.0f );
3723 Actor self = Self();
3724 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3726 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3727 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3729 // Clamp to left or right or of boundary
3730 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3732 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3734 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3736 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3739 clampedPosition.x = position.x + xOffSet;
3740 tailOffsetPosition.x = -xOffSet;
3742 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3743 bool flipTail( false );
3745 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3747 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3751 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3752 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3755 void TextInput::HidePopup(bool animate, bool signalFinished )
3757 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3759 mPopupPanel.Hide( animate );
3761 if( animate && signalFinished )
3763 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3768 void TextInput::ShowPopup( bool animate )
3771 Vector2 alternativePopupPosition;
3773 if(mHighlightMeshActor && mState == StateEdit)
3776 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3778 // When text is selected, show popup above top handle (and text), or below bottom handle.
3779 // topHandle: referring to the top most point of the handle or the top line of selection.
3780 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3782 topHandle = mSelectionHandleOneActualPosition;
3783 bottomHandle = mSelectionHandleTwoActualPosition;
3784 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3788 topHandle = mSelectionHandleTwoActualPosition;
3789 bottomHandle = mSelectionHandleOneActualPosition;
3790 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3792 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3793 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3795 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3797 position.x = xPosition;
3799 // Alternative position if no upper space
3800 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3801 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3805 // When no text is selected, show popup at world position of grab handle or cursor
3806 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3807 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3808 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3809 // if can't be positioned above, then position below row.
3810 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3813 // If grab handle enabled then position pop-up below the grab handle.
3814 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3818 SetPopupPosition( position, alternativePopupPosition );
3821 mPopupPanel.Show( Self(), animate );
3822 StartMonitoringStageForTouch();
3824 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3827 void TextInput::ShowPopupCutCopyPaste()
3831 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3832 // Check the selected text is whole text or not.
3833 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3835 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3838 if ( !mStyledText.empty() )
3840 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3841 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3844 if( mClipboard && mClipboard.NumberOfItems() )
3846 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3847 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3852 mPopupPanel.Hide(false);
3856 void TextInput::SetUpPopupSelection()
3859 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3860 // If no text exists then don't offer to select
3861 if ( !mStyledText.empty() )
3863 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3864 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3865 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3867 // if clipboard has valid contents then offer paste option
3868 if( mClipboard && mClipboard.NumberOfItems() )
3870 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3871 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3876 mPopupPanel.Hide(false);
3879 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3884 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3885 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3886 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3887 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3889 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3891 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3893 float closestYdifference = std::numeric_limits<float>::max();
3894 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3895 std::size_t numberOfMatchedCharacters = 0;
3897 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3898 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3900 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3902 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3903 float baselinePosition = info.mPosition.y - info.mDescender;
3905 if( info.mIsVisible )
3907 // store difference between source y point and the y position of the current character
3908 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3910 if( currentYdifference < closestYdifference )
3912 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3913 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3914 closestYdifference = currentYdifference;
3915 matchedCharacters.clear();
3916 numberOfMatchedCharacters = 0; // reset count
3919 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3920 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3922 // ignore new line character.
3923 if( !info.mIsNewLineChar )
3925 matchedCharacters.push_back( info );
3926 numberOfMatchedCharacters++;
3930 } // End of loop checking each character's y position in the character layout table
3932 // Check if last character is a newline, if it is
3933 // then need pretend there is an imaginary line afterwards,
3934 // and check if user is touching below previous line.
3935 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3937 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3939 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3943 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3944 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3946 bool matched( false );
3948 // 2 Iterate through matching list of y positions and find closest matching X position.
3949 for( ; it != endIt; ++it )
3951 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3953 if( info.mIsVisible )
3955 // stop when on left side of character's center.
3956 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3957 if( sourceScrollOffset.x < characterMidPointPosition )
3959 if(info.mIsRightToLeftCharacter)
3961 rightToLeftChar = true;
3963 glyphIntersection = info.mPosition.x;
3968 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3974 rightToLeftChar = lastRightToLeftChar;
3977 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3978 closestIndex = lineOffset + matchCharacterIndex;
3980 mClosestCursorPositionEOL = false; // reset
3981 if ( it == endIt && !matched )
3983 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3986 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3987 if( rightToLeftChar && lastRightToLeftChar )
3989 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3994 // closestIndex is the visual index, need to convert it to the logical index
3995 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3997 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3999 // Checks for situations where user is touching between LTR and RTL
4000 // characters. To identify if the user means the end of a LTR string
4001 // or the beginning of an RTL string, and vice versa.
4002 if( closestIndex > 0 )
4004 if( rightToLeftChar && !lastRightToLeftChar )
4009 // A: In this touch range, the user is indicating that they wish to place
4010 // the cursor at the end of the LTR text.
4011 // B: In this touch range, the user is indicating that they wish to place
4012 // the cursor at the end of the RTL text.
4014 // Result of touching A area:
4015 // [.....LTR]|[RTL......]+
4017 // |: primary cursor (for typing LTR chars)
4018 // +: secondary cursor (for typing RTL chars)
4020 // Result of touching B area:
4021 // [.....LTR]+[RTL......]|
4023 // |: primary cursor (for typing RTL chars)
4024 // +: secondary cursor (for typing LTR chars)
4026 if( sourceScrollOffset.x < glyphIntersection )
4031 else if( !rightToLeftChar && lastRightToLeftChar )
4033 if( sourceScrollOffset.x < glyphIntersection )
4040 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4041 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4042 // one further ahead
4043 if( rightToLeftChar && !lastRightToLeftChar )
4048 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4050 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4052 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4061 float TextInput::GetLineJustificationPosition() const
4063 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4064 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4065 float alignmentOffset = 0.f;
4067 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4068 if( alignment & Toolkit::Alignment::HorizontalLeft )
4070 alignmentOffset = 0.f;
4072 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4074 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4076 else if( alignment & Toolkit::Alignment::HorizontalRight )
4078 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4081 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4082 float justificationOffset = 0.f;
4084 switch( justification )
4086 case Toolkit::TextView::Left:
4088 justificationOffset = 0.f;
4091 case Toolkit::TextView::Center:
4093 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4096 case Toolkit::TextView::Right:
4098 justificationOffset = mTextLayoutInfo.mTextSize.width;
4101 case Toolkit::TextView::Justified:
4103 justificationOffset = 0.f;
4108 DALI_ASSERT_ALWAYS( false );
4112 return alignmentOffset + justificationOffset;
4115 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4117 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4118 A newline character is not inserted in this case */
4120 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4122 Vector3 cursorPosition;
4124 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4126 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4128 // end character so use
4129 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4130 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4134 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4137 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4139 // If previous character on a different line then use current characters position
4140 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4142 if ( mClosestCursorPositionEOL )
4144 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4148 cursorPosition = Vector3(currentCharInfo.mPosition);
4153 // Previous character is on same line so use position of previous character plus it's width.
4154 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4157 return cursorPosition;
4160 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4162 bool direction(false);
4163 Vector3 alternatePosition;
4164 bool alternatePositionValid(false);
4166 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4169 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4171 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4173 alternatePositionValid = false;
4174 directionRTL = false;
4176 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4178 std::size_t visualCharacterPosition;
4180 // When cursor is not at beginning, consider possibility of
4181 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4182 if(characterPosition > 0)
4184 // Cursor position should be the end of the last character.
4185 // If the last character is LTR, then the end is on the right side of the glyph.
4186 // If the last character is RTL, then the end is on the left side of the glyph.
4187 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4189 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4191 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4194 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4195 if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4197 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4198 const Vector3& size = GetControlSize();
4200 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4202 --visualCharacterPosition;
4204 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4207 if(!info.mIsNewLineChar)
4209 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4213 // When cursor points to first character on new line, position cursor at the start of this glyph.
4214 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4216 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4217 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4218 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4220 cursorPosition.x = infoNext.mPosition.x + start;
4221 cursorPosition.y = infoNext.mPosition.y;
4225 // If cursor points to the end of text, then can only position
4226 // cursor where the new line starts based on the line-justification position.
4227 cursorPosition.x = GetLineJustificationPosition();
4229 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4231 // If this is after the last character, then we can assume that the new cursor
4232 // should be exactly one row below the current row.
4234 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4235 cursorPosition.y = info.mPosition.y + rowRect.height;
4239 // If this is not after last character, then we can use this row's height.
4240 // should be exactly one row below the current row.
4242 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4243 cursorPosition.y = info.mPosition.y + rowRect.height;
4248 directionRTL = info.mIsRightToLeftCharacter;
4250 // 1. When the cursor is neither at the beginning or the end,
4251 // we can show multiple cursors under situations when the cursor is
4252 // between RTL and LTR text...
4253 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4255 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4257 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4258 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4260 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4262 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4263 // Text: [...LTR...]|[...RTL...]
4265 // Alternate cursor pos: ^
4266 // In which case we need to display an alternate cursor for the RTL text.
4268 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4269 alternatePosition.y = infoAlt.mPosition.y;
4270 alternatePositionValid = true;
4272 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4274 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4275 // Text: |[...RTL...] [...LTR....]
4277 // Alternate cursor pos: ^
4278 // In which case we need to display an alternate cursor for the RTL text.
4280 alternatePosition.x = infoAlt.mPosition.x;
4281 alternatePosition.y = infoAlt.mPosition.y;
4282 alternatePositionValid = true;
4287 // 2. When the cursor is at the end of the text,
4288 // and we have multi-directional text,
4289 // we can also consider showing mulitple cursors.
4290 // The rule here is:
4291 // If first and last characters on row are different
4292 // Directions, then two cursors need to be displayed.
4294 // Get first logical glyph on row
4295 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4297 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4298 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4300 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4302 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4303 // Text: [...LTR...]|[...RTL...]
4305 // Alternate cursor pos: ^
4306 // In which case we need to display an alternate cursor for the RTL text, this cursor
4307 // should be at the end of the given line.
4309 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4310 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4311 alternatePosition.y = infoAlt.mPosition.y;
4312 alternatePositionValid = true;
4314 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4316 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4317 // Text: |[...RTL...] [...LTR....]
4319 // Alternate cursor pos: ^
4320 // In which case we need to display an alternate cursor for the RTL text.
4322 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4323 alternatePosition.x = infoAlt.mPosition.x;
4324 alternatePosition.y = infoAlt.mPosition.y;
4325 alternatePositionValid = true;
4328 } // characterPosition > 0
4329 else if(characterPosition == 0)
4331 // When the cursor position is at the beginning, it should be at the start of the current character.
4332 // If the current character is LTR, then the start is on the right side of the glyph.
4333 // If the current character is RTL, then the start is on the left side of the glyph.
4334 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4336 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4338 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4341 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4342 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4344 cursorPosition.x = info.mPosition.x + start;
4345 cursorPosition.y = info.mPosition.y;
4346 directionRTL = info.mIsRightToLeftCharacter;
4351 // If the character table is void, place the cursor accordingly the text alignment.
4352 const Vector3& size = GetControlSize();
4354 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4355 float alignmentOffset = 0.f;
4357 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4358 if( alignment & Toolkit::Alignment::HorizontalLeft )
4360 alignmentOffset = 0.f;
4362 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4364 alignmentOffset = 0.5f * ( size.width );
4366 else if( alignment & Toolkit::Alignment::HorizontalRight )
4368 alignmentOffset = size.width;
4371 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4372 cursorPosition.x = alignmentOffset;
4374 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4375 if( alignment & Toolkit::Alignment::VerticalTop )
4377 cursorPosition.y = mLineHeight;
4379 else if( alignment & Toolkit::Alignment::VerticalCenter )
4381 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4383 else if( alignment & Toolkit::Alignment::VerticalBottom )
4385 cursorPosition.y = size.height;
4389 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4390 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4391 if( alternatePositionValid )
4393 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4394 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4397 return cursorPosition;
4400 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4402 // scan string from current position to beginning of current line to note direction of line
4403 while(logicalPosition)
4406 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4407 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4414 return logicalPosition;
4417 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4421 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4424 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4426 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4427 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4429 min = Vector2::ZERO;
4430 max = Vector2(0.0f, mLineHeight);
4434 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4435 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4436 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4438 // If cursor is pointing to end of line, then start from last character.
4439 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4441 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4443 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4444 if( !it->mIsVisible )
4446 characterPosition = FindVisibleCharacter( Left, characterPosition );
4447 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4450 // Scan characters left and right of cursor, stopping when end of line/string reached or
4451 // y position greater than threshold of reference line.
4453 // 1. scan left until we reach the beginning or a different line.
4454 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4455 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4456 // min-x position is the left-most char's left (x)
4457 // max-x position is the right-most char's right (x)
4458 // min-y position is the minimum of all character's top (y)
4459 // max-y position is the maximum of all character's bottom (y+height)
4460 min.y = validCharIt->mPosition.y;
4461 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4466 min.y = std::min(min.y, validCharIt->mPosition.y);
4467 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4476 if( (it->mPosition.y < referenceLine) ||
4477 (it->mIsNewLineChar) ||
4484 // info refers to the first character on this line.
4485 min.x = validCharIt->mPosition.x;
4487 // 2. scan right until we reach end or a different line
4488 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4489 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4493 if( (it->mPosition.y > referenceLine) ||
4494 (it->mIsNewLineChar) ||
4501 min.y = std::min(min.y, validCharIt->mPosition.y);
4502 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4507 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4509 if ( validCharIt != end )
4511 // info refers to the last character on this line.
4512 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4515 return Size( max.x - min.x, max.y - min.y );
4518 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4520 Actor popUpPanel = mPopupPanel.GetRootActor();
4522 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4528 Dali::Actor parent( touchedActor.GetParent() );
4532 return WasTouchedCheck( parent );
4539 void TextInput::StartMonitoringStageForTouch()
4541 Stage stage = Stage::GetCurrent();
4542 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4545 void TextInput::EndMonitoringStageForTouch()
4547 Stage stage = Stage::GetCurrent();
4548 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4551 void TextInput::OnStageTouched(const TouchEvent& event)
4553 if( event.GetPointCount() > 0 )
4555 if ( TouchPoint::Down == event.GetPoint(0).state )
4557 const Actor touchedActor(event.GetPoint(0).hitActor);
4559 bool popUpShown( false );
4561 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4566 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4568 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4570 EndMonitoringStageForTouch();
4571 HidePopup( true, false );
4574 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4576 EndMonitoringStageForTouch();
4577 ShowGrabHandleAndSetVisibility( false );
4583 void TextInput::SelectText(std::size_t start, std::size_t end)
4585 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4586 IsGrabHandleEnabled()?"true":"false",
4587 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4588 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4589 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4591 StartMonitoringStageForTouch();
4593 if ( mEditModeActive ) // Only allow text selection when in edit mode
4595 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4596 mSelectingText = true;
4598 mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4600 ImfManager imfManager = ImfManager::Get();
4603 imfManager.SetCursorPosition ( mCursorPosition );
4604 imfManager.SetSurroundingText( GetText() );
4605 imfManager.NotifyCursorPosition();
4607 // 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.
4609 // Hide grab handle when selecting.
4610 ShowGrabHandleAndSetVisibility( false );
4612 if( start != end ) // something to select
4614 SetCursorVisibility( false );
4615 StopCursorBlinkTimer();
4617 CreateSelectionHandles(start, end);
4620 const TextStyle oldInputStyle( mInputStyle );
4621 mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4623 if( oldInputStyle != mInputStyle )
4625 // Updates the line height accordingly with the input style.
4628 EmitStyleChangedSignal();
4634 mSelectingText = false;
4638 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4640 MarkupProcessor::StyledTextArray currentSelectedText;
4642 if ( IsTextSelected() )
4644 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4645 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4647 for(; it != end; ++it)
4649 MarkupProcessor::StyledText& styledText( *it );
4650 currentSelectedText.push_back( styledText );
4653 return currentSelectedText;
4656 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4658 const std::size_t beginIndex = std::min( begin, end );
4659 const std::size_t endIndex = std::max( begin, end );
4662 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4664 // Create a styled text array used to replace the text into the text-view.
4665 MarkupProcessor::StyledTextArray text;
4666 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4668 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4669 GetTextLayoutInfo();
4671 if( IsScrollEnabled() )
4673 // Need to set the scroll position as the text's size may have changed.
4674 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4677 ShowGrabHandleAndSetVisibility( false );
4683 // Set Handle positioning as the new style may have repositioned the characters.
4684 SetSelectionHandlePosition(HandleOne);
4685 SetSelectionHandlePosition(HandleTwo);
4688 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4690 // Just hide the grab handle when keyboard is hidden.
4691 if (!keyboardShown )
4693 ShowGrabHandleAndSetVisibility( false );
4695 // If the keyboard is not now being shown, then hide the popup panel
4696 mPopupPanel.Hide( true );
4700 // Removes highlight and resumes edit mode state
4701 void TextInput::RemoveHighlight()
4703 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4705 if ( mHighlightMeshActor )
4707 if ( mSelectionHandleOne )
4709 mActiveLayer.Remove( mSelectionHandleOne );
4710 mSelectionHandleOne.Reset();
4711 mSelectionHandleOneOffset.x = 0.0f;
4713 if ( mSelectionHandleTwo )
4715 mActiveLayer.Remove( mSelectionHandleTwo );
4716 mSelectionHandleTwo.Reset();
4717 mSelectionHandleTwoOffset.x = 0.0f;
4720 mNewHighlightInfo.mQuadList.clear();
4722 Self().Remove( mHighlightMeshActor );
4724 SetCursorVisibility( true );
4725 StartCursorBlinkTimer();
4727 mHighlightMeshActor.Reset();
4728 // NOTE: We cannot dereference mHighlightMesh, due
4729 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4734 mSelectionHandleOnePosition = 0;
4735 mSelectionHandleTwoPosition = 0;
4738 void TextInput::CreateHighlight()
4740 if ( !mHighlightMeshActor )
4742 mMeshData = MeshData( );
4743 mMeshData.SetHasNormals( true );
4745 mCustomMaterial = Material::New("CustomMaterial");
4746 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4748 mMeshData.SetMaterial( mCustomMaterial );
4750 mHighlightMesh = Mesh::New( mMeshData );
4752 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4753 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4754 mHighlightMeshActor.SetInheritShaderEffect( false );
4755 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4756 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4757 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4758 mHighlightMeshActor.SetAffectedByLighting(false);
4760 Self().Add(mHighlightMeshActor);
4765 bool TextInput::CopySelectedTextToClipboard()
4767 mCurrentCopySelecton.clear();
4769 mCurrentCopySelecton = GetSelectedText();
4771 std::string stringToStore;
4773 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4774 * a marked up string.
4776 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4777 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4778 bool success = mClipboard.SetItem( stringToStore );
4782 void TextInput::PasteText( const Text& text )
4784 // Update Flag, indicates whether to update the text-input contents or not.
4785 // Any key stroke that results in a visual change of the text-input should
4786 // set this flag to true.
4787 bool update = false;
4788 if( mHighlightMeshActor )
4790 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4791 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4793 ImfManager imfManager = ImfManager::Get();
4796 imfManager.SetCursorPosition( mCursorPosition );
4797 imfManager.NotifyCursorPosition();
4799 DeleteHighlightedText( true );
4803 bool textExceedsMaximunNumberOfCharacters = false;
4804 bool textExceedsBoundary = false;
4806 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4808 mCursorPosition += insertedStringLength;
4809 ImfManager imfManager = ImfManager::Get();
4812 imfManager.SetCursorPosition ( mCursorPosition );
4813 imfManager.NotifyCursorPosition();
4816 update = update || ( insertedStringLength > 0 );
4822 if( insertedStringLength < text.GetLength() )
4824 EmitMaxInputCharactersReachedSignal();
4827 if( textExceedsBoundary )
4829 EmitInputTextExceedsBoundariesSignal();
4833 void TextInput::SetTextDirection()
4835 // Put the cursor to the right if we are empty and an RTL language is being used.
4836 if ( mStyledText.empty() )
4838 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4840 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4841 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4843 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4844 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4846 int alignment( mDisplayedTextView.GetTextAlignment() &
4847 ( Toolkit::Alignment::VerticalTop |
4848 Toolkit::Alignment::VerticalCenter |
4849 Toolkit::Alignment::VerticalBottom |
4850 Toolkit::Alignment::HorizontalCenter ) );
4851 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4853 // If our alignment is in the center, then do not change.
4854 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4856 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4859 // If our justification is in the center, then do not change.
4860 if ( justification != Toolkit::TextView::Center )
4862 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4865 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4866 mDisplayedTextView.SetLineJustification( justification );
4870 void TextInput::UpdateLineHeight()
4872 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4873 mLineHeight = font.GetLineHeight();
4875 // 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.
4877 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4879 if( !mExceedEnabled || shrink )
4881 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4885 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4887 std::size_t position = 0;
4889 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4895 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4897 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4899 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4905 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4906 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4908 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4914 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4919 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4926 void TextInput::SetSortModifier( float depthOffset )
4928 if(mDisplayedTextView)
4930 mDisplayedTextView.SetSortModifier(depthOffset);
4934 void TextInput::SetSnapshotModeEnabled( bool enable )
4936 if(mDisplayedTextView)
4938 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4942 bool TextInput::IsSnapshotModeEnabled() const
4944 bool snapshotEnabled = false;
4946 if(mDisplayedTextView)
4948 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4951 return snapshotEnabled;
4954 void TextInput::SetMarkupProcessingEnabled( bool enable )
4956 mMarkUpEnabled = enable;
4959 bool TextInput::IsMarkupProcessingEnabled() const
4961 return mMarkUpEnabled;
4964 void TextInput::SetScrollEnabled( bool enable )
4966 if( mDisplayedTextView )
4968 mDisplayedTextView.SetScrollEnabled( enable );
4973 // Don't set cursor's and handle's visibility to false if they are outside the
4974 // boundaries of the text-input.
4975 mIsCursorInScrollArea = true;
4976 mIsGrabHandleInScrollArea = true;
4977 if( mSelectionHandleOne && mSelectionHandleTwo )
4979 mSelectionHandleOne.SetVisible( true );
4980 mSelectionHandleTwo.SetVisible( true );
4982 if( mHighlightMeshActor )
4984 mHighlightMeshActor.SetVisible( true );
4990 bool TextInput::IsScrollEnabled() const
4992 bool scrollEnabled = false;
4994 if( mDisplayedTextView )
4996 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4999 return scrollEnabled;
5002 void TextInput::SetScrollPosition( const Vector2& position )
5004 if( mDisplayedTextView )
5006 mDisplayedTextView.SetScrollPosition( position );
5010 Vector2 TextInput::GetScrollPosition() const
5012 Vector2 scrollPosition;
5014 if( mDisplayedTextView )
5016 scrollPosition = mDisplayedTextView.GetScrollPosition();
5019 return scrollPosition;
5022 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5024 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5025 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5026 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5028 // Add style to the new input text.
5029 MarkupProcessor::StyledTextArray textToInsert;
5030 for( std::size_t i = 0; i < insertedStringLength; ++i )
5032 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5033 textToInsert.push_back( newStyledCharacter );
5036 //Insert text to the TextView.
5037 const bool emptyTextView = mStyledText.empty();
5038 if( emptyTextView && mPlaceHolderSet )
5040 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5041 mDisplayedTextView.SetText( textToInsert );
5045 if( 0 == numberOfCharactersToReplace )
5047 mDisplayedTextView.InsertTextAt( position, textToInsert );
5051 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5054 mPlaceHolderSet = false;
5056 if( textToInsert.empty() )
5058 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5059 GetTextLayoutInfo();
5063 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5064 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5067 textExceedsBoundary = false;
5069 if( !mExceedEnabled )
5071 const Vector3& size = GetControlSize();
5073 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5075 // If new text does not fit within TextView
5076 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5077 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5078 GetTextLayoutInfo();
5079 textExceedsBoundary = true;
5080 insertedStringLength = 0;
5083 if( textExceedsBoundary )
5085 // Add the part of the text which fits on the text-input.
5087 // Split the text which doesn't fit in two halves.
5088 MarkupProcessor::StyledTextArray firstHalf;
5089 MarkupProcessor::StyledTextArray secondHalf;
5090 SplitText( textToInsert, firstHalf, secondHalf );
5092 // Clear text. This text will be filled with the text inserted.
5093 textToInsert.clear();
5095 // Where to insert the text.
5096 std::size_t positionToInsert = position;
5098 bool end = text.GetLength() <= 1;
5101 // Insert text and check ...
5102 const std::size_t textLength = firstHalf.size();
5103 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5104 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5106 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5108 // Inserted text doesn't fit.
5110 // Remove inserted text
5111 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5112 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5114 // The iteration finishes when only one character doesn't fit.
5115 end = textLength <= 1;
5119 // Prepare next two halves for next iteration.
5120 MarkupProcessor::StyledTextArray copyText = firstHalf;
5121 SplitText( copyText, firstHalf, secondHalf );
5128 // store text to be inserted in mStyledText.
5129 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5131 // Increase the inserted characters counter.
5132 insertedStringLength += textLength;
5134 // Prepare next two halves for next iteration.
5135 MarkupProcessor::StyledTextArray copyText = secondHalf;
5136 SplitText( copyText, firstHalf, secondHalf );
5138 // Update where next text has to be inserted
5139 positionToInsert += textLength;
5145 if( textToInsert.empty() && emptyTextView )
5147 // No character has been added and the text-view was empty.
5148 // Set the placeholder text.
5149 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5150 mPlaceHolderSet = true;
5154 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5155 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5156 mPlaceHolderSet = false;
5159 return insertedStringLength;
5162 void TextInput::GetTextLayoutInfo()
5164 if( mStyledText.empty() )
5166 // The text-input has no text, clear the text-view's layout info.
5167 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5171 if( mDisplayedTextView )
5173 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5177 // There is no text-view.
5178 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5183 void TextInput::SetOffsetFromText( const Vector4& offset )
5185 mPopupOffsetFromText = offset;
5188 const Vector4& TextInput::GetOffsetFromText() const
5190 return mPopupOffsetFromText;
5193 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5195 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5199 TextInput& textInputImpl( GetImpl( textInput ) );
5201 switch ( propertyIndex )
5203 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5205 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5208 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5210 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5213 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5215 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5218 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5220 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5223 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5225 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5228 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5230 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5233 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5235 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5238 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5240 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5243 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5245 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5248 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5250 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5253 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5255 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5258 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5260 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5263 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5265 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5268 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5270 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5273 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5275 textInputImpl.SetOffsetFromText( 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();
5374 void TextInput::EmitStyleChangedSignal()
5376 // emit signal if input style changes.
5377 Toolkit::TextInput handle( GetOwner() );
5378 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5381 void TextInput::EmitTextModified()
5383 // emit signal when text changes.
5384 Toolkit::TextInput handle( GetOwner() );
5385 mTextModifiedSignal.Emit( handle );
5389 void TextInput::EmitMaxInputCharactersReachedSignal()
5391 // emit signal if max characters is reached during text input.
5392 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5394 Toolkit::TextInput handle( GetOwner() );
5395 mMaxInputCharactersReachedSignalV2.Emit( handle );
5398 void TextInput::EmitInputTextExceedsBoundariesSignal()
5400 // Emit a signal when the input text exceeds the boundaries of the text input.
5402 Toolkit::TextInput handle( GetOwner() );
5403 mInputTextExceedBoundariesSignalV2.Emit( handle );
5406 } // namespace Internal
5408 } // namespace Toolkit