2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 #include <dali/integration-api/debug.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
56 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
71 const std::string NEWLINE( "\n" );
73 const TextStyle DEFAULT_TEXT_STYLE;
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
80 * Selection state enumeration (FSM)
84 SelectionNone, ///< Currently not encountered selected section.
85 SelectionStarted, ///< Encountered selected section
86 SelectionFinished ///< Finished selected section
90 * Whether the given style is the default style or not.
91 * @param[in] style The given style.
92 * @return \e true if the given style is the default. Otherwise it returns \e false.
94 bool IsDefaultStyle( const TextStyle& style )
96 return DEFAULT_TEXT_STYLE == style;
100 * Whether the given styled text is using the default style or not.
101 * @param[in] textArray The given text.
102 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108 const TextStyle& style( (*it).mStyle );
110 if( !IsDefaultStyle( style ) )
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
125 if( ( *it ).mIsVisible )
127 return --cursorPosition;
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
138 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140 if( ( *it ).mIsVisible )
142 return cursorPosition;
148 return cursorPosition;
152 * Whether the given position plus the cursor size offset is inside the given boundary.
154 * @param[in] position The given position.
155 * @param[in] cursorSize The cursor size.
156 * @param[in] controlSize The given boundary.
158 * @return whether the given position is inside the given boundary.
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
169 * Splits a text in two halves.
171 * If the text's number of characters is odd, firstHalf has one more character.
173 * @param[in] text The text to be split.
174 * @param[out] firstHalf The first half of the text.
175 * @param[out] secondHalf The second half of the text.
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
184 const std::size_t textLength = text.size();
185 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
191 } // end of namespace
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
261 QuadCoordinates quad(x1, y1, x2, y2);
262 mQuadList.push_back( quad );
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
267 for(std::size_t i = 0;i < mQuadList.size(); i++)
269 QuadCoordinates& quad = mQuadList[i];
271 quad.min.Clamp(min, max);
272 quad.max.Clamp(min, max);
276 // [TextInput] ////////////////////////////////////////////////////////////////
278 Dali::Toolkit::TextInput TextInput::New()
280 // Create the implementation
281 TextInputPtr textInput(new TextInput());
282 // Pass ownership to CustomActor via derived handle
283 Dali::Toolkit::TextInput handle(*textInput);
284 handle.SetName( "TextInput");
286 textInput->Initialize();
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
296 mDisplayedTextView(),
297 mStyledPlaceHolderText(),
298 mMaxStringLength( DEFAULT_MAX_SIZE ),
299 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300 mCursorPosition( 0 ),
301 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302 mIsSelectionHandleOneFlipped( false ),
303 mIsSelectionHandleTwoFlipped( false ),
304 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308 mSelectionHandleOnePosition( 0 ),
309 mSelectionHandleTwoPosition( 0 ),
311 mPreEditStartPosition( 0 ),
312 mPreEditLength ( 0 ),
313 mNumberOfSurroundingCharactersDeleted( 0 ),
314 mTouchStartTime( 0 ),
316 mCurrentCopySelecton(),
319 mScrollDisplacement(),
320 mCurrentHandlePosition(),
321 mCurrentSelectionId(),
322 mCurrentSelectionHandlePosition(),
323 mRequestedSelection( 0, 0 ),
324 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
327 mMaterialColor( LIGHTBLUE ),
328 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329 mOverrideAutomaticAlignment( false ),
330 mCursorRTLEnabled( false ),
331 mClosestCursorPositionEOL ( false ),
332 mCursorBlinkStatus( true ),
333 mCursorVisibility( false ),
334 mGrabHandleVisibility( false ),
335 mIsCursorInScrollArea( true ),
336 mIsGrabHandleInScrollArea( true ),
337 mEditModeActive( false ),
338 mEditOnTouch( true ),
339 mTextSelection( true ),
340 mExceedEnabled( true ),
341 mGrabHandleEnabled( true ),
342 mIsSelectionHandleFlipEnabled( true ),
343 mPreEditFlag( false ),
344 mIgnoreCommitFlag( false ),
345 mIgnoreFirstCommitFlag( false ),
346 mSelectingText( false ),
347 mPreserveCursorPosition( false ),
348 mSelectTextOnCommit( false ),
349 mUnderlinedPriorToPreEdit ( false ),
350 mCommitByKeyInput( false ),
351 mPlaceHolderSet( false ),
352 mMarkUpEnabled( false )
354 // Updates the line height accordingly with the input style.
358 TextInput::~TextInput()
360 StopCursorBlinkTimer();
365 std::string TextInput::GetText() const
369 // Return text-view's text only if the text-input's text is not empty
370 // in order to not to return the placeholder text.
371 if( !mStyledText.empty() )
373 text = mDisplayedTextView.GetText();
379 std::string TextInput::GetMarkupText() const
381 std::string markupString;
382 MarkupProcessor::GetMarkupString( mStyledText, markupString );
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
389 // Get the placeholder styled text array from the markup string.
390 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
392 if( mStyledText.empty() )
394 // Set the placeholder text only if the styled text is empty.
395 mDisplayedTextView.SetText( mStyledPlaceHolderText );
396 mPlaceHolderSet = true;
400 std::string TextInput::GetPlaceholderText()
402 // Traverses the styled placeholder array getting only the text.
403 // Note that for some languages a 'character' could be represented by more than one 'char'
405 std::string placeholderText;
406 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
408 placeholderText.append( (*it).mText.GetText() );
411 return placeholderText ;
414 void TextInput::SetInitialText(const std::string& initialText)
416 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
418 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
420 mPreEditFlag = false;
421 mIgnoreCommitFlag = true;
424 SetText( initialText );
425 PreEditReset( false ); // Reset keyboard as text changed
428 void TextInput::SetText(const std::string& initialText)
430 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
432 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
434 if( mStyledText.empty() )
436 // If the initial text is empty, set the placeholder text.
437 mDisplayedTextView.SetText( mStyledPlaceHolderText );
438 mPlaceHolderSet = true;
442 mDisplayedTextView.SetText( mStyledText );
443 mPlaceHolderSet = false;
448 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
450 ImfManager imfManager = ImfManager::Get();
453 imfManager.SetCursorPosition( mCursorPosition );
454 imfManager.SetSurroundingText( initialText );
455 imfManager.NotifyCursorPosition();
458 if( IsScrollEnabled() )
460 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
463 ShowGrabHandleAndSetVisibility( false );
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
474 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
476 mDisplayedTextView.SetText( styleText );
477 mPlaceHolderSet = false;
479 // If text alignment hasn't been manually set by application developer, then we
480 // automatically determine the alignment based on the content of the text i.e. what
481 // language the text begins with.
482 // TODO: This should determine different alignments for each line (broken by '\n') of text.
483 if(!mOverrideAutomaticAlignment)
485 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486 bool leftToRight(true);
488 if( !styleText.empty() )
490 bool breakOut(false);
492 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
494 const Text& text = textIter->mText;
496 for( std::size_t i = 0; i < text.GetLength(); ++i )
498 Character character( text[i] );
499 if( character.GetCharacterDirection() != Character::Neutral )
501 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
509 // Based on this direction, either left or right align text if not manually set by application developer.
510 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512 Toolkit::Alignment::VerticalTop ) );
513 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
521 mMaxStringLength = maxChars;
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
526 DALI_ASSERT_DEBUG( maxLines > 0 )
530 mNumberOflinesLimit = maxLines;
534 std::size_t TextInput::GetNumberOfLinesLimit() const
536 return mNumberOflinesLimit;
539 std::size_t TextInput::GetNumberOfCharacters() const
541 return mStyledText.size();
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
547 mMaterialColor = color;
548 if ( mCustomMaterial )
550 mCustomMaterial.SetDiffuseColor( mMaterialColor );
551 mMeshData.SetMaterial( mCustomMaterial );
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
557 return mMaterialColor;
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
564 return mInputStartedSignalV2;
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
569 return mInputFinishedSignalV2;
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
574 return mCutAndPasteToolBarDisplayedV2;
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
579 return mStyleChangedSignalV2;
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
584 return mTextModifiedSignal;
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
589 return mMaxInputCharactersReachedSignalV2;
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
594 return mInputTextExceedBoundariesSignalV2;
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
599 Dali::BaseHandle handle( object );
601 bool connected( true );
602 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
604 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
606 textInput.InputStartedSignal().Connect( tracker, functor );
608 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
610 textInput.InputFinishedSignal().Connect( tracker, functor );
612 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
614 textInput.StyleChangedSignal().Connect( tracker, functor );
616 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
618 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
620 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
622 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
626 // signalName does not match any signal
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
637 // update line height before calculate the actual position.
642 if( setCursorOnTouchPoint )
644 // Sets the cursor position for the given touch point.
645 ReturnClosestIndex( touchPoint, mCursorPosition );
647 // Creates the grab handle.
648 if( IsGrabHandleEnabled() )
650 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
654 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657 ShowGrabHandleAndSetVisibility( true );
659 // Scrolls the text-view if needed.
660 if( IsScrollEnabled() )
662 ScrollTextViewToMakeCursorVisible( cursorPosition );
668 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
680 bool TextInput::IsEditable() const
682 return mEditModeActive;
685 void TextInput::SetEditOnTouch( bool editOnTouch )
687 mEditOnTouch = editOnTouch;
690 bool TextInput::IsEditOnTouch() const
695 void TextInput::SetTextSelectable( bool textSelectable )
697 mTextSelection = textSelectable;
700 bool TextInput::IsTextSelectable() const
702 return mTextSelection;
705 bool TextInput::IsTextSelected() const
707 return mHighlightMeshActor;
710 void TextInput::DeSelectText()
717 void TextInput::SetGrabHandleImage(Dali::Image image )
721 CreateGrabHandle(image);
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
727 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
731 mCursor.SetImage( image );
732 mCursor.SetNinePatchBorder( border );
736 Vector3 TextInput::GetSelectionHandleSize()
738 return DEFAULT_SELECTION_HANDLE_SIZE;
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
743 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
747 mCursorRTL.SetImage( image);
748 mCursorRTL.SetNinePatchBorder( border );
752 void TextInput::EnableGrabHandle(bool toggle)
754 // enables grab handle with will in turn de-activate magnifier
755 mGrabHandleEnabled = toggle;
758 bool TextInput::IsGrabHandleEnabled()
760 // if false then magnifier will be shown instead.
761 return mGrabHandleEnabled;
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
766 // Deprecated function. To be removed.
767 mIsSelectionHandleFlipEnabled = toggle;
770 bool TextInput::IsSelectionHandleFlipEnabled()
772 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
778 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
782 mSelectionHandleFlipMargin = margin;
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
787 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
793 const Vector4 boundary( originX,
795 originX + boundingRectangle.width,
796 originY + boundingRectangle.height );
798 mBoundingRectangleWorldCoordinates = boundary;
801 const Rect<float> TextInput::GetBoundingRectangle() const
803 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
805 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
808 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
815 return mSelectionHandleFlipMargin;
818 void TextInput::SetTextColor( const Vector4& color )
820 mDisplayedTextView.SetColor( color );
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
825 if( style != mInputStyle )
828 bool emitSignal = false;
830 // mask: modify style according to mask, if different emit signal.
831 const TextStyle oldInputStyle( mInputStyle );
833 // Copy the new style.
834 mInputStyle.Copy( style, mask );
836 // if style has changed, emit signal.
837 if( oldInputStyle != mInputStyle )
842 // Updates the line height accordingly with the input style.
845 // Changing font point size will require the cursor to be re-sized
850 EmitStyleChangedSignal();
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
857 if ( IsTextSelected() )
859 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
862 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
864 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
867 // Keeps the old style to be compared with the new one.
868 const TextStyle oldInputStyle( mInputStyle );
870 // Copy only those parameters from the style which are set in the mask.
871 mInputStyle.Copy( style, mask );
873 if( mInputStyle != oldInputStyle )
875 // Updates the line height accordingly with the input style.
878 EmitStyleChangedSignal();
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
885 if( !mStyledText.empty() )
887 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
891 TextStyle TextInput::GetStyleAtCursor() const
895 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
897 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898 style = mStyledText.at( mCursorPosition-1 ).mStyle;
904 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
906 Dali::Font defaultFont = Dali::Font::New();
907 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
916 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
918 if( position >= mStyledText.size() )
920 position = mStyledText.size() - 1;
923 return mStyledText.at( position ).mStyle;
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
928 mDisplayedTextView.SetTextAlignment( align );
929 mOverrideAutomaticAlignment = true;
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
934 mDisplayedTextView.SetLineJustification( justification );
935 mOverrideAutomaticAlignment = true;
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
940 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
945 return mDisplayedTextView.GetFadeBoundary();
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
950 return mDisplayedTextView.GetTextAlignment();
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
955 mDisplayedTextView.SetMultilinePolicy( policy );
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
960 return mDisplayedTextView.GetMultilinePolicy();
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
965 mDisplayedTextView.SetWidthExceedPolicy( policy );
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
970 return mDisplayedTextView.GetWidthExceedPolicy();
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
975 mDisplayedTextView.SetHeightExceedPolicy( policy );
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
980 return mDisplayedTextView.GetHeightExceedPolicy();
983 void TextInput::SetExceedEnabled( bool enable )
985 mExceedEnabled = enable;
988 bool TextInput::GetExceedEnabled() const
990 return mExceedEnabled;
993 void TextInput::SetBackground(Dali::Image image )
995 // TODO Should add this function and add public api to match.
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1005 switch( event.state )
1007 case KeyEvent::Down:
1009 return OnKeyDownEvent(event);
1015 return OnKeyUpEvent(event);
1027 void TextInput::OnKeyInputFocusGained()
1029 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1031 mEditModeActive = true;
1033 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1035 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1037 // Updates the line height accordingly with the input style.
1040 // Connect the signals to use in text input.
1041 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1044 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1047 GetTextLayoutInfo();
1050 SetCursorVisibility( true );
1051 StartCursorBlinkTimer();
1053 Toolkit::TextInput handle( GetOwner() );
1054 mInputStartedSignalV2.Emit( handle );
1056 ImfManager imfManager = ImfManager::Get();
1060 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1062 // Notify that the text editing start.
1063 imfManager.Activate();
1065 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066 imfManager.SetRestoreAferFocusLost( true );
1068 imfManager.SetCursorPosition( mCursorPosition );
1069 imfManager.NotifyCursorPosition();
1072 mClipboard = Clipboard::Get(); // Store handle to clipboard
1074 // Now in edit mode we can accept string to paste from clipboard
1075 if( Adaptor::IsAvailable() )
1077 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1080 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1085 void TextInput::OnKeyInputFocusLost()
1087 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1091 // If key input focus is lost, it removes the
1092 // underline from the last pre-edit text.
1093 RemovePreEditStyle();
1094 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1095 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1099 ImfManager imfManager = ImfManager::Get();
1102 // The text editing is finished. Therefore the imf manager don't have restore activation.
1103 imfManager.SetRestoreAferFocusLost( false );
1105 // Notify that the text editing finish.
1106 imfManager.Deactivate();
1108 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1110 // Disconnect signal used the text input.
1111 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1113 Toolkit::TextInput handle( GetOwner() );
1114 mInputFinishedSignalV2.Emit( handle );
1115 mEditModeActive = false;
1116 mPreEditFlag = false;
1118 SetCursorVisibility( false );
1119 StopCursorBlinkTimer();
1121 ShowGrabHandleAndSetVisibility( false );
1124 // No longer in edit mode so do not want to receive string from clipboard
1125 if( Adaptor::IsAvailable() )
1127 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1130 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1132 Clipboard clipboard = Clipboard::Get();
1136 clipboard.HideClipboard();
1141 void TextInput::OnControlStageConnection()
1143 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1145 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1147 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1151 void TextInput::CreateActiveLayer()
1153 Actor self = Self();
1154 mActiveLayer = Layer::New();
1155 mActiveLayer.SetName ( "ActiveLayerActor" );
1157 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1158 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1159 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1161 self.Add( mActiveLayer );
1162 mActiveLayer.RaiseToTop();
1165 void TextInput::OnInitialize()
1167 CreateTextViewActor();
1171 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1172 // different positions depending on language)
1173 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1174 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1176 Actor self = Self();
1177 self.Add( mCursor );
1178 self.Add( mCursorRTL );
1180 mCursorVisibility = false;
1182 CreateActiveLayer(); // todo move this so layer only created when needed.
1184 // Assign names to image actors
1185 mCursor.SetName("mainCursor");
1186 mCursorRTL.SetName("rtlCursor");
1189 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1191 mDisplayedTextView.SetSize( targetSize );
1192 GetTextLayoutInfo();
1193 mActiveLayer.SetSize(targetSize);
1196 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1198 Relayout( mDisplayedTextView, size, container );
1199 Relayout( mPopupPanel.GetRootActor(), size, container );
1201 GetTextLayoutInfo();
1206 Vector3 TextInput::GetNaturalSize()
1208 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1210 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1212 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1213 naturalSize.height = mLineHeight;
1219 float TextInput::GetHeightForWidth( float width )
1221 float height = mDisplayedTextView.GetHeightForWidth( width );
1223 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1225 // If the height is zero, it means there is no text. Let's return the cursor height.
1226 height = mLineHeight;
1232 /*end of Virtual methods from parent*/
1234 // Private Internal methods
1236 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1238 switch (gesture.state)
1240 case Gesture::Started:
1241 // fall through so code not duplicated
1242 case Gesture::Continuing:
1244 if (actor == mGrabArea)
1246 SetCursorVisibility( true );
1247 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1248 MoveGrabHandle( gesture.displacement );
1249 HidePopup(); // Do not show popup whilst handle is moving
1251 else if (actor == mHandleOneGrabArea)
1253 // the displacement in PanGesture is affected by the actor's rotation.
1254 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1255 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1257 MoveSelectionHandle( HandleOne, gesture.displacement );
1259 mState = StateDraggingHandle;
1262 else if (actor == mHandleTwoGrabArea)
1264 // the displacement in PanGesture is affected by the actor's rotation.
1265 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1268 MoveSelectionHandle( HandleTwo, gesture.displacement );
1270 mState = StateDraggingHandle;
1276 case Gesture::Finished:
1278 // Revert back to non-pressed selection handle images
1279 if (actor == mGrabArea)
1281 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1282 SetCursorVisibility( true );
1283 SetUpPopupSelection();
1286 if (actor == mHandleOneGrabArea)
1288 // the displacement in PanGesture is affected by the actor's rotation.
1289 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1290 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1292 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1294 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1296 ShowPopupCutCopyPaste();
1298 if (actor == mHandleTwoGrabArea)
1300 // the displacement in PanGesture is affected by the actor's rotation.
1301 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1302 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1304 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1306 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1308 ShowPopupCutCopyPaste();
1317 // Stop the flashing animation so easy to see when moved.
1318 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1320 if (touch.GetPoint(0).state == TouchPoint::Down)
1322 SetCursorVisibility( true );
1323 StopCursorBlinkTimer();
1325 else if (touch.GetPoint(0).state == TouchPoint::Up)
1327 SetCursorVisibility( true );
1328 StartCursorBlinkTimer();
1333 // selection handle one
1334 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1336 if (touch.GetPoint(0).state == TouchPoint::Down)
1338 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1340 else if (touch.GetPoint(0).state == TouchPoint::Up)
1342 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1347 // selection handle two
1348 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1350 if (touch.GetPoint(0).state == TouchPoint::Down)
1352 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1354 else if (touch.GetPoint(0).state == TouchPoint::Up)
1356 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1361 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1363 // If text exists then select nearest word.
1364 if ( !mStyledText.empty())
1368 ShowGrabHandleAndSetVisibility( false );
1373 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1374 // converts the pre-edit word being displayed to a committed word.
1375 if ( !mUnderlinedPriorToPreEdit )
1378 style.SetUnderline( false );
1379 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1381 mPreEditFlag = false;
1382 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1383 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1384 PreEditReset( false );
1386 mCursorPosition = 0;
1388 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1389 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1391 std::size_t start = 0;
1392 std::size_t end = 0;
1393 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1395 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1397 ImfManager imfManager = ImfManager::Get();
1400 imfManager.SetCursorPosition ( mCursorPosition );
1401 imfManager.NotifyCursorPosition();
1404 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1406 SelectText( start, end );
1407 ShowPopupCutCopyPaste();
1411 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1412 HidePopup( false ); // Hide popup with setting to do auto show.
1413 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1417 else if ( mClipboard && mClipboard.NumberOfItems() )
1419 ShowPopupCutCopyPaste();
1422 // If no text and clipboard empty then do nothing
1425 // TODO: Change the function name to be more general.
1426 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1428 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1429 , (mEditOnTouch)?"true":"false"
1430 , (mEditModeActive)?"true":"false");
1432 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1437 if( mGrabArea == actor )
1439 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1441 SetUpPopupSelection();
1451 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1453 // Initially don't create the grab handle.
1454 bool createGrabHandle = false;
1456 if ( !mEditModeActive )
1458 // update line height before calculate the actual position.
1461 // Only start edit mode if TextInput configured to edit on touch
1464 // Set the initial cursor position in the tap point.
1465 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1467 // Create the grab handle.
1468 // TODO Make this a re-usable function.
1469 if ( IsGrabHandleEnabled() )
1471 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1475 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1476 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1477 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1478 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1482 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1483 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1484 // otherwise the Grab handle will be shown when selecting.
1491 // Show the keyboard if it was hidden.
1492 if (!VirtualKeyboard::IsVisible())
1494 VirtualKeyboard::Show();
1497 // Reset keyboard as tap event has occurred.
1498 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1499 PreEditReset( true );
1501 GetTextLayoutInfo();
1503 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1505 // 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.
1507 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1509 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1511 // Notify keyboard so it can 're-capture' word for predictive text.
1512 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1513 ImfManager imfManager = ImfManager::Get();
1516 imfManager.SetCursorPosition ( mCursorPosition );
1517 imfManager.NotifyCursorPosition();
1519 const TextStyle oldInputStyle( mInputStyle );
1521 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1525 // Create the grab handle.
1526 // Grab handle is created later.
1527 createGrabHandle = true;
1529 if( oldInputStyle != mInputStyle )
1531 // Updates the line height accordingly with the input style.
1534 EmitStyleChangedSignal();
1539 if ( createGrabHandle && IsGrabHandleEnabled() )
1541 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1545 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1546 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1547 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1548 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1553 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1555 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1557 // Ignore longpress if in selection mode already
1558 if( mHighlightMeshActor )
1563 if(longPress.state == Dali::Gesture::Started)
1565 // Start edit mode on long press
1566 if ( !mEditModeActive )
1571 // If text exists then select nearest word.
1572 if ( !mStyledText.empty())
1576 ShowGrabHandleAndSetVisibility( false );
1581 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1582 // converts the pre-edit word being displayed to a committed word.
1583 if ( !mUnderlinedPriorToPreEdit )
1586 style.SetUnderline( false );
1587 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1589 mPreEditFlag = false;
1590 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1591 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1592 PreEditReset( false );
1594 mCursorPosition = 0;
1596 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1597 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1599 std::size_t start = 0;
1600 std::size_t end = 0;
1601 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1603 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1605 ImfManager imfManager = ImfManager::Get();
1608 imfManager.SetCursorPosition ( mCursorPosition );
1609 imfManager.NotifyCursorPosition();
1612 SelectText( start, end );
1615 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1616 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1618 ShowPopupCutCopyPaste();
1623 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1625 const Text clipboardText( notifier.GetContent() );
1626 PasteText( clipboardText );
1628 SetCursorVisibility( true );
1629 StartCursorBlinkTimer();
1631 ShowGrabHandleAndSetVisibility( false );
1637 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1639 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1641 const std::string& name = button.GetName();
1643 if(name == TextInputPopup::OPTION_SELECT_WORD)
1645 std::size_t start = 0;
1646 std::size_t end = 0;
1647 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1649 SelectText( start, end );
1651 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1653 SetCursorVisibility(false);
1654 StopCursorBlinkTimer();
1656 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1657 std::size_t start = 0;
1659 SelectText( start, end );
1661 else if(name == TextInputPopup::OPTION_CUT)
1663 bool ret = CopySelectedTextToClipboard();
1667 DeleteHighlightedText( true );
1671 SetCursorVisibility( true );
1672 StartCursorBlinkTimer();
1676 else if(name == TextInputPopup::OPTION_COPY)
1678 CopySelectedTextToClipboard();
1682 SetCursorVisibility( true );
1683 StartCursorBlinkTimer();
1687 else if(name == TextInputPopup::OPTION_PASTE)
1689 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1691 PasteText(retrievedString);
1693 SetCursorVisibility( true );
1694 StartCursorBlinkTimer();
1696 ShowGrabHandleAndSetVisibility( false );
1700 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1702 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1703 // Hence pass the false parameter for signalFinished.
1704 HidePopup( true, false );
1705 mClipboard.ShowClipboard();
1711 bool TextInput::OnCursorBlinkTimerTick()
1714 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1715 if ( mCursorRTLEnabled )
1717 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1719 mCursorBlinkStatus = !mCursorBlinkStatus;
1724 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1726 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1728 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1729 if(mHighlightMeshActor && mState == StateEdit)
1731 ShowPopupCutCopyPaste();
1735 //FIXME this routine needs to be re-written as it contains too many branches.
1736 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1738 std::string keyName = event.keyPressedName;
1739 std::string keyString = event.keyPressed;
1741 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1743 // Do not consume "Tab" and "Escape" keys.
1744 if(keyName == "Tab" || keyName == "Escape")
1746 // Escape key to end the edit mode
1752 HidePopup(); // If Pop-up shown then hides it as editing text.
1754 // Update Flag, indicates whether to update the text-input contents or not.
1755 // Any key stroke that results in a visual change of the text-input should
1756 // set this flag to true.
1759 // Whether to scroll text to cursor position.
1760 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1761 bool scroll = false;
1763 if (keyName == "Return")
1765 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1767 bool preEditFlagPreviouslySet( mPreEditFlag );
1769 // replaces highlighted text with new line
1770 DeleteHighlightedText( false );
1772 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1774 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1775 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1778 mCommitByKeyInput = true;
1781 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1782 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1784 mPreEditFlag = true;
1785 mIgnoreCommitFlag = false;
1795 else if ( keyName == "space" )
1797 if ( mHighlightMeshActor )
1799 // Some text is selected so erase it before adding space.
1800 DeleteHighlightedText( true );
1804 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1806 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1807 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1810 mCommitByKeyInput = true;
1815 else if (keyName == "BackSpace")
1817 if ( mHighlightMeshActor )
1819 // Some text is selected so erase it
1820 DeleteHighlightedText( true );
1825 if ( mCursorPosition > 0 )
1827 DeleteCharacter( mCursorPosition );
1833 else if (keyName == "Right")
1838 else if (keyName == "Left")
1840 AdvanceCursor(true);
1843 else // event is a character
1845 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1846 if ( !keyString.empty() )
1848 // replaces highlighted text with new character
1849 DeleteHighlightedText( false );
1851 // Received key String
1852 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1858 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1859 // as this is a costly operation.
1865 if(update || scroll)
1867 if( IsScrollEnabled() )
1869 // Calculates the new cursor position (in actor coordinates)
1870 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1872 ScrollTextViewToMakeCursorVisible( cursorPosition );
1879 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1881 std::string keyName = event.keyPressedName;
1882 std::string keyString = event.keyPressed;
1884 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1886 // The selected text become deselected when the key code is DALI_KEY_BACK.
1887 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1896 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1898 // Updates the stored scroll position.
1899 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1901 const Vector3& controlSize = GetControlSize();
1902 Size cursorSize( CURSOR_THICKNESS, 0.f );
1904 // Updates the cursor and grab handle position and visibility.
1905 if( mGrabHandle || mCursor )
1907 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1908 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1910 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1912 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1916 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1917 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1922 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1923 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1927 // Updates the selection handles and highlighted text position and visibility.
1928 if( mSelectionHandleOne && mSelectionHandleTwo )
1930 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1931 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1932 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1933 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1934 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1935 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1937 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1938 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1940 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1941 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1942 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1943 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1945 if( mHighlightMeshActor )
1947 mHighlightMeshActor.SetVisible( true );
1953 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1955 // Scroll the text to make the cursor visible.
1956 const Size cursorSize( CURSOR_THICKNESS,
1957 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1959 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1961 const Vector3& controlSize = GetControlSize();
1963 // Calculates the new scroll position.
1964 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1965 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1967 scrollOffset.x += cursorPosition.x;
1970 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1972 scrollOffset.y += cursorPosition.y;
1975 // Sets the new scroll position.
1976 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1977 SetScrollPosition( scrollOffset );
1980 void TextInput::StartScrollTimer()
1984 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1985 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1988 if( !mScrollTimer.IsRunning() )
1990 mScrollTimer.Start();
1994 void TextInput::StopScrollTimer()
1998 mScrollTimer.Stop();
2002 bool TextInput::OnScrollTimerTick()
2004 // TODO: need to set the new style accordingly the new handle position.
2006 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2008 // nothing to do if all handles are invisible or doesn't exist.
2014 // Choose between the grab handle or the selection handles.
2015 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2016 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2017 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2019 std::size_t newCursorPosition = 0;
2020 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2022 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2023 // the new selection handle's position needs to be different of the other one.
2024 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2025 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2026 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2028 if( differentSelectionHandles )
2030 handlePosition = newCursorPosition;
2032 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2034 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2036 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2037 scrollPosition += scrollDelta;
2038 SetScrollPosition( scrollPosition );
2040 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2045 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2048 actualHandlePosition.x += mScrollDisplacement.x;
2049 actualHandlePosition.y += mScrollDisplacement.y;
2054 // Public Internal Methods (public for testing purpose)
2056 void TextInput::SetUpTouchEvents()
2058 if ( !mTapDetector )
2060 mTapDetector = TapGestureDetector::New();
2061 // Attach the actors and connect the signal
2062 mTapDetector.Attach(Self());
2064 // As contains children which may register for tap the default control detector is not used.
2065 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2068 if ( !mDoubleTapDetector )
2070 mDoubleTapDetector = TapGestureDetector::New();
2071 mDoubleTapDetector.SetTapsRequired( 2 );
2072 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2074 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2075 // so that we do not, unnecessarily, have a double tap request all the time
2078 if ( !mPanGestureDetector )
2080 mPanGestureDetector = PanGestureDetector::New();
2081 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2084 if ( !mLongPressDetector )
2086 mLongPressDetector = LongPressGestureDetector::New();
2087 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2088 mLongPressDetector.Attach(Self());
2092 void TextInput::CreateTextViewActor()
2094 mDisplayedTextView = Toolkit::TextView::New();
2095 mDisplayedTextView.SetName( "DisplayedTextView ");
2096 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2097 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2098 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2099 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2100 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2101 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2102 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2103 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2104 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2105 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2107 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2109 Self().Add( mDisplayedTextView );
2112 // Start a timer to initiate, used by the cursor to blink.
2113 void TextInput::StartCursorBlinkTimer()
2115 if ( !mCursorBlinkTimer )
2117 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2118 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2121 if ( !mCursorBlinkTimer.IsRunning() )
2123 mCursorBlinkTimer.Start();
2127 // Start a timer to initiate, used by the cursor to blink.
2128 void TextInput::StopCursorBlinkTimer()
2130 if ( mCursorBlinkTimer )
2132 mCursorBlinkTimer.Stop();
2136 void TextInput::StartEditMode()
2138 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2140 if(!mEditModeActive)
2145 if ( mDoubleTapDetector )
2147 mDoubleTapDetector.Attach( Self() );
2151 void TextInput::EndEditMode()
2153 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2155 ClearKeyInputFocus();
2157 if ( mDoubleTapDetector )
2159 mDoubleTapDetector.Detach( Self() );
2163 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2165 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2167 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2169 style.SetUnderline( true );
2170 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2174 void TextInput::RemovePreEditStyle()
2176 if ( !mUnderlinedPriorToPreEdit )
2179 style.SetUnderline( false );
2180 SetActiveStyle( style, TextStyle::UNDERLINE );
2184 // IMF related methods
2187 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2189 bool update( false );
2190 bool preeditResetRequired ( false );
2192 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2194 HidePopup(); // If Pop-up shown then hides it as editing text.
2197 switch ( imfEvent.eventName )
2199 case ImfManager::PREEDIT:
2201 mIgnoreFirstCommitFlag = false;
2203 // 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
2204 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2206 // replaces highlighted text with new character
2207 DeleteHighlightedText( false );
2210 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2212 if( IsScrollEnabled() )
2214 // Calculates the new cursor position (in actor coordinates)
2215 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2216 ScrollTextViewToMakeCursorVisible( cursorPosition );
2223 case ImfManager::COMMIT:
2225 if( mIgnoreFirstCommitFlag )
2227 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2228 mIgnoreFirstCommitFlag = false;
2232 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2234 // 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
2235 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2237 // replaces highlighted text with new character
2238 DeleteHighlightedText( false );
2241 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2242 // not needed, one such scenario is when the pre-edit word is too long to fit.
2243 if ( !mIgnoreCommitFlag )
2245 update = CommitReceived( imfEvent.predictiveString );
2249 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2255 if( IsScrollEnabled() )
2257 // Calculates the new cursor position (in actor coordinates)
2258 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2260 ScrollTextViewToMakeCursorVisible( cursorPosition );
2265 case ImfManager::DELETESURROUNDING:
2267 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2268 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2270 mPreEditFlag = false;
2272 std::size_t toDelete = 0;
2273 std::size_t numberOfCharacters = 0;
2275 if( mHighlightMeshActor )
2277 // delete highlighted text.
2278 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2279 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2283 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2285 toDelete = mCursorPosition + imfEvent.cursorOffset;
2287 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2289 numberOfCharacters = mStyledText.size() - toDelete;
2293 numberOfCharacters = imfEvent.numberOfChars;
2296 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2297 DeleteRange( toDelete, numberOfCharacters );
2299 mCursorPosition = toDelete;
2300 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2304 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2307 case ImfManager::GETSURROUNDING:
2309 // 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
2310 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2311 if (! ( mHighlightMeshActor || mSelectingText ) )
2313 std::string text( GetText() );
2314 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2316 imfManager.SetCursorPosition( mCursorPosition );
2317 imfManager.SetSurroundingText( text );
2320 if( 0 != mNumberOfSurroundingCharactersDeleted )
2322 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2323 mNumberOfSurroundingCharactersDeleted = 0;
2325 if( mStyledText.empty() )
2327 // Styled text is empty, so set the placeholder text.
2328 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2329 mPlaceHolderSet = true;
2334 case ImfManager::VOID:
2336 DALI_ASSERT_DEBUG( false );
2340 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2342 return callbackData;
2345 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2347 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2349 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2350 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2352 bool preeditResetRequest ( false );
2354 if( mPreEditFlag ) // Already in pre-edit state.
2356 if( mStyledText.size() >= mMaxStringLength )
2358 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2359 // Cannot fit these characters into field, clear pre-edit.
2360 if ( !mUnderlinedPriorToPreEdit )
2363 style.SetUnderline( false );
2364 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2366 mIgnoreCommitFlag = true;
2367 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2368 mPreEditFlag = false;
2369 EmitMaxInputCharactersReachedSignal();
2373 // delete existing pre-edit string
2374 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2376 // Store new pre-edit string
2377 mPreEditString.SetText( keyString );
2379 if ( keyString.empty() )
2381 mPreEditFlag = false;
2382 mCursorPosition = mPreEditStartPosition;
2384 if( mStyledText.empty() )
2386 // Styled text is empty, so set the placeholder text.
2387 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2388 mPlaceHolderSet = true;
2392 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2394 GetTextLayoutInfo();
2399 // Insert new pre-edit string. InsertAt updates the size and position table.
2400 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2401 // 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.
2402 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2403 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2404 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2407 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2411 else // mPreEditFlag not set
2413 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2415 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2416 // new pre-edit so move into pre-edit state by setting flag
2417 mPreEditFlag = true;
2418 mPreEditString.SetText( keyString ); // store new pre-edit string
2419 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2420 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2421 // 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.
2422 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2423 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2424 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2425 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2431 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2435 return preeditResetRequest;
2438 bool TextInput::CommitReceived(const std::string& keyString )
2440 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2441 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2443 bool update( false );
2445 RemovePreEditStyle();
2447 const std::size_t styledTextSize( mStyledText.size() );
2448 if( styledTextSize >= mMaxStringLength )
2450 // Cannot fit these characters into field, clear pre-edit.
2453 mIgnoreCommitFlag = true;
2454 mPreEditFlag = false;
2456 EmitMaxInputCharactersReachedSignal();
2462 // delete existing pre-edit string
2463 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2464 mPreEditFlag = false;
2466 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2467 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2469 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2471 // No need to update cursor position as Cursor location given by touch.
2472 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2473 mPreserveCursorPosition = false;
2477 // Cursor not set by touch so needs to be re-positioned to input more text
2478 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2480 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2481 if ( mCommitByKeyInput )
2483 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2484 mCommitByKeyInput = false;
2490 if ( mSelectTextOnCommit )
2492 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2497 else // mPreEditFlag not set
2499 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2501 if( mStyledText.empty() && mPlaceHolderSet )
2503 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2504 mDisplayedTextView.SetText( "" );
2505 mNumberOfSurroundingCharactersDeleted = 0;
2506 mPlaceHolderSet = false;
2508 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2510 mNumberOfSurroundingCharactersDeleted = 0;
2515 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2520 mSelectTextOnCommit = false;
2522 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2523 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2528 // End of IMF related methods
2530 std::size_t TextInput::DeletePreEdit()
2532 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2534 DALI_ASSERT_DEBUG( mPreEditFlag );
2536 const std::size_t preEditStringLength = mPreEditString.GetLength();
2537 const std::size_t styledTextSize = mStyledText.size();
2539 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2541 // Prevents erase items outside mStyledText bounds.
2542 if( mPreEditStartPosition > styledTextSize )
2544 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2545 mPreEditStartPosition = styledTextSize;
2548 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2550 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2551 endPosition = styledTextSize;
2554 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2556 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2557 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2559 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2561 return preEditStringLength;
2564 void TextInput::PreEditReset( bool preserveCursorPosition )
2566 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2567 preserveCursorPosition, mCursorPosition);
2569 // 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.
2570 mPreserveCursorPosition = preserveCursorPosition;
2572 // Reset incase we are in a pre-edit state.
2573 ImfManager imfManager = ImfManager::Get();
2576 imfManager.Reset(); // Will trigger a commit message
2580 void TextInput::CursorUpdate()
2584 ImfManager imfManager = ImfManager::Get();
2587 std::string text( GetText() );
2588 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2589 imfManager.SetCursorPosition ( mCursorPosition );
2590 imfManager.NotifyCursorPosition();
2594 /* Delete highlighted characters redisplay*/
2595 void TextInput::DeleteHighlightedText( bool inheritStyle )
2597 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2599 if( mHighlightMeshActor )
2601 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2603 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2604 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2606 // Get the styled text of the characters to be deleted as it may be needed if
2607 // the "exceed the text-input's boundaries" option is disabled.
2608 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2610 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2612 mStyledText.erase( start, end ); // erase range of characters
2614 // Remove text from TextView.
2616 if( mStyledText.empty() )
2618 // Styled text is empty, so set the placeholder text.
2619 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2620 mPlaceHolderSet = true;
2624 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2626 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2628 // It may happen than after removing a white space or a new line character,
2629 // two words merge, this new word could be big enough to not fit in its
2630 // current line, so moved to the next one, and make some part of the text to
2631 // exceed the text-input's boundary.
2632 if( !mExceedEnabled )
2634 // Get the new text layout after removing some characters.
2635 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2637 // Get text-input's size.
2638 const Vector3& size = GetControlSize();
2640 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2641 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2643 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2645 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2646 styledCharactersToDelete.begin(),
2647 styledCharactersToDelete.end() );
2651 GetTextLayoutInfo();
2659 const TextStyle oldInputStyle( mInputStyle );
2661 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2663 if( oldInputStyle != mInputStyle )
2665 // Updates the line height accordingly with the input style.
2668 EmitStyleChangedSignal();
2674 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2676 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2677 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2679 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2682 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2684 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2685 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2687 mStyledText.erase(itStart, itEnd);
2689 // update the selection handles if they are visible.
2690 if( mHighlightMeshActor )
2692 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2693 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2695 if( minHandle >= start + ncharacters )
2697 minHandle -= ncharacters;
2699 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2704 if( maxHandle >= start + ncharacters )
2706 maxHandle -= ncharacters;
2708 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2714 // 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.
2717 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2719 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2720 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2721 // Mean we do not re-draw the text more than we have too.
2724 /* Delete character at current cursor position and redisplay*/
2725 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2727 // Ensure positionToDelete is not out of bounds.
2728 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2729 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2730 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2732 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2735 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2737 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2739 // Get the styled text of the character to be deleted as it may be needed if
2740 // the "exceed the text-input's boundaries" option is disabled.
2741 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2743 mStyledText.erase(it); // erase the character left of positionToDelete
2745 if( mStyledText.empty() )
2747 // Styled text is empty, so set the placeholder text.
2748 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2749 mPlaceHolderSet = true;
2753 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2755 const Character characterToDelete = styledCharacterToDelete.mText[0];
2757 // It may happen than after removing a white space or a new line character,
2758 // two words merge, this new word could be big enough to not fit in its
2759 // current line, so moved to the next one, and make some part of the text to
2760 // exceed the text-input's boundary.
2761 if( !mExceedEnabled )
2763 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2765 // Get the new text layout after removing one character.
2766 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2768 // Get text-input's size.
2769 const Vector3& size = GetControlSize();
2771 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2772 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2774 MarkupProcessor::StyledTextArray array;
2775 array.push_back( styledCharacterToDelete );
2776 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2778 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2783 GetTextLayoutInfo();
2785 ShowGrabHandleAndSetVisibility( false );
2787 mCursorPosition = positionToDelete -1;
2789 const TextStyle oldInputStyle( mInputStyle );
2791 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2793 if( oldInputStyle != mInputStyle )
2795 // Updates the line height accordingly with the input style.
2798 EmitStyleChangedSignal();
2803 /*Insert new character into the string and (optionally) redisplay text-input*/
2804 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2806 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2808 // Ensure insertionPosition is not out of bounds.
2809 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2811 bool textExceedsMaximunNumberOfCharacters = false;
2812 bool textExceedsBoundary = false;
2813 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2815 ShowGrabHandleAndSetVisibility( false );
2817 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2821 mIgnoreCommitFlag = true;
2822 mPreEditFlag = false;
2823 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2824 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2827 if( textExceedsMaximunNumberOfCharacters )
2829 EmitMaxInputCharactersReachedSignal();
2832 if( textExceedsBoundary )
2834 EmitInputTextExceedsBoundariesSignal();
2835 PreEditReset( false );
2839 return insertedStringLength;
2842 ImageActor TextInput::CreateCursor( const Vector4& color)
2845 cursor = CreateSolidColorActor(color);
2846 cursor.SetName( "Cursor" );
2848 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2849 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2850 cursor.SetVisible(false);
2855 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2857 // As cursor is not moving due to grab handle, handle should be hidden.
2858 ShowGrabHandleAndSetVisibility( false );
2860 bool cursorPositionChanged = false;
2863 if ( mCursorPosition >= places )
2865 mCursorPosition = mCursorPosition - places;
2866 cursorPositionChanged = true;
2871 if ((mCursorPosition + places) <= mStyledText.size())
2873 mCursorPosition = mCursorPosition + places;
2874 cursorPositionChanged = true;
2878 if( cursorPositionChanged )
2880 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2882 const TextStyle oldInputStyle( mInputStyle );
2883 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2887 if( oldInputStyle != mInputStyle )
2889 // Updates the line height accordingly with the input style.
2892 EmitStyleChangedSignal();
2895 ImfManager imfManager = ImfManager::Get();
2898 imfManager.SetCursorPosition ( mCursorPosition );
2899 imfManager.NotifyCursorPosition();
2904 void TextInput::DrawCursor(const std::size_t nthChar)
2906 // Get height of cursor and set its size
2907 Size size( CURSOR_THICKNESS, 0.0f );
2908 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2910 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2914 // Measure Font so know how big text will be if no initial text to measure.
2915 size.height = mLineHeight;
2918 mCursor.SetSize(size);
2920 // If the character is italic then the cursor also tilts.
2921 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2923 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2925 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2927 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2928 bool altPositionValid; // Alternate cursor validity flag.
2929 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2930 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2932 SetAltCursorEnabled( altPositionValid );
2934 if(!altPositionValid)
2936 mCursor.SetPosition( position + UI_OFFSET );
2940 size.height *= 0.5f;
2941 mCursor.SetSize(size);
2942 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2944 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2945 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2946 size.height = rowSize.height * 0.5f;
2947 mCursorRTL.SetSize(size);
2948 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2951 if( IsScrollEnabled() )
2953 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2954 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2959 void TextInput::SetAltCursorEnabled( bool enabled )
2961 mCursorRTLEnabled = enabled;
2962 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2965 void TextInput::SetCursorVisibility( bool visible )
2967 mCursorVisibility = visible;
2968 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2969 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2972 void TextInput::CreateGrabHandle( Dali::Image image )
2978 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2982 mGrabHandleImage = image;
2985 mGrabHandle = ImageActor::New(mGrabHandleImage);
2986 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2987 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2989 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2991 ShowGrabHandleAndSetVisibility( false );
2993 CreateGrabArea( mGrabHandle );
2995 mActiveLayer.Add(mGrabHandle);
2999 void TextInput::CreateGrabArea( Actor& parent )
3001 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3002 mGrabArea.SetName( "GrabArea" );
3003 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3004 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3005 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3006 mTapDetector.Attach( mGrabArea );
3007 mPanGestureDetector.Attach( mGrabArea );
3008 mLongPressDetector.Attach( mGrabArea );
3010 parent.Add(mGrabArea);
3013 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3015 Vector3 actualHandlePosition;
3019 mActualGrabHandlePosition.x += displacement.x;
3020 mActualGrabHandlePosition.y += displacement.y;
3022 // Grab handle should jump to the nearest character and take cursor with it
3023 std::size_t newCursorPosition = 0;
3024 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3026 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3028 bool handleVisible = true;
3030 if( IsScrollEnabled() )
3032 const Vector3 controlSize = GetControlSize();
3033 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3034 // Scrolls the text if the handle is not in a visible position
3035 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3042 mCurrentHandlePosition = actualHandlePosition;
3043 mScrollDisplacement = Vector2::ZERO;
3047 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3049 mScrollDisplacement.x = -SCROLL_SPEED;
3051 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3053 mScrollDisplacement.x = SCROLL_SPEED;
3055 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3057 mScrollDisplacement.y = -SCROLL_SPEED;
3059 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3061 mScrollDisplacement.y = SCROLL_SPEED;
3067 if( handleVisible && // Only redraw cursor and do updates if position changed
3068 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3070 mCursorPosition = newCursorPosition;
3072 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3074 const TextStyle oldInputStyle( mInputStyle );
3076 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3078 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3080 if( oldInputStyle != mInputStyle )
3082 // Updates the line height accordingly with the input style.
3085 EmitStyleChangedSignal();
3090 return actualHandlePosition;
3093 void TextInput::ShowGrabHandle( bool visible )
3095 if ( IsGrabHandleEnabled() )
3099 mGrabHandle.SetVisible( mGrabHandleVisibility );
3101 StartMonitoringStageForTouch();
3105 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3107 mGrabHandleVisibility = visible;
3108 ShowGrabHandle( visible );
3111 // Callbacks connected to be Property notifications for Boundary checking.
3113 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3115 mIsSelectionHandleOneFlipped = true;
3116 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3117 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3120 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3122 mIsSelectionHandleOneFlipped = false;
3123 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3124 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3127 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3129 mIsSelectionHandleTwoFlipped = true;
3130 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3131 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3134 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3136 mIsSelectionHandleTwoFlipped = false;
3137 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3138 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3141 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3142 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3144 mSelectionHandleOne.SetOpacity(0.0f);
3147 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3149 mSelectionHandleOne.SetOpacity(1.0f);
3152 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3154 mSelectionHandleTwo.SetOpacity(0.0f);
3157 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3159 mSelectionHandleTwo.SetOpacity(1.0f);
3162 // End of Callbacks connected to be Property notifications for Boundary checking.
3164 void TextInput::SetUpHandlePropertyNotifications()
3166 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3168 Vector3 handlesize = GetSelectionHandleSize();
3170 // Exceeding horizontal boundary
3171 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3172 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3174 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3175 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3177 // Within horizontal boundary
3178 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3179 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3181 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3182 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3184 // Exceeding vertical boundary
3185 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3186 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3187 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3188 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3190 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3191 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3192 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3193 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3195 // Within vertical boundary
3196 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3197 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3198 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3199 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3201 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3202 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3203 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3204 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3207 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3209 mSelectionHandleOnePosition = start;
3210 mSelectionHandleTwoPosition = end;
3212 if ( !mSelectionHandleOne )
3214 // create normal and pressed images
3215 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3216 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3218 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3219 mSelectionHandleOne.SetName("SelectionHandleOne");
3220 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3221 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3222 mIsSelectionHandleOneFlipped = false;
3223 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3225 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3226 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3228 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3229 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3231 mTapDetector.Attach( mHandleOneGrabArea );
3232 mPanGestureDetector.Attach( mHandleOneGrabArea );
3234 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3236 mSelectionHandleOne.Add( mHandleOneGrabArea );
3237 mActiveLayer.Add( mSelectionHandleOne );
3240 if ( !mSelectionHandleTwo )
3242 // create normal and pressed images
3243 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3244 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3246 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3247 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3248 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3249 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3250 mIsSelectionHandleTwoFlipped = false;
3251 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3253 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3254 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3255 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3256 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3258 mTapDetector.Attach( mHandleTwoGrabArea );
3259 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3261 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3263 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3265 mActiveLayer.Add( mSelectionHandleTwo );
3268 SetUpHandlePropertyNotifications();
3270 // update table as text may have changed.
3271 GetTextLayoutInfo();
3273 mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3274 mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3276 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3277 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3279 // Calculates and set the visibility if the scroll mode is enabled.
3280 bool isSelectionHandleOneVisible = true;
3281 bool isSelectionHandleTwoVisible = true;
3282 if( IsScrollEnabled() )
3284 const Vector3& controlSize( GetControlSize() );
3285 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3286 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3287 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3288 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3291 CreateHighlight(); // function will only create highlight if not already created.
3294 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3296 Vector3 actualHandlePosition;
3298 if ( mSelectionHandleOne && mSelectionHandleTwo )
3300 const Vector3& controlSize = GetControlSize();
3302 Size cursorSize( CURSOR_THICKNESS, 0.f );
3304 // Get a reference of the wanted selection handle (handle one or two).
3305 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3307 // Get a reference for the current position of the handle and a copy of its pair
3308 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3309 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3311 // Get a handle of the selection handle actor
3312 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3314 // Selection handles should jump to the nearest character
3315 std::size_t newHandlePosition = 0;
3316 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3318 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3320 bool handleVisible = true;
3322 if( IsScrollEnabled() )
3324 mCurrentSelectionId = handleId;
3326 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3327 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3328 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3335 mCurrentSelectionHandlePosition = actualHandlePosition;
3336 mScrollDisplacement = Vector2::ZERO;
3340 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3342 mScrollDisplacement.x = -SCROLL_SPEED;
3344 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3346 mScrollDisplacement.x = SCROLL_SPEED;
3348 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3350 mScrollDisplacement.y = -SCROLL_SPEED;
3352 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3354 mScrollDisplacement.y = SCROLL_SPEED;
3360 if ( handleVisible && // Ensure the handle is visible.
3361 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3362 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3364 currentSelectionHandlePosition = newHandlePosition;
3366 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3367 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3371 if ( handleId == HandleOne )
3373 const TextStyle oldInputStyle( mInputStyle );
3375 // Set Active Style to that of first character in selection
3376 if( mSelectionHandleOnePosition < mStyledText.size() )
3378 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3381 if( oldInputStyle != mInputStyle )
3383 // Updates the line height accordingly with the input style.
3386 EmitStyleChangedSignal();
3392 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3395 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3398 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3399 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3401 if ( selectionHandleActor )
3403 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3404 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3405 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3407 if( IsScrollEnabled() )
3409 const Size cursorSize( CURSOR_THICKNESS,
3410 GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3411 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3413 GetControlSize() ) );
3418 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3420 // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3421 // For now the visual position of end of logical string will be end of visual string.
3422 DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3424 return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3427 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3429 std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3430 std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3431 std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3432 std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3434 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3436 // Deselect text prior to startSelectionIt
3437 for(;it!=startSelectionIt;++it)
3439 selectedVisualText[*it] = false;
3442 // Select text from startSelectionIt -> endSelectionIt
3443 for(;it!=endSelectionIt;++it)
3445 selectedVisualText[*it] = true;
3448 // Deselect text after endSelection
3451 selectedVisualText[*it] = false;
3454 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3457 // Calculate the dimensions of the quads they will make the highlight mesh
3458 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3460 // At the moment there is no public API to modify the block alignment option.
3461 const bool blockAlignEnabled = true;
3463 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3465 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3467 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3468 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3470 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3471 std::vector<bool> selectedVisualText;
3472 GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3473 std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3474 std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3476 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3477 float rowLeft = 0.0f;
3478 float rowRight = 0.0f;
3479 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3480 float maxRowLeft = std::numeric_limits<float>::max();
3481 float maxRowRight = 0.0f;
3483 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3485 // Scan through entire text.
3488 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3490 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3491 bool charSelected( false );
3492 if( selectedIt != selectedEndIt )
3494 charSelected = *selectedIt++;
3497 if(selectionState == SelectionNone)
3501 selectionState = SelectionStarted;
3502 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3503 rowRight = rowLeft + charInfo.mSize.width;
3506 else if(selectionState == SelectionStarted)
3508 // break selection on:
3509 // 1. new line causing selection break. (\n or wordwrap)
3510 // 2. character not selected.
3511 if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3514 // finished selection.
3515 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3516 // that it resides on. That way this enumeration is not necessary.
3518 if(lastIt->mIsNewParagraphChar)
3520 // 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.
3521 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3523 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3524 maxRowLeft = std::min(maxRowLeft, min.x);
3525 maxRowRight = std::max(maxRowRight, max.x);
3526 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3527 float rowTop = rowBottom - rowSize.height;
3529 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3530 if(charSelected && blockAlignEnabled)
3532 rowRight = std::numeric_limits<float>::max();
3534 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3536 selectionState = SelectionNone;
3538 // Still selected? start a new selection
3541 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3542 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3543 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3544 selectionState = SelectionStarted;
3549 // build up highlight(s) with this selection data.
3550 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3551 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3558 // If reached end, and still on selection, then close selection.
3561 if(selectionState == SelectionStarted)
3563 // finished selection.
3565 if(lastIt->mIsNewParagraphChar)
3567 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3569 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3570 maxRowLeft = std::min(maxRowLeft, min.x);
3571 maxRowRight = std::max(maxRowRight, max.x);
3572 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3573 float rowTop = rowBottom - rowSize.height;
3574 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3578 // Get the top left and bottom right corners.
3579 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3580 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3581 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3583 // Clamp quads so they appear to clip to borders of the whole text.
3584 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3586 // For block-align align Further Clamp quads to max left and right extents
3587 if(blockAlignEnabled)
3589 // BlockAlign: Will adjust highlight to block:
3591 // H[ello] (top row right = max of all rows right)
3592 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3593 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3594 // [text] (bottom row left = min of all rows left)
3595 // (common in SMS messaging selection)
3597 // As opposed to the default which is tight text highlighting.
3602 // (common in regular text editors/web browser selection)
3604 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3607 // Finally clamp quads again so they don't exceed the boundry of the control.
3608 const Vector3& controlSize = GetControlSize();
3609 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3612 return mNewHighlightInfo;
3615 void TextInput::UpdateHighlight()
3617 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3619 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3621 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3622 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3623 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3624 // [BOTTOM] [ MIDDLE ]
3627 // Each quad is created as 2 triangles.
3628 // Middle is just 1 quad regardless of its size.
3642 if ( mHighlightMeshActor )
3644 // vertex and triangle buffers should always be present if MeshActor is alive.
3645 HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3646 MeshData::VertexContainer vertices;
3647 Dali::MeshData::FaceIndices faceIndices;
3649 if( !newHighlightInfo.mQuadList.empty() )
3651 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3652 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3654 // vertex position defaults to (0 0 0)
3655 MeshData::Vertex vertex;
3656 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3659 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3661 // Add each quad geometry (a sub-selection) to the mesh data.
3671 QuadCoordinates& quad = *iter;
3673 vertex.x = quad.min.x;
3674 vertex.y = quad.min.y;
3675 vertices.push_back( vertex );
3678 vertex.x = quad.max.x;
3679 vertex.y = quad.min.y;
3680 vertices.push_back( vertex );
3682 // bottom-left (v+2)
3683 vertex.x = quad.min.x;
3684 vertex.y = quad.max.y;
3685 vertices.push_back( vertex );
3687 // bottom-right (v+3)
3688 vertex.x = quad.max.x;
3689 vertex.y = quad.max.y;
3690 vertices.push_back( vertex );
3692 // triangle A (3, 1, 0)
3693 faceIndices.push_back( v + 3 );
3694 faceIndices.push_back( v + 1 );
3695 faceIndices.push_back( v );
3697 // triangle B (0, 2, 3)
3698 faceIndices.push_back( v );
3699 faceIndices.push_back( v + 2 );
3700 faceIndices.push_back( v + 3 );
3702 mMeshData.SetFaceIndices( faceIndices );
3705 BoneContainer bones(0); // passed empty as bones not required
3706 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3707 mHighlightMesh.UpdateMeshData(mMeshData);
3712 void TextInput::ClearPopup()
3714 mPopupPanel.Clear();
3717 void TextInput::AddPopupOptions()
3719 mPopupPanel.AddPopupOptions();
3722 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3724 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3726 Vector3 clampedPosition ( position );
3727 Vector3 tailOffsetPosition ( position );
3729 float xOffSet( 0.0f );
3731 Actor self = Self();
3732 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3734 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3735 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3737 // Clamp to left or right or of boundary
3738 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3740 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3742 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3744 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3747 clampedPosition.x = position.x + xOffSet;
3748 tailOffsetPosition.x = -xOffSet;
3750 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3751 bool flipTail( false );
3753 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3755 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3759 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3760 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3763 void TextInput::HidePopup(bool animate, bool signalFinished )
3765 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3767 mPopupPanel.Hide( animate );
3769 if( animate && signalFinished )
3771 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3776 void TextInput::ShowPopup( bool animate )
3779 Vector2 alternativePopupPosition;
3781 if(mHighlightMeshActor && mState == StateEdit)
3784 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3786 // When text is selected, show popup above top handle (and text), or below bottom handle.
3787 // topHandle: referring to the top most point of the handle or the top line of selection.
3788 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3790 topHandle = mSelectionHandleOneActualPosition;
3791 bottomHandle = mSelectionHandleTwoActualPosition;
3792 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3796 topHandle = mSelectionHandleTwoActualPosition;
3797 bottomHandle = mSelectionHandleOneActualPosition;
3798 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3800 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3801 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3803 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3805 position.x = xPosition;
3807 // Alternative position if no upper space
3808 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3809 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3813 // When no text is selected, show popup at world position of grab handle or cursor
3814 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3815 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3816 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3817 // if can't be positioned above, then position below row.
3818 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3821 // If grab handle enabled then position pop-up below the grab handle.
3822 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3826 SetPopupPosition( position, alternativePopupPosition );
3829 mPopupPanel.Show( Self(), animate );
3830 StartMonitoringStageForTouch();
3832 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3835 void TextInput::ShowPopupCutCopyPaste()
3839 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3840 // Check the selected text is whole text or not.
3841 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3843 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3846 if ( !mStyledText.empty() )
3848 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3849 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3852 if( mClipboard && mClipboard.NumberOfItems() )
3854 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3855 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3860 mPopupPanel.Hide(false);
3864 void TextInput::SetUpPopupSelection( bool treatWhiteSpaceAsAnyOtherCharacter )
3867 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3868 // If no text exists then don't offer to select
3869 if ( !mStyledText.empty() )
3871 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3872 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3873 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, treatWhiteSpaceAsAnyOtherCharacter );
3875 // if clipboard has valid contents then offer paste option
3876 if( mClipboard && mClipboard.NumberOfItems() )
3878 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3879 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3884 mPopupPanel.Hide(false);
3887 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3892 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3893 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
3894 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
3895 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
3897 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3899 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3901 float closestYdifference = std::numeric_limits<float>::max();
3902 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
3903 std::size_t numberOfMatchedCharacters = 0;
3905 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3906 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3908 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3910 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3911 float baselinePosition = info.mPosition.y - info.mDescender;
3913 if( info.mIsVisible )
3915 // store difference between source y point and the y position of the current character
3916 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3918 if( currentYdifference < closestYdifference )
3920 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3921 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3922 closestYdifference = currentYdifference;
3923 matchedCharacters.clear();
3924 numberOfMatchedCharacters = 0; // reset count
3927 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3928 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
3930 // ignore new line character.
3931 if( !info.mIsNewParagraphChar )
3933 matchedCharacters.push_back( info );
3934 numberOfMatchedCharacters++;
3938 } // End of loop checking each character's y position in the character layout table
3940 // Check if last character is a newline, if it is
3941 // then need pretend there is an imaginary line afterwards,
3942 // and check if user is touching below previous line.
3943 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3945 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3947 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3951 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3952 std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3954 bool matched( false );
3956 // 2 Iterate through matching list of y positions and find closest matching X position.
3957 for( ; it != endIt; ++it )
3959 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3961 if( info.mIsVisible )
3963 // stop when on left side of character's center.
3964 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
3965 if( sourceScrollOffset.x < characterMidPointPosition )
3967 if(info.mIsRightToLeftCharacter)
3969 rightToLeftChar = true;
3971 glyphIntersection = info.mPosition.x;
3976 lastRightToLeftChar = info.mIsRightToLeftCharacter;
3982 rightToLeftChar = lastRightToLeftChar;
3985 std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3986 closestIndex = lineOffset + matchCharacterIndex;
3988 mClosestCursorPositionEOL = false; // reset
3989 if ( it == endIt && !matched )
3991 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3994 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3995 if( rightToLeftChar && lastRightToLeftChar )
3997 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4002 // closestIndex is the visual index, need to convert it to the logical index
4003 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4005 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4007 // Checks for situations where user is touching between LTR and RTL
4008 // characters. To identify if the user means the end of a LTR string
4009 // or the beginning of an RTL string, and vice versa.
4010 if( closestIndex > 0 )
4012 if( rightToLeftChar && !lastRightToLeftChar )
4017 // A: In this touch range, the user is indicating that they wish to place
4018 // the cursor at the end of the LTR text.
4019 // B: In this touch range, the user is indicating that they wish to place
4020 // the cursor at the end of the RTL text.
4022 // Result of touching A area:
4023 // [.....LTR]|[RTL......]+
4025 // |: primary cursor (for typing LTR chars)
4026 // +: secondary cursor (for typing RTL chars)
4028 // Result of touching B area:
4029 // [.....LTR]+[RTL......]|
4031 // |: primary cursor (for typing RTL chars)
4032 // +: secondary cursor (for typing LTR chars)
4034 if( sourceScrollOffset.x < glyphIntersection )
4039 else if( !rightToLeftChar && lastRightToLeftChar )
4041 if( sourceScrollOffset.x < glyphIntersection )
4048 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4049 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4050 // one further ahead
4051 if( rightToLeftChar && !lastRightToLeftChar )
4056 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4058 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4060 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4069 float TextInput::GetLineJustificationPosition() const
4071 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4072 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4073 float alignmentOffset = 0.f;
4075 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4076 if( alignment & Toolkit::Alignment::HorizontalLeft )
4078 alignmentOffset = 0.f;
4080 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4082 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4084 else if( alignment & Toolkit::Alignment::HorizontalRight )
4086 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4089 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4090 float justificationOffset = 0.f;
4092 switch( justification )
4094 case Toolkit::TextView::Left:
4096 justificationOffset = 0.f;
4099 case Toolkit::TextView::Center:
4101 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4104 case Toolkit::TextView::Right:
4106 justificationOffset = mTextLayoutInfo.mTextSize.width;
4109 case Toolkit::TextView::Justified:
4111 justificationOffset = 0.f;
4116 DALI_ASSERT_ALWAYS( false );
4120 return alignmentOffset + justificationOffset;
4123 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4125 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4126 A newline character is not inserted in this case */
4128 DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4130 Vector3 cursorPosition;
4132 Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4134 if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4136 // end character so use
4137 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4138 cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4142 currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4145 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4147 // If previous character on a different line then use current characters position
4148 if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4150 if ( mClosestCursorPositionEOL )
4152 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4156 cursorPosition = Vector3(currentCharInfo.mPosition);
4161 // Previous character is on same line so use position of previous character plus it's width.
4162 cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4165 return cursorPosition;
4168 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4170 bool direction(false);
4171 Vector3 alternatePosition;
4172 bool alternatePositionValid(false);
4174 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4177 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4179 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4181 alternatePositionValid = false;
4182 directionRTL = false;
4184 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4186 std::size_t visualCharacterPosition;
4188 // When cursor is not at beginning, consider possibility of
4189 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4190 if(characterPosition > 0)
4192 // Cursor position should be the end of the last character.
4193 // If the last character is LTR, then the end is on the right side of the glyph.
4194 // If the last character is RTL, then the end is on the left side of the glyph.
4195 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4197 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4199 visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4202 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4203 if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4205 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4206 const Vector3& size = GetControlSize();
4208 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4210 --visualCharacterPosition;
4212 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4215 if(!info.mIsNewParagraphChar)
4217 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4221 // When cursor points to first character on new line, position cursor at the start of this glyph.
4222 if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4224 std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4225 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4226 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4228 cursorPosition.x = infoNext.mPosition.x + start;
4229 cursorPosition.y = infoNext.mPosition.y;
4233 // If cursor points to the end of text, then can only position
4234 // cursor where the new line starts based on the line-justification position.
4235 cursorPosition.x = GetLineJustificationPosition();
4237 if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4239 // If this is after the last character, then we can assume that the new cursor
4240 // should be exactly one row below the current row.
4242 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4243 cursorPosition.y = info.mPosition.y + rowRect.height;
4247 // If this is not after last character, then we can use this row's height.
4248 // should be exactly one row below the current row.
4250 const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4251 cursorPosition.y = info.mPosition.y + rowRect.height;
4256 directionRTL = info.mIsRightToLeftCharacter;
4258 // 1. When the cursor is neither at the beginning or the end,
4259 // we can show multiple cursors under situations when the cursor is
4260 // between RTL and LTR text...
4261 if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4263 std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4265 DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4266 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4268 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4270 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4271 // Text: [...LTR...]|[...RTL...]
4273 // Alternate cursor pos: ^
4274 // In which case we need to display an alternate cursor for the RTL text.
4276 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4277 alternatePosition.y = infoAlt.mPosition.y;
4278 alternatePositionValid = true;
4280 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4282 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4283 // Text: |[...RTL...] [...LTR....]
4285 // Alternate cursor pos: ^
4286 // In which case we need to display an alternate cursor for the RTL text.
4288 alternatePosition.x = infoAlt.mPosition.x;
4289 alternatePosition.y = infoAlt.mPosition.y;
4290 alternatePositionValid = true;
4295 // 2. When the cursor is at the end of the text,
4296 // and we have multi-directional text,
4297 // we can also consider showing mulitple cursors.
4298 // The rule here is:
4299 // If first and last characters on row are different
4300 // Directions, then two cursors need to be displayed.
4302 // Get first logical glyph on row
4303 std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4305 std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4306 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4308 if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4310 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4311 // Text: [...LTR...]|[...RTL...]
4313 // Alternate cursor pos: ^
4314 // In which case we need to display an alternate cursor for the RTL text, this cursor
4315 // should be at the end of the given line.
4317 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4318 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4319 alternatePosition.y = infoAlt.mPosition.y;
4320 alternatePositionValid = true;
4322 else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4324 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4325 // Text: |[...RTL...] [...LTR....]
4327 // Alternate cursor pos: ^
4328 // In which case we need to display an alternate cursor for the RTL text.
4330 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4331 alternatePosition.x = infoAlt.mPosition.x;
4332 alternatePosition.y = infoAlt.mPosition.y;
4333 alternatePositionValid = true;
4336 } // characterPosition > 0
4337 else if(characterPosition == 0)
4339 // When the cursor position is at the beginning, it should be at the start of the current character.
4340 // If the current character is LTR, then the start is on the right side of the glyph.
4341 // If the current character is RTL, then the start is on the left side of the glyph.
4342 visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4344 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4346 visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4349 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4350 const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4352 cursorPosition.x = info.mPosition.x + start;
4353 cursorPosition.y = info.mPosition.y;
4354 directionRTL = info.mIsRightToLeftCharacter;
4359 // If the character table is void, place the cursor accordingly the text alignment.
4360 const Vector3& size = GetControlSize();
4362 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4363 float alignmentOffset = 0.f;
4365 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4366 if( alignment & Toolkit::Alignment::HorizontalLeft )
4368 alignmentOffset = 0.f;
4370 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4372 alignmentOffset = 0.5f * ( size.width );
4374 else if( alignment & Toolkit::Alignment::HorizontalRight )
4376 alignmentOffset = size.width;
4379 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4380 cursorPosition.x = alignmentOffset;
4382 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4383 if( alignment & Toolkit::Alignment::VerticalTop )
4385 cursorPosition.y = mLineHeight;
4387 else if( alignment & Toolkit::Alignment::VerticalCenter )
4389 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4391 else if( alignment & Toolkit::Alignment::VerticalBottom )
4393 cursorPosition.y = size.height;
4397 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4398 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4399 if( alternatePositionValid )
4401 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4402 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4405 return cursorPosition;
4408 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4410 // scan string from current position to beginning of current line to note direction of line
4411 while(logicalPosition)
4414 std::size_t visualPosition = GetVisualPosition(logicalPosition);
4415 if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4422 return logicalPosition;
4425 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4429 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4432 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4434 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4435 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4437 min = Vector2::ZERO;
4438 max = Vector2(0.0f, mLineHeight);
4442 // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4443 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4444 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4446 // If cursor is pointing to end of line, then start from last character.
4447 characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4449 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4451 // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4452 if( !it->mIsVisible )
4454 characterPosition = FindVisibleCharacter( Left, characterPosition );
4455 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4458 // Scan characters left and right of cursor, stopping when end of line/string reached or
4459 // y position greater than threshold of reference line.
4461 // 1. scan left until we reach the beginning or a different line.
4462 Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4463 float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4464 // min-x position is the left-most char's left (x)
4465 // max-x position is the right-most char's right (x)
4466 // min-y position is the minimum of all character's top (y)
4467 // max-y position is the maximum of all character's bottom (y+height)
4468 min.y = validCharIt->mPosition.y;
4469 max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4474 min.y = std::min(min.y, validCharIt->mPosition.y);
4475 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4484 if( (it->mPosition.y < referenceLine) ||
4485 (it->mIsNewParagraphChar) ||
4492 // info refers to the first character on this line.
4493 min.x = validCharIt->mPosition.x;
4495 // 2. scan right until we reach end or a different line
4496 it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4497 referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4501 if( (it->mPosition.y > referenceLine) ||
4502 (it->mIsNewParagraphChar) ||
4509 min.y = std::min(min.y, validCharIt->mPosition.y);
4510 max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4515 DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid")
4517 if ( validCharIt != end )
4519 // info refers to the last character on this line.
4520 max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4523 return Size( max.x - min.x, max.y - min.y );
4526 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4528 Actor popUpPanel = mPopupPanel.GetRootActor();
4530 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4536 Dali::Actor parent( touchedActor.GetParent() );
4540 return WasTouchedCheck( parent );
4547 void TextInput::StartMonitoringStageForTouch()
4549 Stage stage = Stage::GetCurrent();
4550 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4553 void TextInput::EndMonitoringStageForTouch()
4555 Stage stage = Stage::GetCurrent();
4556 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4559 void TextInput::OnStageTouched(const TouchEvent& event)
4561 if( event.GetPointCount() > 0 )
4563 if ( TouchPoint::Down == event.GetPoint(0).state )
4565 const Actor touchedActor(event.GetPoint(0).hitActor);
4567 bool popUpShown( false );
4569 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4574 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4576 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4578 EndMonitoringStageForTouch();
4579 HidePopup( true, false );
4582 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4584 EndMonitoringStageForTouch();
4585 ShowGrabHandleAndSetVisibility( false );
4591 void TextInput::SelectText(std::size_t start, std::size_t end)
4593 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4594 IsGrabHandleEnabled()?"true":"false",
4595 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4596 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4597 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4599 StartMonitoringStageForTouch();
4601 if ( mEditModeActive ) // Only allow text selection when in edit mode
4603 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4604 mSelectingText = true;
4606 std::size_t selectionStartPosition = std::min( start, end );
4608 // Hide grab handle when selecting.
4609 ShowGrabHandleAndSetVisibility( false );
4611 if( start != end ) // something to select
4613 SetCursorVisibility( false );
4614 StopCursorBlinkTimer();
4616 CreateSelectionHandles(start, end);
4619 const TextStyle oldInputStyle( mInputStyle );
4620 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4622 if( oldInputStyle != mInputStyle )
4624 // Updates the line height accordingly with the input style.
4627 EmitStyleChangedSignal();
4633 mSelectingText = false;
4637 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4639 MarkupProcessor::StyledTextArray currentSelectedText;
4641 if ( IsTextSelected() )
4643 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4644 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4646 for(; it != end; ++it)
4648 MarkupProcessor::StyledText& styledText( *it );
4649 currentSelectedText.push_back( styledText );
4652 return currentSelectedText;
4655 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4657 const std::size_t beginIndex = std::min( begin, end );
4658 const std::size_t endIndex = std::max( begin, end );
4661 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4663 // Create a styled text array used to replace the text into the text-view.
4664 MarkupProcessor::StyledTextArray text;
4665 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4667 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4668 GetTextLayoutInfo();
4670 if( IsScrollEnabled() )
4672 // Need to set the scroll position as the text's size may have changed.
4673 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4676 ShowGrabHandleAndSetVisibility( false );
4682 // Set Handle positioning as the new style may have repositioned the characters.
4683 SetSelectionHandlePosition(HandleOne);
4684 SetSelectionHandlePosition(HandleTwo);
4687 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4689 // Just hide the grab handle when keyboard is hidden.
4690 if (!keyboardShown )
4692 ShowGrabHandleAndSetVisibility( false );
4694 // If the keyboard is not now being shown, then hide the popup panel
4695 mPopupPanel.Hide( true );
4699 // Removes highlight and resumes edit mode state
4700 void TextInput::RemoveHighlight( bool hidePopup )
4702 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4704 if ( mHighlightMeshActor )
4706 if ( mSelectionHandleOne )
4708 mActiveLayer.Remove( mSelectionHandleOne );
4709 mSelectionHandleOne.Reset();
4710 mSelectionHandleOneOffset.x = 0.0f;
4712 if ( mSelectionHandleTwo )
4714 mActiveLayer.Remove( mSelectionHandleTwo );
4715 mSelectionHandleTwo.Reset();
4716 mSelectionHandleTwoOffset.x = 0.0f;
4719 mNewHighlightInfo.mQuadList.clear();
4721 Self().Remove( mHighlightMeshActor );
4723 SetCursorVisibility( true );
4724 StartCursorBlinkTimer();
4726 mHighlightMeshActor.Reset();
4727 // NOTE: We cannot dereference mHighlightMesh, due
4728 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4736 mSelectionHandleOnePosition = 0;
4737 mSelectionHandleTwoPosition = 0;
4740 void TextInput::CreateHighlight()
4742 if ( !mHighlightMeshActor )
4744 mMeshData = MeshData( );
4745 mMeshData.SetHasNormals( true );
4747 mCustomMaterial = Material::New("CustomMaterial");
4748 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4750 mMeshData.SetMaterial( mCustomMaterial );
4752 mHighlightMesh = Mesh::New( mMeshData );
4754 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4755 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4756 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4757 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4758 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4759 mHighlightMeshActor.SetAffectedByLighting(false);
4761 Self().Add(mHighlightMeshActor);
4766 bool TextInput::CopySelectedTextToClipboard()
4768 mCurrentCopySelecton.clear();
4770 mCurrentCopySelecton = GetSelectedText();
4772 std::string stringToStore;
4774 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4775 * a marked up string.
4777 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4778 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4779 bool success = mClipboard.SetItem( stringToStore );
4783 void TextInput::PasteText( const Text& text )
4785 // Update Flag, indicates whether to update the text-input contents or not.
4786 // Any key stroke that results in a visual change of the text-input should
4787 // set this flag to true.
4788 bool update = false;
4789 if( mHighlightMeshActor )
4791 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4792 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4794 ImfManager imfManager = ImfManager::Get();
4797 imfManager.SetCursorPosition( mCursorPosition );
4798 imfManager.NotifyCursorPosition();
4800 DeleteHighlightedText( true );
4804 bool textExceedsMaximunNumberOfCharacters = false;
4805 bool textExceedsBoundary = false;
4807 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4809 mCursorPosition += insertedStringLength;
4810 ImfManager imfManager = ImfManager::Get();
4813 imfManager.SetCursorPosition ( mCursorPosition );
4814 imfManager.NotifyCursorPosition();
4817 update = update || ( insertedStringLength > 0 );
4824 if( insertedStringLength < text.GetLength() )
4826 EmitMaxInputCharactersReachedSignal();
4829 if( textExceedsBoundary )
4831 EmitInputTextExceedsBoundariesSignal();
4835 void TextInput::SetTextDirection()
4837 // Put the cursor to the right if we are empty and an RTL language is being used.
4838 if ( mStyledText.empty() )
4840 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4842 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4843 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4845 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4846 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4848 int alignment( mDisplayedTextView.GetTextAlignment() &
4849 ( Toolkit::Alignment::VerticalTop |
4850 Toolkit::Alignment::VerticalCenter |
4851 Toolkit::Alignment::VerticalBottom |
4852 Toolkit::Alignment::HorizontalCenter ) );
4853 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4855 // If our alignment is in the center, then do not change.
4856 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4858 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4861 // If our justification is in the center, then do not change.
4862 if ( justification != Toolkit::TextView::Center )
4864 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4867 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4868 mDisplayedTextView.SetLineJustification( justification );
4872 void TextInput::UpdateLineHeight()
4874 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4875 mLineHeight = font.GetLineHeight();
4877 // 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.
4879 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4881 if( !mExceedEnabled || shrink )
4883 mLineHeight = std::min( mLineHeight, GetControlSize().height );
4887 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4889 std::size_t position = 0;
4891 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4897 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4899 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4901 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4907 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4908 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4910 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4916 position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4921 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4928 void TextInput::SetSortModifier( float depthOffset )
4930 if(mDisplayedTextView)
4932 mDisplayedTextView.SetSortModifier(depthOffset);
4936 void TextInput::SetSnapshotModeEnabled( bool enable )
4938 if(mDisplayedTextView)
4940 mDisplayedTextView.SetSnapshotModeEnabled( enable );
4944 bool TextInput::IsSnapshotModeEnabled() const
4946 bool snapshotEnabled = false;
4948 if(mDisplayedTextView)
4950 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4953 return snapshotEnabled;
4956 void TextInput::SetMarkupProcessingEnabled( bool enable )
4958 mMarkUpEnabled = enable;
4961 bool TextInput::IsMarkupProcessingEnabled() const
4963 return mMarkUpEnabled;
4966 void TextInput::SetScrollEnabled( bool enable )
4968 if( mDisplayedTextView )
4970 mDisplayedTextView.SetScrollEnabled( enable );
4975 // Don't set cursor's and handle's visibility to false if they are outside the
4976 // boundaries of the text-input.
4977 mIsCursorInScrollArea = true;
4978 mIsGrabHandleInScrollArea = true;
4979 if( mSelectionHandleOne && mSelectionHandleTwo )
4981 mSelectionHandleOne.SetVisible( true );
4982 mSelectionHandleTwo.SetVisible( true );
4984 if( mHighlightMeshActor )
4986 mHighlightMeshActor.SetVisible( true );
4992 bool TextInput::IsScrollEnabled() const
4994 bool scrollEnabled = false;
4996 if( mDisplayedTextView )
4998 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5001 return scrollEnabled;
5004 void TextInput::SetScrollPosition( const Vector2& position )
5006 if( mDisplayedTextView )
5008 mDisplayedTextView.SetScrollPosition( position );
5012 Vector2 TextInput::GetScrollPosition() const
5014 Vector2 scrollPosition;
5016 if( mDisplayedTextView )
5018 scrollPosition = mDisplayedTextView.GetScrollPosition();
5021 return scrollPosition;
5024 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5026 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5027 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5028 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5030 // Add style to the new input text.
5031 MarkupProcessor::StyledTextArray textToInsert;
5032 for( std::size_t i = 0; i < insertedStringLength; ++i )
5034 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5035 textToInsert.push_back( newStyledCharacter );
5038 //Insert text to the TextView.
5039 const bool emptyTextView = mStyledText.empty();
5040 if( emptyTextView && mPlaceHolderSet )
5042 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5043 mDisplayedTextView.SetText( textToInsert );
5047 if( 0 == numberOfCharactersToReplace )
5049 mDisplayedTextView.InsertTextAt( position, textToInsert );
5053 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5056 mPlaceHolderSet = false;
5058 if( textToInsert.empty() )
5060 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5061 GetTextLayoutInfo();
5065 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5066 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5069 textExceedsBoundary = false;
5071 if( !mExceedEnabled )
5073 const Vector3& size = GetControlSize();
5075 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5077 // If new text does not fit within TextView
5078 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5079 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5080 GetTextLayoutInfo();
5081 textExceedsBoundary = true;
5082 insertedStringLength = 0;
5085 if( textExceedsBoundary )
5087 // Add the part of the text which fits on the text-input.
5089 // Split the text which doesn't fit in two halves.
5090 MarkupProcessor::StyledTextArray firstHalf;
5091 MarkupProcessor::StyledTextArray secondHalf;
5092 SplitText( textToInsert, firstHalf, secondHalf );
5094 // Clear text. This text will be filled with the text inserted.
5095 textToInsert.clear();
5097 // Where to insert the text.
5098 std::size_t positionToInsert = position;
5100 bool end = text.GetLength() <= 1;
5103 // Insert text and check ...
5104 const std::size_t textLength = firstHalf.size();
5105 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5106 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5108 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5110 // Inserted text doesn't fit.
5112 // Remove inserted text
5113 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5114 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5116 // The iteration finishes when only one character doesn't fit.
5117 end = textLength <= 1;
5121 // Prepare next two halves for next iteration.
5122 MarkupProcessor::StyledTextArray copyText = firstHalf;
5123 SplitText( copyText, firstHalf, secondHalf );
5130 // store text to be inserted in mStyledText.
5131 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5133 // Increase the inserted characters counter.
5134 insertedStringLength += textLength;
5136 // Prepare next two halves for next iteration.
5137 MarkupProcessor::StyledTextArray copyText = secondHalf;
5138 SplitText( copyText, firstHalf, secondHalf );
5140 // Update where next text has to be inserted
5141 positionToInsert += textLength;
5147 if( textToInsert.empty() && emptyTextView )
5149 // No character has been added and the text-view was empty.
5150 // Set the placeholder text.
5151 mDisplayedTextView.SetText( mStyledPlaceHolderText );
5152 mPlaceHolderSet = true;
5156 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5157 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5158 mPlaceHolderSet = false;
5161 return insertedStringLength;
5164 void TextInput::GetTextLayoutInfo()
5166 if( mStyledText.empty() )
5168 // The text-input has no text, clear the text-view's layout info.
5169 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5173 if( mDisplayedTextView )
5175 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5179 // There is no text-view.
5180 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5185 void TextInput::SetOffsetFromText( const Vector4& offset )
5187 mPopupOffsetFromText = offset;
5190 const Vector4& TextInput::GetOffsetFromText() const
5192 return mPopupOffsetFromText;
5195 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5197 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5201 TextInput& textInputImpl( GetImpl( textInput ) );
5203 switch ( propertyIndex )
5205 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5207 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5210 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5212 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5215 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5217 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5220 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5222 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5225 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5227 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5230 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5232 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5235 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5237 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5240 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5242 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5245 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5247 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5250 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5252 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5255 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5257 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5260 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5262 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5265 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5267 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5270 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5272 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5275 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5277 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5280 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5282 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5288 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5290 Property::Value value;
5292 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5296 TextInput& textInputImpl( GetImpl( textInput ) );
5298 switch ( propertyIndex )
5300 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5302 value = textInputImpl.GetMaterialDiffuseColor();
5305 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5307 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5310 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5312 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5315 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5317 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5320 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5322 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5325 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5327 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5330 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5332 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5335 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5337 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5340 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5342 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5345 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5347 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5350 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5352 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5355 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5357 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5360 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5362 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5365 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5367 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5370 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5372 value = textInputImpl.GetOffsetFromText();
5375 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5377 value = textInputImpl.mCursor.GetCurrentColor();
5384 void TextInput::EmitStyleChangedSignal()
5386 // emit signal if input style changes.
5387 Toolkit::TextInput handle( GetOwner() );
5388 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5391 void TextInput::EmitTextModified()
5393 // emit signal when text changes.
5394 Toolkit::TextInput handle( GetOwner() );
5395 mTextModifiedSignal.Emit( handle );
5399 void TextInput::EmitMaxInputCharactersReachedSignal()
5401 // emit signal if max characters is reached during text input.
5402 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5404 Toolkit::TextInput handle( GetOwner() );
5405 mMaxInputCharactersReachedSignalV2.Emit( handle );
5408 void TextInput::EmitInputTextExceedsBoundariesSignal()
5410 // Emit a signal when the input text exceeds the boundaries of the text input.
5412 Toolkit::TextInput handle( GetOwner() );
5413 mInputTextExceedBoundariesSignalV2.Emit( handle );
5416 } // namespace Internal
5418 } // namespace Toolkit