2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
90 * Whether the given style is the default style or not.
91 * @param[in] style The given style.
92 * @return \e true if the given style is the default. Otherwise it returns \e false.
94 bool IsDefaultStyle( const TextStyle& style )
96 return DEFAULT_TEXT_STYLE == style;
100 * Whether the given styled text is using the default style or not.
101 * @param[in] textArray The given text.
102 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108 const TextStyle& style( (*it).mStyle );
110 if( !IsDefaultStyle( style ) )
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
125 if( ( *it ).mIsVisible )
127 return --cursorPosition;
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
138 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140 if( ( *it ).mIsVisible )
142 return cursorPosition;
148 return cursorPosition;
152 * Whether the given position plus the cursor size offset is inside the given boundary.
154 * @param[in] position The given position.
155 * @param[in] cursorSize The cursor size.
156 * @param[in] controlSize The given boundary.
158 * @return whether the given position is inside the given boundary.
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
169 * Splits a text in two halves.
171 * If the text's number of characters is odd, firstHalf has one more character.
173 * @param[in] text The text to be split.
174 * @param[out] firstHalf The first half of the text.
175 * @param[out] secondHalf The second half of the text.
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
184 const std::size_t textLength = text.size();
185 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
191 } // end of namespace
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
261 QuadCoordinates quad(x1, y1, x2, y2);
262 mQuadList.push_back( quad );
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
267 for(std::size_t i = 0;i < mQuadList.size(); i++)
269 QuadCoordinates& quad = mQuadList[i];
271 quad.min.Clamp(min, max);
272 quad.max.Clamp(min, max);
276 // [TextInput] ////////////////////////////////////////////////////////////////
278 Dali::Toolkit::TextInput TextInput::New()
280 // Create the implementation
281 TextInputPtr textInput(new TextInput());
282 // Pass ownership to CustomActor via derived handle
283 Dali::Toolkit::TextInput handle(*textInput);
284 handle.SetName( "TextInput");
286 textInput->Initialize();
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
296 mDisplayedTextView(),
297 mStyledPlaceHolderText(),
298 mMaxStringLength( DEFAULT_MAX_SIZE ),
299 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300 mCursorPosition( 0 ),
301 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302 mIsSelectionHandleOneFlipped( false ),
303 mIsSelectionHandleTwoFlipped( false ),
304 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308 mSelectionHandleOnePosition( 0 ),
309 mSelectionHandleTwoPosition( 0 ),
311 mPreEditStartPosition( 0 ),
312 mPreEditLength ( 0 ),
313 mNumberOfSurroundingCharactersDeleted( 0 ),
314 mTouchStartTime( 0 ),
316 mCurrentCopySelecton(),
319 mScrollDisplacement(),
320 mCurrentHandlePosition(),
321 mCurrentSelectionId(),
322 mCurrentSelectionHandlePosition(),
323 mRequestedSelection( 0, 0 ),
324 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
327 mMaterialColor( LIGHTBLUE ),
328 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329 mOverrideAutomaticAlignment( false ),
330 mCursorRTLEnabled( false ),
331 mClosestCursorPositionEOL ( false ),
332 mCursorBlinkStatus( true ),
333 mCursorVisibility( false ),
334 mGrabHandleVisibility( false ),
335 mIsCursorInScrollArea( true ),
336 mIsGrabHandleInScrollArea( true ),
337 mEditModeActive( false ),
338 mEditOnTouch( true ),
339 mTextSelection( true ),
340 mExceedEnabled( true ),
341 mGrabHandleEnabled( true ),
342 mIsSelectionHandleFlipEnabled( true ),
343 mPreEditFlag( false ),
344 mIgnoreCommitFlag( false ),
345 mIgnoreFirstCommitFlag( false ),
346 mSelectingText( false ),
347 mPreserveCursorPosition( false ),
348 mSelectTextOnCommit( false ),
349 mUnderlinedPriorToPreEdit ( false ),
350 mCommitByKeyInput( false ),
351 mPlaceHolderSet( false ),
352 mMarkUpEnabled( false )
354 // Updates the line height accordingly with the input style.
358 TextInput::~TextInput()
360 StopCursorBlinkTimer();
365 std::string TextInput::GetText() const
369 // Return text-view's text only if the text-input's text is not empty
370 // in order to not to return the placeholder text.
371 if( !mStyledText.empty() )
373 text = mDisplayedTextView.GetText();
379 std::string TextInput::GetMarkupText() const
381 std::string markupString;
382 MarkupProcessor::GetMarkupString( mStyledText, markupString );
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
389 // Get the placeholder styled text array from the markup string.
390 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
392 if( mStyledText.empty() )
394 // Set the placeholder text only if the styled text is empty.
395 mDisplayedTextView.SetText( mStyledPlaceHolderText );
396 mPlaceHolderSet = true;
400 std::string TextInput::GetPlaceholderText()
402 // Traverses the styled placeholder array getting only the text.
403 // Note that for some languages a 'character' could be represented by more than one 'char'
405 std::string placeholderText;
406 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
408 placeholderText.append( (*it).mText.GetText() );
411 return placeholderText ;
414 void TextInput::SetInitialText(const std::string& initialText)
416 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
418 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
420 mPreEditFlag = false;
421 mIgnoreCommitFlag = true;
424 SetText( initialText );
425 PreEditReset( false ); // Reset keyboard as text changed
428 void TextInput::SetText(const std::string& initialText)
430 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
432 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
434 if( mStyledText.empty() )
436 // If the initial text is empty, set the placeholder text.
437 mDisplayedTextView.SetText( mStyledPlaceHolderText );
438 mPlaceHolderSet = true;
442 mDisplayedTextView.SetText( mStyledText );
443 mPlaceHolderSet = false;
448 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
450 ImfManager imfManager = ImfManager::Get();
453 imfManager.SetCursorPosition( mCursorPosition );
454 imfManager.SetSurroundingText( initialText );
455 imfManager.NotifyCursorPosition();
458 if( IsScrollEnabled() )
460 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
463 ShowGrabHandleAndSetVisibility( false );
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
474 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
476 mDisplayedTextView.SetText( styleText );
477 mPlaceHolderSet = false;
479 // If text alignment hasn't been manually set by application developer, then we
480 // automatically determine the alignment based on the content of the text i.e. what
481 // language the text begins with.
482 // TODO: This should determine different alignments for each line (broken by '\n') of text.
483 if(!mOverrideAutomaticAlignment)
485 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486 bool leftToRight(true);
488 if( !styleText.empty() )
490 bool breakOut(false);
492 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
494 const Text& text = textIter->mText;
496 for( std::size_t i = 0; i < text.GetLength(); ++i )
498 Character character( text[i] );
499 if( character.GetCharacterDirection() != Character::Neutral )
501 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
509 // Based on this direction, either left or right align text if not manually set by application developer.
510 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512 Toolkit::Alignment::VerticalTop ) );
513 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
521 mMaxStringLength = maxChars;
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
526 DALI_ASSERT_DEBUG( maxLines > 0 )
530 mNumberOflinesLimit = maxLines;
534 std::size_t TextInput::GetNumberOfLinesLimit() const
536 return mNumberOflinesLimit;
539 std::size_t TextInput::GetNumberOfCharacters() const
541 return mStyledText.size();
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
547 mMaterialColor = color;
548 if ( mCustomMaterial )
550 mCustomMaterial.SetDiffuseColor( mMaterialColor );
551 mMeshData.SetMaterial( mCustomMaterial );
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
557 return mMaterialColor;
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
564 return mInputStartedSignalV2;
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
569 return mInputFinishedSignalV2;
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
574 return mCutAndPasteToolBarDisplayedV2;
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
579 return mStyleChangedSignalV2;
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
584 return mTextModifiedSignal;
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
589 return mMaxInputCharactersReachedSignalV2;
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
594 return mInputTextExceedBoundariesSignalV2;
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
599 Dali::BaseHandle handle( object );
601 bool connected( true );
602 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
604 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
606 textInput.InputStartedSignal().Connect( tracker, functor );
608 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
610 textInput.InputFinishedSignal().Connect( tracker, functor );
612 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
614 textInput.StyleChangedSignal().Connect( tracker, functor );
616 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
618 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
620 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
622 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
626 // signalName does not match any signal
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
637 // update line height before calculate the actual position.
642 if( setCursorOnTouchPoint )
644 // Sets the cursor position for the given touch point.
645 ReturnClosestIndex( touchPoint, mCursorPosition );
647 // Creates the grab handle.
648 if( IsGrabHandleEnabled() )
650 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
654 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657 ShowGrabHandleAndSetVisibility( true );
659 // Scrolls the text-view if needed.
660 if( IsScrollEnabled() )
662 ScrollTextViewToMakeCursorVisible( cursorPosition );
668 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
680 bool TextInput::IsEditable() const
682 return mEditModeActive;
685 void TextInput::SetEditOnTouch( bool editOnTouch )
687 mEditOnTouch = editOnTouch;
690 bool TextInput::IsEditOnTouch() const
695 void TextInput::SetTextSelectable( bool textSelectable )
697 mTextSelection = textSelectable;
700 bool TextInput::IsTextSelectable() const
702 return mTextSelection;
705 bool TextInput::IsTextSelected() const
707 return mHighlightMeshActor;
710 void TextInput::DeSelectText()
717 void TextInput::SetGrabHandleImage(Dali::Image image )
721 CreateGrabHandle(image);
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
727 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
731 mCursor.SetImage( image );
732 mCursor.SetNinePatchBorder( border );
736 Vector3 TextInput::GetSelectionHandleSize()
738 return DEFAULT_SELECTION_HANDLE_SIZE;
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
743 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
747 mCursorRTL.SetImage( image);
748 mCursorRTL.SetNinePatchBorder( border );
752 void TextInput::EnableGrabHandle(bool toggle)
754 // enables grab handle with will in turn de-activate magnifier
755 mGrabHandleEnabled = toggle;
758 bool TextInput::IsGrabHandleEnabled()
760 // if false then magnifier will be shown instead.
761 return mGrabHandleEnabled;
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
766 // Deprecated function. To be removed.
767 mIsSelectionHandleFlipEnabled = toggle;
770 bool TextInput::IsSelectionHandleFlipEnabled()
772 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
778 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
782 mSelectionHandleFlipMargin = margin;
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
787 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
793 const Vector4 boundary( originX,
795 originX + boundingRectangle.width,
796 originY + boundingRectangle.height );
798 mBoundingRectangleWorldCoordinates = boundary;
801 const Rect<float> TextInput::GetBoundingRectangle() const
803 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
805 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
808 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
815 return mSelectionHandleFlipMargin;
818 void TextInput::SetTextColor( const Vector4& color )
820 mDisplayedTextView.SetColor( color );
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
825 if( style != mInputStyle )
828 bool emitSignal = false;
830 // mask: modify style according to mask, if different emit signal.
831 const TextStyle oldInputStyle( mInputStyle );
833 // Copy the new style.
834 mInputStyle.Copy( style, mask );
836 // if style has changed, emit signal.
837 if( oldInputStyle != mInputStyle )
842 // Updates the line height accordingly with the input style.
845 // Changing font point size will require the cursor to be re-sized
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
857 if ( IsTextSelected() )
859 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
862 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
864 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
867 // Keeps the old style to be compared with the new one.
868 const TextStyle oldInputStyle( mInputStyle );
870 // Copy only those parameters from the style which are set in the mask.
871 mInputStyle.Copy( style, mask );
873 if( mInputStyle != oldInputStyle )
875 // Updates the line height accordingly with the input style.
878 EmitStyleChangedSignal();
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
885 if( !mStyledText.empty() )
887 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
891 TextStyle TextInput::GetStyleAtCursor() const
895 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
897 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898 style = mStyledText.at( mCursorPosition-1 ).mStyle;
904 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
906 Dali::Font defaultFont = Dali::Font::New();
907 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
916 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
918 if( position >= mStyledText.size() )
920 position = mStyledText.size() - 1;
923 return mStyledText.at( position ).mStyle;
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
928 mDisplayedTextView.SetTextAlignment( align );
929 mOverrideAutomaticAlignment = true;
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
934 mDisplayedTextView.SetLineJustification( justification );
935 mOverrideAutomaticAlignment = true;
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
940 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
945 return mDisplayedTextView.GetFadeBoundary();
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
950 return mDisplayedTextView.GetTextAlignment();
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
955 mDisplayedTextView.SetMultilinePolicy( policy );
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
960 return mDisplayedTextView.GetMultilinePolicy();
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
965 mDisplayedTextView.SetWidthExceedPolicy( policy );
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
970 return mDisplayedTextView.GetWidthExceedPolicy();
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
975 mDisplayedTextView.SetHeightExceedPolicy( policy );
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
980 return mDisplayedTextView.GetHeightExceedPolicy();
983 void TextInput::SetExceedEnabled( bool enable )
985 mExceedEnabled = enable;
988 bool TextInput::GetExceedEnabled() const
990 return mExceedEnabled;
993 void TextInput::SetBackground(Dali::Image image )
995 // TODO Should add this function and add public api to match.
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1005 switch( event.state )
1007 case KeyEvent::Down:
1009 return OnKeyDownEvent(event);
1015 return OnKeyUpEvent(event);
1027 void TextInput::OnKeyInputFocusGained()
1029 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1031 mEditModeActive = true;
1033 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1035 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1037 // Updates the line height accordingly with the input style.
1040 // Connect the signals to use in text input.
1041 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1044 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1047 GetTextLayoutInfo();
1050 SetCursorVisibility( true );
1051 StartCursorBlinkTimer();
1053 Toolkit::TextInput handle( GetOwner() );
1054 mInputStartedSignalV2.Emit( handle );
1056 ImfManager imfManager = ImfManager::Get();
1060 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1062 // Notify that the text editing start.
1063 imfManager.Activate();
1065 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066 imfManager.SetRestoreAferFocusLost( true );
1068 imfManager.SetCursorPosition( mCursorPosition );
1069 imfManager.NotifyCursorPosition();
1072 mClipboard = Clipboard::Get(); // Store handle to clipboard
1074 // Now in edit mode we can accept string to paste from clipboard
1075 if( Adaptor::IsAvailable() )
1077 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1080 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1085 void TextInput::OnKeyInputFocusLost()
1087 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1091 // If key input focus is lost, it removes the
1092 // underline from the last pre-edit text.
1093 RemovePreEditStyle();
1094 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1095 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1099 ImfManager imfManager = ImfManager::Get();
1102 // The text editing is finished. Therefore the imf manager don't have restore activation.
1103 imfManager.SetRestoreAferFocusLost( false );
1105 // Notify that the text editing finish.
1106 imfManager.Deactivate();
1108 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1110 // Disconnect signal used the text input.
1111 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1113 Toolkit::TextInput handle( GetOwner() );
1114 mInputFinishedSignalV2.Emit( handle );
1115 mEditModeActive = false;
1116 mPreEditFlag = false;
1118 SetCursorVisibility( false );
1119 StopCursorBlinkTimer();
1121 ShowGrabHandleAndSetVisibility( false );
1124 // No longer in edit mode so do not want to receive string from clipboard
1125 if( Adaptor::IsAvailable() )
1127 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1130 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1132 Clipboard clipboard = Clipboard::Get();
1136 clipboard.HideClipboard();
1141 void TextInput::OnControlStageConnection()
1143 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1145 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1147 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1151 void TextInput::CreateActiveLayer()
1153 Actor self = Self();
1154 mActiveLayer = Layer::New();
1155 mActiveLayer.SetName ( "ActiveLayerActor" );
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1174 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1176 Actor self = Self();
1177 self.Add( mCursor );
1178 self.Add( mCursorRTL );
1180 mCursorVisibility = false;
1182 CreateActiveLayer(); // todo move this so layer only created when needed.
1184 // Assign names to image actors
1185 mCursor.SetName("mainCursor");
1186 mCursorRTL.SetName("rtlCursor");
1189 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1191 mDisplayedTextView.SetSize( targetSize );
1192 GetTextLayoutInfo();
1193 mActiveLayer.SetSize(targetSize);
1196 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1198 Relayout( mDisplayedTextView, size, container );
1199 Relayout( mPopupPanel.GetRootActor(), size, container );
1201 GetTextLayoutInfo();
1206 Vector3 TextInput::GetNaturalSize()
1208 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1210 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1212 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1213 naturalSize.height = mLineHeight;
1219 float TextInput::GetHeightForWidth( float width )
1221 float height = mDisplayedTextView.GetHeightForWidth( width );
1223 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1225 // If the height is zero, it means there is no text. Let's return the cursor height.
1226 height = mLineHeight;
1232 /*end of Virtual methods from parent*/
1234 // Private Internal methods
1236 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1238 switch (gesture.state)
1240 case Gesture::Started:
1241 // fall through so code not duplicated
1242 case Gesture::Continuing:
1244 if (actor == mGrabArea)
1246 SetCursorVisibility( true );
1247 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1248 MoveGrabHandle( gesture.displacement );
1249 HidePopup(); // Do not show popup whilst handle is moving
1251 else if (actor == mHandleOneGrabArea)
1253 // the displacement in PanGesture is affected by the actor's rotation.
1254 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1255 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1257 MoveSelectionHandle( HandleOne, gesture.displacement );
1259 mState = StateDraggingHandle;
1262 else if (actor == mHandleTwoGrabArea)
1264 // the displacement in PanGesture is affected by the actor's rotation.
1265 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1268 MoveSelectionHandle( HandleTwo, gesture.displacement );
1270 mState = StateDraggingHandle;
1276 case Gesture::Finished:
1278 // Revert back to non-pressed selection handle images
1279 if (actor == mGrabArea)
1281 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1282 SetCursorVisibility( true );
1283 SetUpPopupSelection();
1286 if (actor == mHandleOneGrabArea)
1288 // the displacement in PanGesture is affected by the actor's rotation.
1289 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1290 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1292 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1294 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1296 ShowPopupCutCopyPaste();
1298 if (actor == mHandleTwoGrabArea)
1300 // the displacement in PanGesture is affected by the actor's rotation.
1301 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1302 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1304 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1306 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1308 ShowPopupCutCopyPaste();
1317 // Stop the flashing animation so easy to see when moved.
1318 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1320 if (touch.GetPoint(0).state == TouchPoint::Down)
1322 SetCursorVisibility( true );
1323 StopCursorBlinkTimer();
1325 else if (touch.GetPoint(0).state == TouchPoint::Up)
1327 SetCursorVisibility( true );
1328 StartCursorBlinkTimer();
1333 // selection handle one
1334 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1336 if (touch.GetPoint(0).state == TouchPoint::Down)
1338 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1340 else if (touch.GetPoint(0).state == TouchPoint::Up)
1342 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1347 // selection handle two
1348 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1350 if (touch.GetPoint(0).state == TouchPoint::Down)
1352 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1354 else if (touch.GetPoint(0).state == TouchPoint::Up)
1356 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1361 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1363 // If text exists then select nearest word.
1364 if ( !mStyledText.empty())
1368 ShowGrabHandleAndSetVisibility( false );
1373 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1374 // converts the pre-edit word being displayed to a committed word.
1375 if ( !mUnderlinedPriorToPreEdit )
1378 style.SetUnderline( false );
1379 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1381 mPreEditFlag = false;
1382 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1383 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1384 PreEditReset( false );
1386 mCursorPosition = 0;
1388 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1389 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1391 std::size_t start = 0;
1392 std::size_t end = 0;
1393 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1395 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1397 ImfManager imfManager = ImfManager::Get();
1400 imfManager.SetCursorPosition ( mCursorPosition );
1401 imfManager.NotifyCursorPosition();
1404 SelectText( start, end );
1406 // if no text but clipboard has content then show paste option
1407 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1409 ShowPopupCutCopyPaste();
1412 // If no text and clipboard empty then do nothing
1415 // TODO: Change the function name to be more general.
1416 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1418 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1419 , (mEditOnTouch)?"true":"false"
1420 , (mEditModeActive)?"true":"false");
1422 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1427 if( mGrabArea == actor )
1429 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1431 SetUpPopupSelection();
1441 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1443 // Initially don't create the grab handle.
1444 bool createGrabHandle = false;
1446 if ( !mEditModeActive )
1448 // update line height before calculate the actual position.
1451 // Only start edit mode if TextInput configured to edit on touch
1454 // Set the initial cursor position in the tap point.
1455 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1457 // Create the grab handle.
1458 // TODO Make this a re-usable function.
1459 if ( IsGrabHandleEnabled() )
1461 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1465 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1466 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1467 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1468 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1472 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1473 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1474 // otherwise the Grab handle will be shown when selecting.
1481 // Show the keyboard if it was hidden.
1482 if (!VirtualKeyboard::IsVisible())
1484 VirtualKeyboard::Show();
1487 // Reset keyboard as tap event has occurred.
1488 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1489 PreEditReset( true );
1491 GetTextLayoutInfo();
1493 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1495 // 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.
1497 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1499 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1501 // Notify keyboard so it can 're-capture' word for predictive text.
1502 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1503 ImfManager imfManager = ImfManager::Get();
1506 imfManager.SetCursorPosition ( mCursorPosition );
1507 imfManager.NotifyCursorPosition();
1509 const TextStyle oldInputStyle( mInputStyle );
1511 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1515 // Create the grab handle.
1516 // Grab handle is created later.
1517 createGrabHandle = true;
1519 if( oldInputStyle != mInputStyle )
1521 // Updates the line height accordingly with the input style.
1524 EmitStyleChangedSignal();
1529 if ( createGrabHandle && IsGrabHandleEnabled() )
1531 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1535 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1536 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1537 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1538 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1543 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1545 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1547 // Ignore longpress if in selection mode already
1548 if( mHighlightMeshActor )
1553 if(longPress.state == Dali::Gesture::Started)
1555 // Start edit mode on long press
1556 if ( !mEditModeActive )
1561 // If text exists then select nearest word.
1562 if ( !mStyledText.empty())
1566 ShowGrabHandleAndSetVisibility( false );
1571 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1572 // converts the pre-edit word being displayed to a committed word.
1573 if ( !mUnderlinedPriorToPreEdit )
1576 style.SetUnderline( false );
1577 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1579 mPreEditFlag = false;
1580 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1581 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1582 PreEditReset( false );
1584 mCursorPosition = 0;
1586 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1587 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1589 std::size_t start = 0;
1590 std::size_t end = 0;
1591 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1593 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1595 ImfManager imfManager = ImfManager::Get();
1598 imfManager.SetCursorPosition ( mCursorPosition );
1599 imfManager.NotifyCursorPosition();
1602 SelectText( start, end );
1605 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1606 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1608 ShowPopupCutCopyPaste();
1613 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1615 const Text clipboardText( notifier.GetContent() );
1616 PasteText( clipboardText );
1618 SetCursorVisibility( true );
1619 StartCursorBlinkTimer();
1621 ShowGrabHandleAndSetVisibility( false );
1627 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1629 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1631 const std::string& name = button.GetName();
1633 if(name == TextInputPopup::OPTION_SELECT_WORD)
1635 std::size_t start = 0;
1636 std::size_t end = 0;
1637 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1639 SelectText( start, end );
1641 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1643 SetCursorVisibility(false);
1644 StopCursorBlinkTimer();
1646 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1647 std::size_t start = 0;
1649 SelectText( start, end );
1651 else if(name == TextInputPopup::OPTION_CUT)
1653 bool ret = CopySelectedTextToClipboard();
1657 DeleteHighlightedText( true );
1661 SetCursorVisibility( true );
1662 StartCursorBlinkTimer();
1666 else if(name == TextInputPopup::OPTION_COPY)
1668 CopySelectedTextToClipboard();
1672 SetCursorVisibility( true );
1673 StartCursorBlinkTimer();
1677 else if(name == TextInputPopup::OPTION_PASTE)
1679 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1681 PasteText(retrievedString);
1683 SetCursorVisibility( true );
1684 StartCursorBlinkTimer();
1686 ShowGrabHandleAndSetVisibility( false );
1690 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1692 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1693 // Hence pass the false parameter for signalFinished.
1694 HidePopup( true, false );
1695 mClipboard.ShowClipboard();
1701 bool TextInput::OnCursorBlinkTimerTick()
1704 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1705 if ( mCursorRTLEnabled )
1707 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1709 mCursorBlinkStatus = !mCursorBlinkStatus;
1714 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1716 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1718 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1719 if(mHighlightMeshActor && mState == StateEdit)
1721 ShowPopupCutCopyPaste();
1725 //FIXME this routine needs to be re-written as it contains too many branches.
1726 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1728 std::string keyName = event.keyPressedName;
1729 std::string keyString = event.keyPressed;
1731 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1733 // Do not consume "Tab" and "Escape" keys.
1734 if(keyName == "Tab" || keyName == "Escape")
1736 // Escape key to end the edit mode
1742 HidePopup(); // If Pop-up shown then hides it as editing text.
1744 // Update Flag, indicates whether to update the text-input contents or not.
1745 // Any key stroke that results in a visual change of the text-input should
1746 // set this flag to true.
1749 // Whether to scroll text to cursor position.
1750 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1751 bool scroll = false;
1753 if (keyName == "Return")
1755 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1757 bool preEditFlagPreviouslySet( mPreEditFlag );
1759 // replaces highlighted text with new line
1760 DeleteHighlightedText( false );
1762 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1764 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1765 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1768 mCommitByKeyInput = true;
1771 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1772 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1774 mPreEditFlag = true;
1775 mIgnoreCommitFlag = false;
1785 else if ( keyName == "space" )
1787 if ( mHighlightMeshActor )
1789 // Some text is selected so erase it before adding space.
1790 DeleteHighlightedText( true );
1794 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1796 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1797 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1800 mCommitByKeyInput = true;
1805 else if (keyName == "BackSpace")
1807 if ( mHighlightMeshActor )
1809 // Some text is selected so erase it
1810 DeleteHighlightedText( true );
1815 if ( mCursorPosition > 0 )
1817 DeleteCharacter( mCursorPosition );
1823 else if (keyName == "Right")
1828 else if (keyName == "Left")
1830 AdvanceCursor(true);
1833 else // event is a character
1835 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1836 if ( !keyString.empty() )
1838 // replaces highlighted text with new character
1839 DeleteHighlightedText( false );
1841 // Received key String
1842 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1848 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1849 // as this is a costly operation.
1855 if(update || scroll)
1857 if( IsScrollEnabled() )
1859 // Calculates the new cursor position (in actor coordinates)
1860 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1862 ScrollTextViewToMakeCursorVisible( cursorPosition );
1869 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1871 std::string keyName = event.keyPressedName;
1872 std::string keyString = event.keyPressed;
1874 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1876 // The selected text become deselected when the key code is DALI_KEY_BACK.
1877 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1886 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1888 // Updates the stored scroll position.
1889 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1891 const Vector3& controlSize = GetControlSize();
1892 Size cursorSize( CURSOR_THICKNESS, 0.f );
1894 // Updates the cursor and grab handle position and visibility.
1895 if( mGrabHandle || mCursor )
1897 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1898 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1900 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1902 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1906 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1907 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1912 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1913 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1917 // Updates the selection handles and highlighted text position and visibility.
1918 if( mSelectionHandleOne && mSelectionHandleTwo )
1920 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1921 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1922 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1923 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1924 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1925 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1927 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1928 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1930 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1931 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1932 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1933 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1935 if( mHighlightMeshActor )
1937 mHighlightMeshActor.SetVisible( true );
1943 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1945 // Scroll the text to make the cursor visible.
1946 const Size cursorSize( CURSOR_THICKNESS,
1947 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1949 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1951 const Vector3& controlSize = GetControlSize();
1953 // Calculates the new scroll position.
1954 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1955 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1957 scrollOffset.x += cursorPosition.x;
1960 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1962 scrollOffset.y += cursorPosition.y;
1965 // Sets the new scroll position.
1966 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1967 SetScrollPosition( scrollOffset );
1970 void TextInput::StartScrollTimer()
1974 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1975 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1978 if( !mScrollTimer.IsRunning() )
1980 mScrollTimer.Start();
1984 void TextInput::StopScrollTimer()
1988 mScrollTimer.Stop();
1992 bool TextInput::OnScrollTimerTick()
1994 // TODO: need to set the new style accordingly the new handle position.
1996 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1998 // nothing to do if all handles are invisible or doesn't exist.
2004 // Choose between the grab handle or the selection handles.
2005 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2006 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2007 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2009 std::size_t newCursorPosition = 0;
2010 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2012 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2013 // the new selection handle's position needs to be different of the other one.
2014 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2015 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2016 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2018 if( differentSelectionHandles )
2020 handlePosition = newCursorPosition;
2022 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2024 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2026 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2027 scrollPosition += scrollDelta;
2028 SetScrollPosition( scrollPosition );
2030 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2035 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2038 actualHandlePosition.x += mScrollDisplacement.x;
2039 actualHandlePosition.y += mScrollDisplacement.y;
2044 // Public Internal Methods (public for testing purpose)
2046 void TextInput::SetUpTouchEvents()
2048 if ( !mTapDetector )
2050 mTapDetector = TapGestureDetector::New();
2051 // Attach the actors and connect the signal
2052 mTapDetector.Attach(Self());
2054 // As contains children which may register for tap the default control detector is not used.
2055 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2058 if ( !mDoubleTapDetector )
2060 mDoubleTapDetector = TapGestureDetector::New();
2061 mDoubleTapDetector.SetTapsRequired( 2 );
2062 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2064 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2065 // so that we do not, unnecessarily, have a double tap request all the time
2068 if ( !mPanGestureDetector )
2070 mPanGestureDetector = PanGestureDetector::New();
2071 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2074 if ( !mLongPressDetector )
2076 mLongPressDetector = LongPressGestureDetector::New();
2077 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2078 mLongPressDetector.Attach(Self());
2082 void TextInput::CreateTextViewActor()
2084 mDisplayedTextView = Toolkit::TextView::New();
2085 mDisplayedTextView.SetName( "DisplayedTextView ");
2086 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2087 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2088 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2089 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2090 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2091 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2092 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2093 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2094 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2095 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2097 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2099 Self().Add( mDisplayedTextView );
2102 // Start a timer to initiate, used by the cursor to blink.
2103 void TextInput::StartCursorBlinkTimer()
2105 if ( !mCursorBlinkTimer )
2107 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2108 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2111 if ( !mCursorBlinkTimer.IsRunning() )
2113 mCursorBlinkTimer.Start();
2117 // Start a timer to initiate, used by the cursor to blink.
2118 void TextInput::StopCursorBlinkTimer()
2120 if ( mCursorBlinkTimer )
2122 mCursorBlinkTimer.Stop();
2126 void TextInput::StartEditMode()
2128 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2130 if(!mEditModeActive)
2135 if ( mDoubleTapDetector )
2137 mDoubleTapDetector.Attach( Self() );
2141 void TextInput::EndEditMode()
2143 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2145 ClearKeyInputFocus();
2147 if ( mDoubleTapDetector )
2149 mDoubleTapDetector.Detach( Self() );
2153 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2155 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2157 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2159 style.SetUnderline( true );
2160 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2164 void TextInput::RemovePreEditStyle()
2166 if ( !mUnderlinedPriorToPreEdit )
2169 style.SetUnderline( false );
2170 SetActiveStyle( style, TextStyle::UNDERLINE );
2174 // IMF related methods
2177 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2179 bool update( false );
2180 bool preeditResetRequired ( false );
2182 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2184 HidePopup(); // If Pop-up shown then hides it as editing text.
2187 switch ( imfEvent.eventName )
2189 case ImfManager::PREEDIT:
2191 mIgnoreFirstCommitFlag = false;
2193 // 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
2194 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2196 // replaces highlighted text with new character
2197 DeleteHighlightedText( false );
2200 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2202 if( IsScrollEnabled() )
2204 // Calculates the new cursor position (in actor coordinates)
2205 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2206 ScrollTextViewToMakeCursorVisible( cursorPosition );
2213 case ImfManager::COMMIT:
2215 if( mIgnoreFirstCommitFlag )
2217 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2218 mIgnoreFirstCommitFlag = false;
2222 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2224 // 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
2225 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2227 // replaces highlighted text with new character
2228 DeleteHighlightedText( false );
2231 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2232 // not needed, one such scenario is when the pre-edit word is too long to fit.
2233 if ( !mIgnoreCommitFlag )
2235 update = CommitReceived( imfEvent.predictiveString );
2239 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2245 if( IsScrollEnabled() )
2247 // Calculates the new cursor position (in actor coordinates)
2248 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2250 ScrollTextViewToMakeCursorVisible( cursorPosition );
2255 case ImfManager::DELETESURROUNDING:
2257 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2258 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2260 mPreEditFlag = false;
2262 std::size_t toDelete = 0;
2263 std::size_t numberOfCharacters = 0;
2265 if( mHighlightMeshActor )
2267 // delete highlighted text.
2268 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2269 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2273 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2275 toDelete = mCursorPosition + imfEvent.cursorOffset;
2277 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2279 numberOfCharacters = mStyledText.size() - toDelete;
2283 numberOfCharacters = imfEvent.numberOfChars;
2286 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2287 DeleteRange( toDelete, numberOfCharacters );
2289 mCursorPosition = toDelete;
2290 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2294 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2297 case ImfManager::GETSURROUNDING:
2299 // 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
2300 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2301 if (! ( mHighlightMeshActor || mSelectingText ) )
2303 std::string text( GetText() );
2304 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2306 imfManager.SetCursorPosition( mCursorPosition );
2307 imfManager.SetSurroundingText( text );
2310 if( 0 != mNumberOfSurroundingCharactersDeleted )
2312 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2313 mNumberOfSurroundingCharactersDeleted = 0;
2315 if( mStyledText.empty() )
2317 // Styled text is empty, so set the placeholder text.
2318 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2319 mPlaceHolderSet = true;
2324 case ImfManager::VOID:
2326 DALI_ASSERT_DEBUG( false );
2330 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2332 return callbackData;
2335 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2337 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2339 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2340 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2342 bool preeditResetRequest ( false );
2344 if( mPreEditFlag ) // Already in pre-edit state.
2346 if( mStyledText.size() >= mMaxStringLength )
2348 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2349 // Cannot fit these characters into field, clear pre-edit.
2350 if ( !mUnderlinedPriorToPreEdit )
2353 style.SetUnderline( false );
2354 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2356 mIgnoreCommitFlag = true;
2357 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2358 mPreEditFlag = false;
2359 EmitMaxInputCharactersReachedSignal();
2363 // delete existing pre-edit string
2364 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2366 // Store new pre-edit string
2367 mPreEditString.SetText( keyString );
2369 if ( keyString.empty() )
2371 mPreEditFlag = false;
2372 mCursorPosition = mPreEditStartPosition;
2374 if( mStyledText.empty() )
2376 // Styled text is empty, so set the placeholder text.
2377 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2378 mPlaceHolderSet = true;
2382 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2384 GetTextLayoutInfo();
2389 // Insert new pre-edit string. InsertAt updates the size and position table.
2390 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2391 // 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.
2392 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2393 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2394 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2397 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2401 else // mPreEditFlag not set
2403 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2405 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2406 // new pre-edit so move into pre-edit state by setting flag
2407 mPreEditFlag = true;
2408 mPreEditString.SetText( keyString ); // store new pre-edit string
2409 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2410 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2411 // 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.
2412 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2413 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2414 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2415 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2421 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2425 return preeditResetRequest;
2428 bool TextInput::CommitReceived(const std::string& keyString )
2430 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2431 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2433 bool update( false );
2435 RemovePreEditStyle();
2437 const std::size_t styledTextSize( mStyledText.size() );
2438 if( styledTextSize >= mMaxStringLength )
2440 // Cannot fit these characters into field, clear pre-edit.
2443 mIgnoreCommitFlag = true;
2444 mPreEditFlag = false;
2446 EmitMaxInputCharactersReachedSignal();
2452 // delete existing pre-edit string
2453 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2454 mPreEditFlag = false;
2456 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2457 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2459 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2461 // No need to update cursor position as Cursor location given by touch.
2462 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2463 mPreserveCursorPosition = false;
2467 // Cursor not set by touch so needs to be re-positioned to input more text
2468 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2470 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2471 if ( mCommitByKeyInput )
2473 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2474 mCommitByKeyInput = false;
2480 if ( mSelectTextOnCommit )
2482 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2487 else // mPreEditFlag not set
2489 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2491 if( mStyledText.empty() && mPlaceHolderSet )
2493 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2494 mDisplayedTextView.SetText( "" );
2495 mNumberOfSurroundingCharactersDeleted = 0;
2496 mPlaceHolderSet = false;
2498 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2500 mNumberOfSurroundingCharactersDeleted = 0;
2505 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2510 mSelectTextOnCommit = false;
2512 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2513 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2518 // End of IMF related methods
2520 std::size_t TextInput::DeletePreEdit()
2522 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2524 DALI_ASSERT_DEBUG( mPreEditFlag );
2526 const std::size_t preEditStringLength = mPreEditString.GetLength();
2527 const std::size_t styledTextSize = mStyledText.size();
2529 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2531 // Prevents erase items outside mStyledText bounds.
2532 if( mPreEditStartPosition > styledTextSize )
2534 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2535 mPreEditStartPosition = styledTextSize;
2538 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2540 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2541 endPosition = styledTextSize;
2544 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2546 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2547 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2549 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2551 return preEditStringLength;
2554 void TextInput::PreEditReset( bool preserveCursorPosition )
2556 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2557 preserveCursorPosition, mCursorPosition);
2559 // 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.
2560 mPreserveCursorPosition = preserveCursorPosition;
2562 // Reset incase we are in a pre-edit state.
2563 ImfManager imfManager = ImfManager::Get();
2566 imfManager.Reset(); // Will trigger a commit message
2570 void TextInput::CursorUpdate()
2574 ImfManager imfManager = ImfManager::Get();
2577 std::string text( GetText() );
2578 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2579 imfManager.SetCursorPosition ( mCursorPosition );
2580 imfManager.NotifyCursorPosition();
2584 /* Delete highlighted characters redisplay*/
2585 void TextInput::DeleteHighlightedText( bool inheritStyle )
2587 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2589 if( mHighlightMeshActor )
2591 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2593 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2594 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2596 // Get the styled text of the characters to be deleted as it may be needed if
2597 // the "exceed the text-input's boundaries" option is disabled.
2598 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2600 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2602 mStyledText.erase( start, end ); // erase range of characters
2604 // Remove text from TextView.
2606 if( mStyledText.empty() )
2608 // Styled text is empty, so set the placeholder text.
2609 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2610 mPlaceHolderSet = true;
2614 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2616 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2618 // It may happen than after removing a white space or a new line character,
2619 // two words merge, this new word could be big enough to not fit in its
2620 // current line, so moved to the next one, and make some part of the text to
2621 // exceed the text-input's boundary.
2622 if( !mExceedEnabled )
2624 // Get the new text layout after removing some characters.
2625 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2627 // Get text-input's size.
2628 const Vector3& size = GetControlSize();
2630 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2631 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2633 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2635 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2636 styledCharactersToDelete.begin(),
2637 styledCharactersToDelete.end() );
2641 GetTextLayoutInfo();
2649 const TextStyle oldInputStyle( mInputStyle );
2651 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2653 if( oldInputStyle != mInputStyle )
2655 // Updates the line height accordingly with the input style.
2658 EmitStyleChangedSignal();
2664 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2666 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2667 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2669 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2672 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2674 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2675 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2677 mStyledText.erase(itStart, itEnd);
2679 // update the selection handles if they are visible.
2680 if( mHighlightMeshActor )
2682 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2683 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2685 if( minHandle >= start + ncharacters )
2687 minHandle -= ncharacters;
2689 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2694 if( maxHandle >= start + ncharacters )
2696 maxHandle -= ncharacters;
2698 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2704 // 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.
2707 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2709 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2710 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2711 // Mean we do not re-draw the text more than we have too.
2714 /* Delete character at current cursor position and redisplay*/
2715 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2717 // Ensure positionToDelete is not out of bounds.
2718 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2719 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2720 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2722 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2725 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2727 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2729 // Get the styled text of the character to be deleted as it may be needed if
2730 // the "exceed the text-input's boundaries" option is disabled.
2731 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2733 mStyledText.erase(it); // erase the character left of positionToDelete
2735 if( mStyledText.empty() )
2737 // Styled text is empty, so set the placeholder text.
2738 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2739 mPlaceHolderSet = true;
2743 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2745 const Character characterToDelete = styledCharacterToDelete.mText[0];
2747 // It may happen than after removing a white space or a new line character,
2748 // two words merge, this new word could be big enough to not fit in its
2749 // current line, so moved to the next one, and make some part of the text to
2750 // exceed the text-input's boundary.
2751 if( !mExceedEnabled )
2753 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2755 // Get the new text layout after removing one character.
2756 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2758 // Get text-input's size.
2759 const Vector3& size = GetControlSize();
2761 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2762 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2764 MarkupProcessor::StyledTextArray array;
2765 array.push_back( styledCharacterToDelete );
2766 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2768 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2773 GetTextLayoutInfo();
2775 ShowGrabHandleAndSetVisibility( false );
2777 mCursorPosition = positionToDelete -1;
2779 const TextStyle oldInputStyle( mInputStyle );
2781 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2783 if( oldInputStyle != mInputStyle )
2785 // Updates the line height accordingly with the input style.
2788 EmitStyleChangedSignal();
2793 /*Insert new character into the string and (optionally) redisplay text-input*/
2794 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2796 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2798 // Ensure insertionPosition is not out of bounds.
2799 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2801 bool textExceedsMaximunNumberOfCharacters = false;
2802 bool textExceedsBoundary = false;
2803 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2805 ShowGrabHandleAndSetVisibility( false );
2807 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2811 mIgnoreCommitFlag = true;
2812 mPreEditFlag = false;
2813 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2814 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2817 if( textExceedsMaximunNumberOfCharacters )
2819 EmitMaxInputCharactersReachedSignal();
2822 if( textExceedsBoundary )
2824 EmitInputTextExceedsBoundariesSignal();
2825 PreEditReset( false );
2829 return insertedStringLength;
2832 ImageActor TextInput::CreateCursor( const Vector4& color)
2835 cursor = CreateSolidColorActor(color);
2836 cursor.SetName( "Cursor" );
2838 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2839 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2840 cursor.SetVisible(false);
2845 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2847 // As cursor is not moving due to grab handle, handle should be hidden.
2848 ShowGrabHandleAndSetVisibility( false );
2850 bool cursorPositionChanged = false;
2853 if ( mCursorPosition >= places )
2855 mCursorPosition = mCursorPosition - places;
2856 cursorPositionChanged = true;
2861 if ((mCursorPosition + places) <= mStyledText.size())
2863 mCursorPosition = mCursorPosition + places;
2864 cursorPositionChanged = true;
2868 if( cursorPositionChanged )
2870 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2872 const TextStyle oldInputStyle( mInputStyle );
2873 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2877 if( oldInputStyle != mInputStyle )
2879 // Updates the line height accordingly with the input style.
2882 EmitStyleChangedSignal();
2885 ImfManager imfManager = ImfManager::Get();
2888 imfManager.SetCursorPosition ( mCursorPosition );
2889 imfManager.NotifyCursorPosition();
2894 void TextInput::DrawCursor(const std::size_t nthChar)
2896 // Get height of cursor and set its size
2897 Size size( CURSOR_THICKNESS, 0.0f );
2898 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2900 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2904 // Measure Font so know how big text will be if no initial text to measure.
2905 size.height = mLineHeight;
2908 mCursor.SetSize(size);
2910 // If the character is italic then the cursor also tilts.
2911 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2913 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2915 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2917 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2918 bool altPositionValid; // Alternate cursor validity flag.
2919 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2920 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2922 SetAltCursorEnabled( altPositionValid );
2924 if(!altPositionValid)
2926 mCursor.SetPosition( position + UI_OFFSET );
2930 size.height *= 0.5f;
2931 mCursor.SetSize(size);
2932 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2934 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2935 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2936 size.height = rowSize.height * 0.5f;
2937 mCursorRTL.SetSize(size);
2938 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2941 if( IsScrollEnabled() )
2943 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2944 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2949 void TextInput::SetAltCursorEnabled( bool enabled )
2951 mCursorRTLEnabled = enabled;
2952 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2955 void TextInput::SetCursorVisibility( bool visible )
2957 mCursorVisibility = visible;
2958 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2959 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2962 void TextInput::CreateGrabHandle( Dali::Image image )
2968 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2972 mGrabHandleImage = image;
2975 mGrabHandle = ImageActor::New(mGrabHandleImage);
2976 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2977 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2979 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2981 ShowGrabHandleAndSetVisibility( false );
2983 CreateGrabArea( mGrabHandle );
2985 mActiveLayer.Add(mGrabHandle);
2989 void TextInput::CreateGrabArea( Actor& parent )
2991 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2992 mGrabArea.SetName( "GrabArea" );
2993 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2994 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2995 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2996 mTapDetector.Attach( mGrabArea );
2997 mPanGestureDetector.Attach( mGrabArea );
2998 mLongPressDetector.Attach( mGrabArea );
3000 parent.Add(mGrabArea);
3003 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3005 Vector3 actualHandlePosition;
3009 mActualGrabHandlePosition.x += displacement.x;
3010 mActualGrabHandlePosition.y += displacement.y;
3012 // Grab handle should jump to the nearest character and take cursor with it
3013 std::size_t newCursorPosition = 0;
3014 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3016 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3018 bool handleVisible = true;
3020 if( IsScrollEnabled() )
3022 const Vector3 controlSize = GetControlSize();
3023 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3024 // Scrolls the text if the handle is not in a visible position
3025 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3032 mCurrentHandlePosition = actualHandlePosition;
3033 mScrollDisplacement = Vector2::ZERO;
3037 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3039 mScrollDisplacement.x = -SCROLL_SPEED;
3041 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3043 mScrollDisplacement.x = SCROLL_SPEED;
3045 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3047 mScrollDisplacement.y = -SCROLL_SPEED;
3049 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3051 mScrollDisplacement.y = SCROLL_SPEED;
3057 if( handleVisible && // Only redraw cursor and do updates if position changed
3058 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3060 mCursorPosition = newCursorPosition;
3062 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3064 const TextStyle oldInputStyle( mInputStyle );
3066 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3068 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3070 if( oldInputStyle != mInputStyle )
3072 // Updates the line height accordingly with the input style.
3075 EmitStyleChangedSignal();
3080 return actualHandlePosition;
3083 void TextInput::ShowGrabHandle( bool visible )
3085 if ( IsGrabHandleEnabled() )
3089 mGrabHandle.SetVisible( mGrabHandleVisibility );
3091 StartMonitoringStageForTouch();
3095 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3097 mGrabHandleVisibility = visible;
3098 ShowGrabHandle( visible );
3101 // Callbacks connected to be Property notifications for Boundary checking.
3103 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3105 mIsSelectionHandleOneFlipped = true;
3106 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3107 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3110 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3112 mIsSelectionHandleOneFlipped = false;
3113 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3114 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3117 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3119 mIsSelectionHandleTwoFlipped = true;
3120 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3121 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3124 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3126 mIsSelectionHandleTwoFlipped = false;
3127 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3128 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3131 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3132 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3134 mSelectionHandleOne.SetOpacity(0.0f);
3137 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3139 mSelectionHandleOne.SetOpacity(1.0f);
3142 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3144 mSelectionHandleTwo.SetOpacity(0.0f);
3147 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3149 mSelectionHandleTwo.SetOpacity(1.0f);
3152 // End of Callbacks connected to be Property notifications for Boundary checking.
3154 void TextInput::SetUpHandlePropertyNotifications()
3156 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3158 Vector3 handlesize = GetSelectionHandleSize();
3160 // Exceeding horizontal boundary
3161 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3162 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3164 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3165 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3167 // Within horizontal boundary
3168 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3169 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3171 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3172 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3174 // Exceeding vertical boundary
3175 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3176 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3177 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3178 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3180 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3181 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3182 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3183 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3185 // Within vertical boundary
3186 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3187 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3188 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3189 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3191 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3192 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3193 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3194 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3197 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3199 mSelectionHandleOnePosition = start;
3200 mSelectionHandleTwoPosition = end;
3202 if ( !mSelectionHandleOne )
3204 // create normal and pressed images
3205 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3206 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3208 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3209 mSelectionHandleOne.SetName("SelectionHandleOne");
3210 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3211 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3212 mIsSelectionHandleOneFlipped = false;
3213 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3215 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3216 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3218 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3219 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3221 mTapDetector.Attach( mHandleOneGrabArea );
3222 mPanGestureDetector.Attach( mHandleOneGrabArea );
3224 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3226 mSelectionHandleOne.Add( mHandleOneGrabArea );
3227 mActiveLayer.Add( mSelectionHandleOne );
3230 if ( !mSelectionHandleTwo )
3232 // create normal and pressed images
3233 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3234 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3236 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3237 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3238 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3239 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3240 mIsSelectionHandleTwoFlipped = false;
3241 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3243 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3244 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3245 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3246 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3248 mTapDetector.Attach( mHandleTwoGrabArea );
3249 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3251 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3253 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3255 mActiveLayer.Add( mSelectionHandleTwo );
3258 SetUpHandlePropertyNotifications();
3260 // update table as text may have changed.
3261 GetTextLayoutInfo();
3263 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3264 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3266 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3267 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3269 // Calculates and set the visibility if the scroll mode is enabled.
3270 bool isSelectionHandleOneVisible = true;
3271 bool isSelectionHandleTwoVisible = true;
3272 if( IsScrollEnabled() )
3274 const Vector3& controlSize( GetControlSize() );
3275 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3276 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3277 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3278 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3281 CreateHighlight(); // function will only create highlight if not already created.
3284 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3286 Vector3 actualHandlePosition;
3288 if ( mSelectionHandleOne && mSelectionHandleTwo )
3290 const Vector3& controlSize = GetControlSize();
3292 Size cursorSize( CURSOR_THICKNESS, 0.f );
3294 // Get a reference of the wanted selection handle (handle one or two).
3295 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3297 // Get a reference for the current position of the handle and a copy of its pair
3298 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3299 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3301 // Get a handle of the selection handle actor
3302 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3304 // Selection handles should jump to the nearest character
3305 std::size_t newHandlePosition = 0;
3306 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3308 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3310 bool handleVisible = true;
3312 if( IsScrollEnabled() )
3314 mCurrentSelectionId = handleId;
3316 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3317 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3318 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3325 mCurrentSelectionHandlePosition = actualHandlePosition;
3326 mScrollDisplacement = Vector2::ZERO;
3330 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3332 mScrollDisplacement.x = -SCROLL_SPEED;
3334 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3336 mScrollDisplacement.x = SCROLL_SPEED;
3338 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3340 mScrollDisplacement.y = -SCROLL_SPEED;
3342 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3344 mScrollDisplacement.y = SCROLL_SPEED;
3350 if ( handleVisible && // Ensure the handle is visible.
3351 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3352 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3354 currentSelectionHandlePosition = newHandlePosition;
3356 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3357 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3361 if ( handleId == HandleOne )
3363 const TextStyle oldInputStyle( mInputStyle );
3365 // Set Active Style to that of first character in selection
3366 if( mSelectionHandleOnePosition < mStyledText.size() )
3368 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3371 if( oldInputStyle != mInputStyle )
3373 // Updates the line height accordingly with the input style.
3376 EmitStyleChangedSignal();
3382 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3385 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3388 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3389 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3391 if ( selectionHandleActor )
3393 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3394 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3395 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3397 if( IsScrollEnabled() )
3399 const Size cursorSize( CURSOR_THICKNESS,
3400 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3401 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3403 GetControlSize() ) );
3408 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3410 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3411 // For now the visual position of end of logical string will be end of visual string.
3412 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3414 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3417 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3419 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3420 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3421 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3422 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3424 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3426 // Deselect text prior to startSelectionIt
3427 for(;it!=startSelectionIt;++it)
3429 selectedVisualText[*it] = false;
3432 // Select text from startSelectionIt -> endSelectionIt
3433 for(;it!=endSelectionIt;++it)
3435 selectedVisualText[*it] = true;
3438 // Deselect text after endSelection
3441 selectedVisualText[*it] = false;
3444 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3447 // Calculate the dimensions of the quads they will make the highlight mesh
3448 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3450 // At the moment there is no public API to modify the block alignment option.
3451 const bool blockAlignEnabled = true;
3453 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3455 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3457 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3458 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3460 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3461 std::vector<bool> selectedVisualText;
3462 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3463 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3464 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3466 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3467 float rowLeft = 0.0f;
3468 float rowRight = 0.0f;
3469 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3470 float maxRowLeft = std::numeric_limits<float>::max();
3471 float maxRowRight = 0.0f;
3473 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3475 // Scan through entire text.
3478 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3480 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3481 bool charSelected( false );
3482 if( selectedIt != selectedEndIt )
3484 charSelected = *selectedIt++;
3487 if(selectionState == SelectionNone)
3491 selectionState = SelectionStarted;
3492 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3493 rowRight = rowLeft + charInfo.mSize.width;
3496 else if(selectionState == SelectionStarted)
3498 // break selection on:
3499 // 1. new line causing selection break. (\n or wordwrap)
3500 // 2. character not selected.
3501 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3504 // finished selection.
3505 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3506 // that it resides on. That way this enumeration is not necessary.
3508 if(lastIt->mIsNewParagraphChar)
3510 // 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.
3511 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3513 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3514 maxRowLeft = std::min(maxRowLeft, min.x);
3515 maxRowRight = std::max(maxRowRight, max.x);
3516 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3517 float rowTop = rowBottom - rowSize.height;
3519 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3520 if(charSelected && blockAlignEnabled)
3522 rowRight = std::numeric_limits<float>::max();
3524 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3526 selectionState = SelectionNone;
3528 // Still selected? start a new selection
3531 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3532 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3533 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3534 selectionState = SelectionStarted;
3539 // build up highlight(s) with this selection data.
3540 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3541 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3548 // If reached end, and still on selection, then close selection.
3551 if(selectionState == SelectionStarted)
3553 // finished selection.
3555 if(lastIt->mIsNewParagraphChar)
3557 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3559 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3560 maxRowLeft = std::min(maxRowLeft, min.x);
3561 maxRowRight = std::max(maxRowRight, max.x);
3562 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3563 float rowTop = rowBottom - rowSize.height;
3564 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3568 // Get the top left and bottom right corners.
3569 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3570 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3571 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3573 // Clamp quads so they appear to clip to borders of the whole text.
3574 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3576 // For block-align align Further Clamp quads to max left and right extents
3577 if(blockAlignEnabled)
3579 // BlockAlign: Will adjust highlight to block:
3581 // H[ello] (top row right = max of all rows right)
3582 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3583 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3584 // [text] (bottom row left = min of all rows left)
3585 // (common in SMS messaging selection)
3587 // As opposed to the default which is tight text highlighting.
3592 // (common in regular text editors/web browser selection)
3594 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3597 // Finally clamp quads again so they don't exceed the boundry of the control.
3598 const Vector3& controlSize = GetControlSize();
3599 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3602 return mNewHighlightInfo;
3605 void TextInput::UpdateHighlight()
3607 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3609 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3611 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3612 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3613 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3614 // [BOTTOM] [ MIDDLE ]
3617 // Each quad is created as 2 triangles.
3618 // Middle is just 1 quad regardless of its size.
3632 if ( mHighlightMeshActor )
3634 // vertex and triangle buffers should always be present if MeshActor is alive.
3635 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3636 MeshData::VertexContainer vertices;
3637 Dali::MeshData::FaceIndices faceIndices;
3639 if( !newHighlightInfo.mQuadList.empty() )
3641 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3642 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3644 // vertex position defaults to (0 0 0)
3645 MeshData::Vertex vertex;
3646 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3649 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3651 // Add each quad geometry (a sub-selection) to the mesh data.
3661 QuadCoordinates& quad = *iter;
3663 vertex.x = quad.min.x;
3664 vertex.y = quad.min.y;
3665 vertices.push_back( vertex );
3668 vertex.x = quad.max.x;
3669 vertex.y = quad.min.y;
3670 vertices.push_back( vertex );
3672 // bottom-left (v+2)
3673 vertex.x = quad.min.x;
3674 vertex.y = quad.max.y;
3675 vertices.push_back( vertex );
3677 // bottom-right (v+3)
3678 vertex.x = quad.max.x;
3679 vertex.y = quad.max.y;
3680 vertices.push_back( vertex );
3682 // triangle A (3, 1, 0)
3683 faceIndices.push_back( v + 3 );
3684 faceIndices.push_back( v + 1 );
3685 faceIndices.push_back( v );
3687 // triangle B (0, 2, 3)
3688 faceIndices.push_back( v );
3689 faceIndices.push_back( v + 2 );
3690 faceIndices.push_back( v + 3 );
3692 mMeshData.SetFaceIndices( faceIndices );
3695 BoneContainer bones(0); // passed empty as bones not required
3696 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3697 mHighlightMesh.UpdateMeshData(mMeshData);
3702 void TextInput::ClearPopup()
3704 mPopupPanel.Clear();
3707 void TextInput::AddPopupOptions()
3709 mPopupPanel.AddPopupOptions();
3712 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3714 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3716 Vector3 clampedPosition ( position );
3717 Vector3 tailOffsetPosition ( position );
3719 float xOffSet( 0.0f );
3721 Actor self = Self();
3722 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3724 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3725 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3727 // Clamp to left or right or of boundary
3728 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3730 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3732 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3734 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3737 clampedPosition.x = position.x + xOffSet;
3738 tailOffsetPosition.x = -xOffSet;
3740 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3741 bool flipTail( false );
3743 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3745 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3749 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3750 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3753 void TextInput::HidePopup(bool animate, bool signalFinished )
3755 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3757 mPopupPanel.Hide( animate );
3759 if( animate && signalFinished )
3761 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3766 void TextInput::ShowPopup( bool animate )
3769 Vector2 alternativePopupPosition;
3771 if(mHighlightMeshActor && mState == StateEdit)
3774 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3776 // When text is selected, show popup above top handle (and text), or below bottom handle.
3777 // topHandle: referring to the top most point of the handle or the top line of selection.
3778 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3780 topHandle = mSelectionHandleOneActualPosition;
3781 bottomHandle = mSelectionHandleTwoActualPosition;
3782 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3786 topHandle = mSelectionHandleTwoActualPosition;
3787 bottomHandle = mSelectionHandleOneActualPosition;
3788 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3790 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3791 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3793 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3795 position.x = xPosition;
3797 // Alternative position if no upper space
3798 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3799 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3803 // When no text is selected, show popup at world position of grab handle or cursor
3804 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3805 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3806 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3807 // if can't be positioned above, then position below row.
3808 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3811 // If grab handle enabled then position pop-up below the grab handle.
3812 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3816 SetPopupPosition( position, alternativePopupPosition );
3819 mPopupPanel.Show( Self(), animate );
3820 StartMonitoringStageForTouch();
3822 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3825 void TextInput::ShowPopupCutCopyPaste()
3829 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3830 // Check the selected text is whole text or not.
3831 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3833 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3836 if ( !mStyledText.empty() )
3838 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3839 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3842 if( mClipboard && mClipboard.NumberOfItems() )
3844 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3845 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3850 mPopupPanel.Hide(false);
3854 void TextInput::SetUpPopupSelection()
3857 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3858 // If no text exists then don't offer to select
3859 if ( !mStyledText.empty() )
3861 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3862 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3863 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3865 // if clipboard has valid contents then offer paste option
3866 if( mClipboard && mClipboard.NumberOfItems() )
3868 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3869 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3874 mPopupPanel.Hide(false);
3877 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3882 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3883 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3884 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3885 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3887 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3889 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3891 float closestYdifference = std::numeric_limits<float>::max();
3892 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3893 std::size_t numberOfMatchedCharacters = 0;
3895 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3896 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3898 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3900 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3901 float baselinePosition = info.mPosition.y - info.mDescender;
3903 if( info.mIsVisible )
3905 // store difference between source y point and the y position of the current character
3906 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3908 if( currentYdifference < closestYdifference )
3910 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3911 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3912 closestYdifference = currentYdifference;
3913 matchedCharacters.clear();
3914 numberOfMatchedCharacters = 0; // reset count
3917 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3918 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3920 // ignore new line character.
3921 if( !info.mIsNewParagraphChar )
3923 matchedCharacters.push_back( info );
3924 numberOfMatchedCharacters++;
3928 } // End of loop checking each character's y position in the character layout table
3930 // Check if last character is a newline, if it is
3931 // then need pretend there is an imaginary line afterwards,
3932 // and check if user is touching below previous line.
3933 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3935 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3937 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3941 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3942 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3944 bool matched( false );
3946 // 2 Iterate through matching list of y positions and find closest matching X position.
3947 for( ; it != endIt; ++it )
3949 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3951 if( info.mIsVisible )
3953 // stop when on left side of character's center.
3954 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3955 if( sourceScrollOffset.x < characterMidPointPosition )
3957 if(info.mIsRightToLeftCharacter)
3959 rightToLeftChar = true;
3961 glyphIntersection = info.mPosition.x;
3966 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3972 rightToLeftChar = lastRightToLeftChar;
3975 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3976 closestIndex = lineOffset + matchCharacterIndex;
3978 mClosestCursorPositionEOL = false; // reset
3979 if ( it == endIt && !matched )
3981 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3984 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3985 if( rightToLeftChar && lastRightToLeftChar )
3987 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3992 // closestIndex is the visual index, need to convert it to the logical index
3993 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3995 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3997 // Checks for situations where user is touching between LTR and RTL
3998 // characters. To identify if the user means the end of a LTR string
3999 // or the beginning of an RTL string, and vice versa.
4000 if( closestIndex > 0 )
4002 if( rightToLeftChar && !lastRightToLeftChar )
4007 // A: In this touch range, the user is indicating that they wish to place
4008 // the cursor at the end of the LTR text.
4009 // B: In this touch range, the user is indicating that they wish to place
4010 // the cursor at the end of the RTL text.
4012 // Result of touching A area:
4013 // [.....LTR]|[RTL......]+
4015 // |: primary cursor (for typing LTR chars)
4016 // +: secondary cursor (for typing RTL chars)
4018 // Result of touching B area:
4019 // [.....LTR]+[RTL......]|
4021 // |: primary cursor (for typing RTL chars)
4022 // +: secondary cursor (for typing LTR chars)
4024 if( sourceScrollOffset.x < glyphIntersection )
4029 else if( !rightToLeftChar && lastRightToLeftChar )
4031 if( sourceScrollOffset.x < glyphIntersection )
4038 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4039 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4040 // one further ahead
4041 if( rightToLeftChar && !lastRightToLeftChar )
4046 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4048 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4050 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4059 float TextInput::GetLineJustificationPosition() const
4061 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4062 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4063 float alignmentOffset = 0.f;
4065 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4066 if( alignment & Toolkit::Alignment::HorizontalLeft )
4068 alignmentOffset = 0.f;
4070 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4072 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4074 else if( alignment & Toolkit::Alignment::HorizontalRight )
4076 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4079 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4080 float justificationOffset = 0.f;
4082 switch( justification )
4084 case Toolkit::TextView::Left:
4086 justificationOffset = 0.f;
4089 case Toolkit::TextView::Center:
4091 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4094 case Toolkit::TextView::Right:
4096 justificationOffset = mTextLayoutInfo.mTextSize.width;
4099 case Toolkit::TextView::Justified:
4101 justificationOffset = 0.f;
4106 DALI_ASSERT_ALWAYS( false );
4110 return alignmentOffset + justificationOffset;
4113 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4115 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4116 A newline character is not inserted in this case */
4118 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4120 Vector3 cursorPosition;
4122 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4124 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4126 // end character so use
4127 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4128 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4132 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4135 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4137 // If previous character on a different line then use current characters position
4138 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4140 if ( mClosestCursorPositionEOL )
4142 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4146 cursorPosition = Vector3(currentCharInfo.mPosition);
4151 // Previous character is on same line so use position of previous character plus it's width.
4152 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4155 return cursorPosition;
4158 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4160 bool direction(false);
4161 Vector3 alternatePosition;
4162 bool alternatePositionValid(false);
4164 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4167 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4169 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4171 alternatePositionValid = false;
4172 directionRTL = false;
4174 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4176 std::size_t visualCharacterPosition;
4178 // When cursor is not at beginning, consider possibility of
4179 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4180 if(characterPosition > 0)
4182 // Cursor position should be the end of the last character.
4183 // If the last character is LTR, then the end is on the right side of the glyph.
4184 // If the last character is RTL, then the end is on the left side of the glyph.
4185 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4187 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4189 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4192 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4193 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4195 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4196 const Vector3& size = GetControlSize();
4198 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4200 --visualCharacterPosition;
4202 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4205 if(!info.mIsNewParagraphChar)
4207 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4211 // When cursor points to first character on new line, position cursor at the start of this glyph.
4212 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4214 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4215 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4216 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4218 cursorPosition.x = infoNext.mPosition.x + start;
4219 cursorPosition.y = infoNext.mPosition.y;
4223 // If cursor points to the end of text, then can only position
4224 // cursor where the new line starts based on the line-justification position.
4225 cursorPosition.x = GetLineJustificationPosition();
4227 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4229 // If this is after the last character, then we can assume that the new cursor
4230 // should be exactly one row below the current row.
4232 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4233 cursorPosition.y = info.mPosition.y + rowRect.height;
4237 // If this is not after last character, then we can use this row's height.
4238 // should be exactly one row below the current row.
4240 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4241 cursorPosition.y = info.mPosition.y + rowRect.height;
4246 directionRTL = info.mIsRightToLeftCharacter;
4248 // 1. When the cursor is neither at the beginning or the end,
4249 // we can show multiple cursors under situations when the cursor is
4250 // between RTL and LTR text...
4251 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4253 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4255 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4256 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4258 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4260 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4261 // Text: [...LTR...]|[...RTL...]
4263 // Alternate cursor pos: ^
4264 // In which case we need to display an alternate cursor for the RTL text.
4266 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4267 alternatePosition.y = infoAlt.mPosition.y;
4268 alternatePositionValid = true;
4270 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4272 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4273 // Text: |[...RTL...] [...LTR....]
4275 // Alternate cursor pos: ^
4276 // In which case we need to display an alternate cursor for the RTL text.
4278 alternatePosition.x = infoAlt.mPosition.x;
4279 alternatePosition.y = infoAlt.mPosition.y;
4280 alternatePositionValid = true;
4285 // 2. When the cursor is at the end of the text,
4286 // and we have multi-directional text,
4287 // we can also consider showing mulitple cursors.
4288 // The rule here is:
4289 // If first and last characters on row are different
4290 // Directions, then two cursors need to be displayed.
4292 // Get first logical glyph on row
4293 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4295 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4296 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4298 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4300 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4301 // Text: [...LTR...]|[...RTL...]
4303 // Alternate cursor pos: ^
4304 // In which case we need to display an alternate cursor for the RTL text, this cursor
4305 // should be at the end of the given line.
4307 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4308 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4309 alternatePosition.y = infoAlt.mPosition.y;
4310 alternatePositionValid = true;
4312 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4314 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4315 // Text: |[...RTL...] [...LTR....]
4317 // Alternate cursor pos: ^
4318 // In which case we need to display an alternate cursor for the RTL text.
4320 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4321 alternatePosition.x = infoAlt.mPosition.x;
4322 alternatePosition.y = infoAlt.mPosition.y;
4323 alternatePositionValid = true;
4326 } // characterPosition > 0
4327 else if(characterPosition == 0)
4329 // When the cursor position is at the beginning, it should be at the start of the current character.
4330 // If the current character is LTR, then the start is on the right side of the glyph.
4331 // If the current character is RTL, then the start is on the left side of the glyph.
4332 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4334 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4336 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4339 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4340 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4342 cursorPosition.x = info.mPosition.x + start;
4343 cursorPosition.y = info.mPosition.y;
4344 directionRTL = info.mIsRightToLeftCharacter;
4349 // If the character table is void, place the cursor accordingly the text alignment.
4350 const Vector3& size = GetControlSize();
4352 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4353 float alignmentOffset = 0.f;
4355 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4356 if( alignment & Toolkit::Alignment::HorizontalLeft )
4358 alignmentOffset = 0.f;
4360 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4362 alignmentOffset = 0.5f * ( size.width );
4364 else if( alignment & Toolkit::Alignment::HorizontalRight )
4366 alignmentOffset = size.width;
4369 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4370 cursorPosition.x = alignmentOffset;
4372 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4373 if( alignment & Toolkit::Alignment::VerticalTop )
4375 cursorPosition.y = mLineHeight;
4377 else if( alignment & Toolkit::Alignment::VerticalCenter )
4379 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4381 else if( alignment & Toolkit::Alignment::VerticalBottom )
4383 cursorPosition.y = size.height;
4387 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4388 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4389 if( alternatePositionValid )
4391 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4392 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4395 return cursorPosition;
4398 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4400 // scan string from current position to beginning of current line to note direction of line
4401 while(logicalPosition)
4404 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4405 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4412 return logicalPosition;
4415 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4419 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4422 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4424 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4425 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4427 min = Vector2::ZERO;
4428 max = Vector2(0.0f, mLineHeight);
4432 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4433 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4434 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4436 // If cursor is pointing to end of line, then start from last character.
4437 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4439 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4441 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4442 if( !it->mIsVisible )
4444 characterPosition = FindVisibleCharacter( Left, characterPosition );
4445 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4448 // Scan characters left and right of cursor, stopping when end of line/string reached or
4449 // y position greater than threshold of reference line.
4451 // 1. scan left until we reach the beginning or a different line.
4452 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4453 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4454 // min-x position is the left-most char's left (x)
4455 // max-x position is the right-most char's right (x)
4456 // min-y position is the minimum of all character's top (y)
4457 // max-y position is the maximum of all character's bottom (y+height)
4458 min.y = validCharIt->mPosition.y;
4459 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4464 min.y = std::min(min.y, validCharIt->mPosition.y);
4465 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4474 if( (it->mPosition.y < referenceLine) ||
4475 (it->mIsNewParagraphChar) ||
4482 // info refers to the first character on this line.
4483 min.x = validCharIt->mPosition.x;
4485 // 2. scan right until we reach end or a different line
4486 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4487 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4491 if( (it->mPosition.y > referenceLine) ||
4492 (it->mIsNewParagraphChar) ||
4499 min.y = std::min(min.y, validCharIt->mPosition.y);
4500 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4505 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4507 if ( validCharIt != end )
4509 // info refers to the last character on this line.
4510 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4513 return Size( max.x - min.x, max.y - min.y );
4516 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4518 Actor popUpPanel = mPopupPanel.GetRootActor();
4520 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4526 Dali::Actor parent( touchedActor.GetParent() );
4530 return WasTouchedCheck( parent );
4537 void TextInput::StartMonitoringStageForTouch()
4539 Stage stage = Stage::GetCurrent();
4540 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4543 void TextInput::EndMonitoringStageForTouch()
4545 Stage stage = Stage::GetCurrent();
4546 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4549 void TextInput::OnStageTouched(const TouchEvent& event)
4551 if( event.GetPointCount() > 0 )
4553 if ( TouchPoint::Down == event.GetPoint(0).state )
4555 const Actor touchedActor(event.GetPoint(0).hitActor);
4557 bool popUpShown( false );
4559 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4564 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4566 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4568 EndMonitoringStageForTouch();
4569 HidePopup( true, false );
4572 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4574 EndMonitoringStageForTouch();
4575 ShowGrabHandleAndSetVisibility( false );
4581 void TextInput::SelectText(std::size_t start, std::size_t end)
4583 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4584 IsGrabHandleEnabled()?"true":"false",
4585 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4586 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4587 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4589 StartMonitoringStageForTouch();
4591 if ( mEditModeActive ) // Only allow text selection when in edit mode
4593 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4594 mSelectingText = true;
4596 std::size_t selectionStartPosition = std::min( start, end );
4598 // Hide grab handle when selecting.
4599 ShowGrabHandleAndSetVisibility( false );
4601 if( start != end ) // something to select
4603 SetCursorVisibility( false );
4604 StopCursorBlinkTimer();
4606 CreateSelectionHandles(start, end);
4609 const TextStyle oldInputStyle( mInputStyle );
4610 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4612 if( oldInputStyle != mInputStyle )
4614 // Updates the line height accordingly with the input style.
4617 EmitStyleChangedSignal();
4623 mSelectingText = false;
4627 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4629 MarkupProcessor::StyledTextArray currentSelectedText;
4631 if ( IsTextSelected() )
4633 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4634 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4636 for(; it != end; ++it)
4638 MarkupProcessor::StyledText& styledText( *it );
4639 currentSelectedText.push_back( styledText );
4642 return currentSelectedText;
4645 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4647 const std::size_t beginIndex = std::min( begin, end );
4648 const std::size_t endIndex = std::max( begin, end );
4651 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4653 // Create a styled text array used to replace the text into the text-view.
4654 MarkupProcessor::StyledTextArray text;
4655 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4657 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4658 GetTextLayoutInfo();
4660 if( IsScrollEnabled() )
4662 // Need to set the scroll position as the text's size may have changed.
4663 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4666 ShowGrabHandleAndSetVisibility( false );
4672 // Set Handle positioning as the new style may have repositioned the characters.
4673 SetSelectionHandlePosition(HandleOne);
4674 SetSelectionHandlePosition(HandleTwo);
4677 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4679 // Just hide the grab handle when keyboard is hidden.
4680 if (!keyboardShown )
4682 ShowGrabHandleAndSetVisibility( false );
4684 // If the keyboard is not now being shown, then hide the popup panel
4685 mPopupPanel.Hide( true );
4689 // Removes highlight and resumes edit mode state
4690 void TextInput::RemoveHighlight()
4692 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4694 if ( mHighlightMeshActor )
4696 if ( mSelectionHandleOne )
4698 mActiveLayer.Remove( mSelectionHandleOne );
4699 mSelectionHandleOne.Reset();
4700 mSelectionHandleOneOffset.x = 0.0f;
4702 if ( mSelectionHandleTwo )
4704 mActiveLayer.Remove( mSelectionHandleTwo );
4705 mSelectionHandleTwo.Reset();
4706 mSelectionHandleTwoOffset.x = 0.0f;
4709 mNewHighlightInfo.mQuadList.clear();
4711 Self().Remove( mHighlightMeshActor );
4713 SetCursorVisibility( true );
4714 StartCursorBlinkTimer();
4716 mHighlightMeshActor.Reset();
4717 // NOTE: We cannot dereference mHighlightMesh, due
4718 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4723 mSelectionHandleOnePosition = 0;
4724 mSelectionHandleTwoPosition = 0;
4727 void TextInput::CreateHighlight()
4729 if ( !mHighlightMeshActor )
4731 mMeshData = MeshData( );
4732 mMeshData.SetHasNormals( true );
4734 mCustomMaterial = Material::New("CustomMaterial");
4735 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4737 mMeshData.SetMaterial( mCustomMaterial );
4739 mHighlightMesh = Mesh::New( mMeshData );
4741 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4742 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4743 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4744 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4745 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4746 mHighlightMeshActor.SetAffectedByLighting(false);
4748 Self().Add(mHighlightMeshActor);
4753 bool TextInput::CopySelectedTextToClipboard()
4755 mCurrentCopySelecton.clear();
4757 mCurrentCopySelecton = GetSelectedText();
4759 std::string stringToStore;
4761 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4762 * a marked up string.
4764 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4765 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4766 bool success = mClipboard.SetItem( stringToStore );
4770 void TextInput::PasteText( const Text& text )
4772 // Update Flag, indicates whether to update the text-input contents or not.
4773 // Any key stroke that results in a visual change of the text-input should
4774 // set this flag to true.
4775 bool update = false;
4776 if( mHighlightMeshActor )
4778 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4779 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4781 ImfManager imfManager = ImfManager::Get();
4784 imfManager.SetCursorPosition( mCursorPosition );
4785 imfManager.NotifyCursorPosition();
4787 DeleteHighlightedText( true );
4791 bool textExceedsMaximunNumberOfCharacters = false;
4792 bool textExceedsBoundary = false;
4794 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4796 mCursorPosition += insertedStringLength;
4797 ImfManager imfManager = ImfManager::Get();
4800 imfManager.SetCursorPosition ( mCursorPosition );
4801 imfManager.NotifyCursorPosition();
4804 update = update || ( insertedStringLength > 0 );
4811 if( insertedStringLength < text.GetLength() )
4813 EmitMaxInputCharactersReachedSignal();
4816 if( textExceedsBoundary )
4818 EmitInputTextExceedsBoundariesSignal();
4822 void TextInput::SetTextDirection()
4824 // Put the cursor to the right if we are empty and an RTL language is being used.
4825 if ( mStyledText.empty() )
4827 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4829 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4830 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4832 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4833 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4835 int alignment( mDisplayedTextView.GetTextAlignment() &
4836 ( Toolkit::Alignment::VerticalTop |
4837 Toolkit::Alignment::VerticalCenter |
4838 Toolkit::Alignment::VerticalBottom |
4839 Toolkit::Alignment::HorizontalCenter ) );
4840 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4842 // If our alignment is in the center, then do not change.
4843 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4845 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4848 // If our justification is in the center, then do not change.
4849 if ( justification != Toolkit::TextView::Center )
4851 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4854 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4855 mDisplayedTextView.SetLineJustification( justification );
4859 void TextInput::UpdateLineHeight()
4861 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4862 mLineHeight = font.GetLineHeight();
4864 // 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.
4866 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4868 if( !mExceedEnabled || shrink )
4870 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4874 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4876 std::size_t position = 0;
4878 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4884 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4886 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4888 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4894 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4895 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4897 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4903 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4908 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4915 void TextInput::SetSortModifier( float depthOffset )
4917 if(mDisplayedTextView)
4919 mDisplayedTextView.SetSortModifier(depthOffset);
4923 void TextInput::SetSnapshotModeEnabled( bool enable )
4925 if(mDisplayedTextView)
4927 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4931 bool TextInput::IsSnapshotModeEnabled() const
4933 bool snapshotEnabled = false;
4935 if(mDisplayedTextView)
4937 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4940 return snapshotEnabled;
4943 void TextInput::SetMarkupProcessingEnabled( bool enable )
4945 mMarkUpEnabled = enable;
4948 bool TextInput::IsMarkupProcessingEnabled() const
4950 return mMarkUpEnabled;
4953 void TextInput::SetScrollEnabled( bool enable )
4955 if( mDisplayedTextView )
4957 mDisplayedTextView.SetScrollEnabled( enable );
4962 // Don't set cursor's and handle's visibility to false if they are outside the
4963 // boundaries of the text-input.
4964 mIsCursorInScrollArea = true;
4965 mIsGrabHandleInScrollArea = true;
4966 if( mSelectionHandleOne && mSelectionHandleTwo )
4968 mSelectionHandleOne.SetVisible( true );
4969 mSelectionHandleTwo.SetVisible( true );
4971 if( mHighlightMeshActor )
4973 mHighlightMeshActor.SetVisible( true );
4979 bool TextInput::IsScrollEnabled() const
4981 bool scrollEnabled = false;
4983 if( mDisplayedTextView )
4985 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4988 return scrollEnabled;
4991 void TextInput::SetScrollPosition( const Vector2& position )
4993 if( mDisplayedTextView )
4995 mDisplayedTextView.SetScrollPosition( position );
4999 Vector2 TextInput::GetScrollPosition() const
5001 Vector2 scrollPosition;
5003 if( mDisplayedTextView )
5005 scrollPosition = mDisplayedTextView.GetScrollPosition();
5008 return scrollPosition;
5011 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5013 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5014 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5015 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5017 // Add style to the new input text.
5018 MarkupProcessor::StyledTextArray textToInsert;
5019 for( std::size_t i = 0; i < insertedStringLength; ++i )
5021 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5022 textToInsert.push_back( newStyledCharacter );
5025 //Insert text to the TextView.
5026 const bool emptyTextView = mStyledText.empty();
5027 if( emptyTextView && mPlaceHolderSet )
5029 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5030 mDisplayedTextView.SetText( textToInsert );
5034 if( 0 == numberOfCharactersToReplace )
5036 mDisplayedTextView.InsertTextAt( position, textToInsert );
5040 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5043 mPlaceHolderSet = false;
5045 if( textToInsert.empty() )
5047 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5048 GetTextLayoutInfo();
5052 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5053 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5056 textExceedsBoundary = false;
5058 if( !mExceedEnabled )
5060 const Vector3& size = GetControlSize();
5062 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5064 // If new text does not fit within TextView
5065 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5066 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5067 GetTextLayoutInfo();
5068 textExceedsBoundary = true;
5069 insertedStringLength = 0;
5072 if( textExceedsBoundary )
5074 // Add the part of the text which fits on the text-input.
5076 // Split the text which doesn't fit in two halves.
5077 MarkupProcessor::StyledTextArray firstHalf;
5078 MarkupProcessor::StyledTextArray secondHalf;
5079 SplitText( textToInsert, firstHalf, secondHalf );
5081 // Clear text. This text will be filled with the text inserted.
5082 textToInsert.clear();
5084 // Where to insert the text.
5085 std::size_t positionToInsert = position;
5087 bool end = text.GetLength() <= 1;
5090 // Insert text and check ...
5091 const std::size_t textLength = firstHalf.size();
5092 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5093 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5095 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5097 // Inserted text doesn't fit.
5099 // Remove inserted text
5100 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5101 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5103 // The iteration finishes when only one character doesn't fit.
5104 end = textLength <= 1;
5108 // Prepare next two halves for next iteration.
5109 MarkupProcessor::StyledTextArray copyText = firstHalf;
5110 SplitText( copyText, firstHalf, secondHalf );
5117 // store text to be inserted in mStyledText.
5118 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5120 // Increase the inserted characters counter.
5121 insertedStringLength += textLength;
5123 // Prepare next two halves for next iteration.
5124 MarkupProcessor::StyledTextArray copyText = secondHalf;
5125 SplitText( copyText, firstHalf, secondHalf );
5127 // Update where next text has to be inserted
5128 positionToInsert += textLength;
5134 if( textToInsert.empty() && emptyTextView )
5136 // No character has been added and the text-view was empty.
5137 // Set the placeholder text.
5138 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5139 mPlaceHolderSet = true;
5143 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5144 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5145 mPlaceHolderSet = false;
5148 return insertedStringLength;
5151 void TextInput::GetTextLayoutInfo()
5153 if( mStyledText.empty() )
5155 // The text-input has no text, clear the text-view's layout info.
5156 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5160 if( mDisplayedTextView )
5162 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5166 // There is no text-view.
5167 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5172 void TextInput::SetOffsetFromText( const Vector4& offset )
5174 mPopupOffsetFromText = offset;
5177 const Vector4& TextInput::GetOffsetFromText() const
5179 return mPopupOffsetFromText;
5182 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5184 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5188 TextInput& textInputImpl( GetImpl( textInput ) );
5190 switch ( propertyIndex )
5192 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5194 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5197 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5199 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5202 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5204 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5207 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5209 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5212 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5214 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5217 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5219 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5222 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5224 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5227 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5229 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5232 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5234 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5237 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5239 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5242 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5244 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5247 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5249 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5252 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5254 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5257 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5259 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5262 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5264 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5267 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5269 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5275 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5277 Property::Value value;
5279 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5283 TextInput& textInputImpl( GetImpl( textInput ) );
5285 switch ( propertyIndex )
5287 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5289 value = textInputImpl.GetMaterialDiffuseColor();
5292 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5294 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5297 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5299 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5302 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5304 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5307 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5309 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5312 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5314 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5317 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5319 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5322 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5324 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5327 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5329 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5332 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5334 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5337 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5339 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5342 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5344 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5347 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5349 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5352 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5354 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5357 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5359 value = textInputImpl.GetOffsetFromText();
5362 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5364 value = textInputImpl.mCursor.GetCurrentColor();
5371 void TextInput::EmitStyleChangedSignal()
5373 // emit signal if input style changes.
5374 Toolkit::TextInput handle( GetOwner() );
5375 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5378 void TextInput::EmitTextModified()
5380 // emit signal when text changes.
5381 Toolkit::TextInput handle( GetOwner() );
5382 mTextModifiedSignal.Emit( handle );
5386 void TextInput::EmitMaxInputCharactersReachedSignal()
5388 // emit signal if max characters is reached during text input.
5389 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5391 Toolkit::TextInput handle( GetOwner() );
5392 mMaxInputCharactersReachedSignalV2.Emit( handle );
5395 void TextInput::EmitInputTextExceedsBoundariesSignal()
5397 // Emit a signal when the input text exceeds the boundaries of the text input.
5399 Toolkit::TextInput handle( GetOwner() );
5400 mInputTextExceedBoundariesSignalV2.Emit( handle );
5403 } // namespace Internal
5405 } // namespace Toolkit