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.
19 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
25 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/key-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/property-notification.h>
32 #include <dali/integration-api/debug.h>
33 #include <dali/public-api/images/resource-image.h>
36 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
37 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
38 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
39 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
51 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
52 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
53 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
54 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
55 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
56 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
58 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
59 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
60 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
61 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
62 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
64 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
65 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
66 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
67 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
68 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
70 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
71 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
72 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
73 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
74 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
75 const float CURSOR_THICKNESS(4.0f);
76 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
77 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
79 const std::string NEWLINE( "\n" );
81 const TextStyle DEFAULT_TEXT_STYLE;
83 const unsigned int SCROLL_TICK_INTERVAL = 50u;
84 const float SCROLL_THRESHOLD = 10.f;
85 const float SCROLL_SPEED = 15.f;
88 * Selection state enumeration (FSM)
92 SelectionNone, ///< Currently not encountered selected section.
93 SelectionStarted, ///< Encountered selected section
94 SelectionFinished ///< Finished selected section
97 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
99 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
103 if( ( *it ).mIsVisible )
105 return --cursorPosition;
114 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
116 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
118 if( ( *it ).mIsVisible )
120 return cursorPosition;
126 return cursorPosition;
130 * Whether the given position plus the cursor size offset is inside the given boundary.
132 * @param[in] position The given position.
133 * @param[in] cursorSize The cursor size.
134 * @param[in] controlSize The given boundary.
136 * @return whether the given position is inside the given boundary.
138 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
140 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
141 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
142 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
143 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
147 * Splits a text in two halves.
149 * If the text's number of characters is odd, firstHalf has one more character.
151 * @param[in] text The text to be split.
152 * @param[out] firstHalf The first half of the text.
153 * @param[out] secondHalf The second half of the text.
155 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
156 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
157 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
162 const std::size_t textLength = text.size();
163 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
165 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
166 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
169 } // end of namespace
177 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
178 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
179 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
180 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
181 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
182 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
183 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
184 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
185 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
186 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
187 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
188 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
189 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
190 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
191 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
192 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
203 return Toolkit::TextInput::New();
206 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
208 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
209 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
210 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
211 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
212 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
213 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
217 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
225 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
226 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
227 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
228 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
229 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
230 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
231 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
232 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
235 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
237 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
239 QuadCoordinates quad(x1, y1, x2, y2);
240 mQuadList.push_back( quad );
243 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
245 for(std::size_t i = 0;i < mQuadList.size(); i++)
247 QuadCoordinates& quad = mQuadList[i];
249 quad.min.Clamp(min, max);
250 quad.max.Clamp(min, max);
254 // [TextInput] ////////////////////////////////////////////////////////////////
256 Dali::Toolkit::TextInput TextInput::New()
258 // Create the implementation
259 TextInputPtr textInput(new TextInput());
260 // Pass ownership to CustomActor via derived handle
261 Dali::Toolkit::TextInput handle(*textInput);
262 handle.SetName( "TextInput");
264 textInput->Initialize();
268 TextInput::TextInput()
269 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
274 mDisplayedTextView(),
275 mStyledPlaceHolderText(),
276 mMaxStringLength( DEFAULT_MAX_SIZE ),
277 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
278 mCursorPosition( 0 ),
279 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
280 mIsSelectionHandleOneFlipped( false ),
281 mIsSelectionHandleTwoFlipped( false ),
282 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
283 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
284 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
285 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
286 mSelectionHandleOnePosition( 0 ),
287 mSelectionHandleTwoPosition( 0 ),
289 mPreEditStartPosition( 0 ),
290 mPreEditLength ( 0 ),
291 mNumberOfSurroundingCharactersDeleted( 0 ),
292 mTouchStartTime( 0 ),
294 mCurrentCopySelecton(),
297 mScrollDisplacement(),
298 mCurrentHandlePosition(),
299 mCurrentSelectionId(),
300 mCurrentSelectionHandlePosition(),
301 mRequestedSelection( 0, 0 ),
302 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
303 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
305 mMaterialColor( LIGHTBLUE ),
306 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
307 mOverrideAutomaticAlignment( false ),
308 mCursorRTLEnabled( false ),
309 mClosestCursorPositionEOL ( false ),
310 mCursorBlinkStatus( true ),
311 mCursorVisibility( false ),
312 mGrabHandleVisibility( false ),
313 mIsCursorInScrollArea( true ),
314 mIsGrabHandleInScrollArea( true ),
315 mEditModeActive( false ),
316 mEditOnTouch( true ),
317 mTextSelection( true ),
318 mExceedEnabled( true ),
319 mGrabHandleEnabled( true ),
320 mIsSelectionHandleFlipEnabled( true ),
321 mPreEditFlag( false ),
322 mIgnoreCommitFlag( false ),
323 mIgnoreFirstCommitFlag( false ),
324 mSelectingText( false ),
325 mPreserveCursorPosition( false ),
326 mSelectTextOnCommit( false ),
327 mUnderlinedPriorToPreEdit ( false ),
328 mCommitByKeyInput( false ),
329 mPlaceHolderSet( false ),
330 mMarkUpEnabled( false )
332 // Updates the line height accordingly with the input style.
336 TextInput::~TextInput()
338 StopCursorBlinkTimer();
343 std::string TextInput::GetText() const
347 // Return text-view's text only if the text-input's text is not empty
348 // in order to not to return the placeholder text.
349 if( !mStyledText.empty() )
351 text = mDisplayedTextView.GetText();
357 std::string TextInput::GetMarkupText() const
359 std::string markupString;
360 MarkupProcessor::GetMarkupString( mStyledText, markupString );
365 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
367 mDisplayedTextView.SetText( stylePlaceHolderText );
368 mPlaceHolderSet = true;
369 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
372 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
374 // Get the placeholder styled text array from the markup string.
375 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
376 if( mStyledText.empty() )
378 ShowPlaceholderText( mStyledPlaceHolderText );
382 std::string TextInput::GetPlaceholderText()
384 // Traverses the styled placeholder array getting only the text.
385 // Note that for some languages a 'character' could be represented by more than one 'char'
387 std::string placeholderText;
388 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
390 placeholderText.append( (*it).mText.GetText() );
393 return placeholderText ;
396 void TextInput::SetInitialText(const std::string& initialText)
398 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
400 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
402 mPreEditFlag = false;
403 mIgnoreCommitFlag = true;
406 SetText( initialText );
407 PreEditReset( false ); // Reset keyboard as text changed
410 void TextInput::SetText(const std::string& initialText)
412 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
414 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
416 if( mStyledText.empty() )
418 ShowPlaceholderText( mStyledPlaceHolderText );
422 mDisplayedTextView.SetText( mStyledText );
423 mPlaceHolderSet = false;
428 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
430 ImfManager imfManager = ImfManager::Get();
433 imfManager.SetCursorPosition( mCursorPosition );
434 imfManager.SetSurroundingText( initialText );
435 imfManager.NotifyCursorPosition();
438 if( IsScrollEnabled() )
440 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
443 ShowGrabHandleAndSetVisibility( false );
452 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
454 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
456 mDisplayedTextView.SetText( styleText );
457 mPlaceHolderSet = false;
459 // If text alignment hasn't been manually set by application developer, then we
460 // automatically determine the alignment based on the content of the text i.e. what
461 // language the text begins with.
462 // TODO: This should determine different alignments for each line (broken by '\n') of text.
463 if(!mOverrideAutomaticAlignment)
465 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
466 bool leftToRight(true);
468 if( !styleText.empty() )
470 bool breakOut(false);
472 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
474 const Text& text = textIter->mText;
476 for( std::size_t i = 0; i < text.GetLength(); ++i )
478 Character character( text[i] );
479 if( character.GetCharacterDirection() != Character::Neutral )
481 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
489 // Based on this direction, either left or right align text if not manually set by application developer.
490 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
491 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
492 Toolkit::Alignment::VerticalTop ) );
493 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
499 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
501 mMaxStringLength = maxChars;
504 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
506 DALI_ASSERT_DEBUG( maxLines > 0 )
510 mNumberOflinesLimit = maxLines;
514 std::size_t TextInput::GetNumberOfLinesLimit() const
516 return mNumberOflinesLimit;
519 std::size_t TextInput::GetNumberOfCharacters() const
521 return mStyledText.size();
525 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
527 mMaterialColor = color;
528 if ( mCustomMaterial )
530 mCustomMaterial.SetDiffuseColor( mMaterialColor );
531 mMeshData.SetMaterial( mCustomMaterial );
535 const Vector4& TextInput::GetMaterialDiffuseColor() const
537 return mMaterialColor;
542 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
544 return mInputStartedSignal;
547 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
549 return mInputFinishedSignal;
552 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
554 return mCutAndPasteToolBarDisplayed;
557 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
559 return mStyleChangedSignal;
562 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
564 return mTextModifiedSignal;
567 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
569 return mMaxInputCharactersReachedSignal;
572 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
574 return mInputTextExceedBoundariesSignal;
577 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
579 Dali::BaseHandle handle( object );
581 bool connected( true );
582 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
584 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
586 textInput.InputStartedSignal().Connect( tracker, functor );
588 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
590 textInput.InputFinishedSignal().Connect( tracker, functor );
592 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
594 textInput.StyleChangedSignal().Connect( tracker, functor );
596 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
598 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
600 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
602 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
606 // signalName does not match any signal
613 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
617 // update line height before calculate the actual position.
622 if( setCursorOnTouchPoint )
624 // Sets the cursor position for the given touch point.
625 ReturnClosestIndex( touchPoint, mCursorPosition );
627 // Creates the grab handle.
628 if( IsGrabHandleEnabled() )
630 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
634 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
635 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
636 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
637 ShowGrabHandleAndSetVisibility( true );
639 // Scrolls the text-view if needed.
640 if( IsScrollEnabled() )
642 ScrollTextViewToMakeCursorVisible( cursorPosition );
648 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
660 bool TextInput::IsEditable() const
662 return mEditModeActive;
665 void TextInput::SetEditOnTouch( bool editOnTouch )
667 mEditOnTouch = editOnTouch;
670 bool TextInput::IsEditOnTouch() const
675 void TextInput::SetTextSelectable( bool textSelectable )
677 mTextSelection = textSelectable;
680 bool TextInput::IsTextSelectable() const
682 return mTextSelection;
685 bool TextInput::IsTextSelected() const
687 return mHighlightMeshActor;
690 void TextInput::DeSelectText()
697 void TextInput::SetGrabHandleImage(Dali::Image image )
701 CreateGrabHandle(image);
705 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
707 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
711 mCursor.SetImage( image );
712 mCursor.SetNinePatchBorder( border );
716 Vector3 TextInput::GetSelectionHandleSize()
718 return DEFAULT_SELECTION_HANDLE_SIZE;
721 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
723 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
727 mCursorRTL.SetImage( image);
728 mCursorRTL.SetNinePatchBorder( border );
732 void TextInput::EnableGrabHandle(bool toggle)
734 // enables grab handle with will in turn de-activate magnifier
735 mGrabHandleEnabled = toggle;
738 bool TextInput::IsGrabHandleEnabled()
740 // if false then magnifier will be shown instead.
741 return mGrabHandleEnabled;
744 void TextInput::EnableSelectionHandleFlip( bool toggle )
746 // Deprecated function. To be removed.
747 mIsSelectionHandleFlipEnabled = toggle;
750 bool TextInput::IsSelectionHandleFlipEnabled()
752 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
756 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
758 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
759 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
760 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
762 mSelectionHandleFlipMargin = margin;
765 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
767 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
768 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
770 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
771 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
773 const Vector4 boundary( originX,
775 originX + boundingRectangle.width,
776 originY + boundingRectangle.height );
778 mBoundingRectangleWorldCoordinates = boundary;
781 const Rect<float> TextInput::GetBoundingRectangle() const
783 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
785 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
786 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
788 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
793 const Vector4& TextInput::GetSelectionHandleFlipMargin()
795 return mSelectionHandleFlipMargin;
798 void TextInput::SetTextColor( const Vector4& color )
800 mDisplayedTextView.SetColor( color );
803 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
805 if( style != mInputStyle )
808 bool emitSignal = false;
810 // mask: modify style according to mask, if different emit signal.
811 const TextStyle oldInputStyle( mInputStyle );
813 // Copy the new style.
814 mInputStyle.Copy( style, mask );
816 // if style has changed, emit signal.
817 if( oldInputStyle != mInputStyle )
822 // Updates the line height accordingly with the input style.
825 // Changing font point size will require the cursor to be re-sized
830 EmitStyleChangedSignal();
835 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
837 if ( IsTextSelected() )
839 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
840 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
842 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
844 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
847 // Keeps the old style to be compared with the new one.
848 const TextStyle oldInputStyle( mInputStyle );
850 // Copy only those parameters from the style which are set in the mask.
851 mInputStyle.Copy( style, mask );
853 if( mInputStyle != oldInputStyle )
855 // Updates the line height accordingly with the input style.
858 EmitStyleChangedSignal();
863 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
865 if( !mStyledText.empty() )
867 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
871 TextStyle TextInput::GetStyleAtCursor() const
875 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
877 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
878 style = mStyledText.at( mCursorPosition-1 ).mStyle;
884 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
886 Dali::Font defaultFont = Dali::Font::New();
887 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
894 TextStyle TextInput::GetStyleAt( std::size_t position ) const
896 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
898 if( position >= mStyledText.size() )
900 position = mStyledText.size() - 1;
903 return mStyledText.at( position ).mStyle;
906 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
908 mDisplayedTextView.SetTextAlignment( align );
909 mOverrideAutomaticAlignment = true;
912 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
914 mDisplayedTextView.SetLineJustification( justification );
915 mOverrideAutomaticAlignment = true;
918 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
920 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
923 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
925 return mDisplayedTextView.GetFadeBoundary();
928 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
930 return mDisplayedTextView.GetTextAlignment();
933 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
935 mDisplayedTextView.SetMultilinePolicy( policy );
938 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
940 return mDisplayedTextView.GetMultilinePolicy();
943 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
945 mDisplayedTextView.SetWidthExceedPolicy( policy );
948 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
950 return mDisplayedTextView.GetWidthExceedPolicy();
953 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
955 mDisplayedTextView.SetHeightExceedPolicy( policy );
958 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
960 return mDisplayedTextView.GetHeightExceedPolicy();
963 void TextInput::SetExceedEnabled( bool enable )
965 mExceedEnabled = enable;
968 bool TextInput::GetExceedEnabled() const
970 return mExceedEnabled;
973 void TextInput::SetBackground(Dali::Image image )
975 // TODO Should add this function and add public api to match.
978 bool TextInput::OnTouchEvent(const TouchEvent& event)
983 bool TextInput::OnKeyEvent(const KeyEvent& event)
985 switch( event.state )
989 return OnKeyDownEvent(event);
995 return OnKeyUpEvent(event);
1007 void TextInput::OnKeyInputFocusGained()
1009 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1011 mEditModeActive = true;
1013 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1015 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1017 // Updates the line height accordingly with the input style.
1020 // Connect the signals to use in text input.
1021 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1022 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1024 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1027 GetTextLayoutInfo();
1030 SetCursorVisibility( true );
1031 StartCursorBlinkTimer();
1033 Toolkit::TextInput handle( GetOwner() );
1034 mInputStartedSignal.Emit( handle );
1036 ImfManager imfManager = ImfManager::Get();
1040 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1042 // Notify that the text editing start.
1043 imfManager.Activate();
1045 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1046 imfManager.SetRestoreAfterFocusLost( true );
1048 imfManager.SetCursorPosition( mCursorPosition );
1049 imfManager.NotifyCursorPosition();
1052 mClipboard = Clipboard::Get(); // Store handle to clipboard
1054 // Now in edit mode we can accept string to paste from clipboard
1055 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1058 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1062 void TextInput::OnKeyInputFocusLost()
1064 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1068 // If key input focus is lost, it removes the
1069 // underline from the last pre-edit text.
1070 RemovePreEditStyle();
1071 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1072 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1076 ImfManager imfManager = ImfManager::Get();
1079 // The text editing is finished. Therefore the imf manager don't have restore activation.
1080 imfManager.SetRestoreAfterFocusLost( false );
1082 // Notify that the text editing finish.
1083 imfManager.Deactivate();
1085 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1087 // Disconnect signal used the text input.
1088 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1090 Toolkit::TextInput handle( GetOwner() );
1091 mInputFinishedSignal.Emit( handle );
1092 mEditModeActive = false;
1093 mPreEditFlag = false;
1095 SetCursorVisibility( false );
1096 StopCursorBlinkTimer();
1098 ShowGrabHandleAndSetVisibility( false );
1101 // No longer in edit mode so do not want to receive string from clipboard
1102 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1105 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1108 Clipboard clipboard = Clipboard::Get();
1111 clipboard.HideClipboard();
1115 void TextInput::OnControlStageConnection()
1117 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1119 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1121 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1125 void TextInput::CreateActiveLayer()
1127 Actor self = Self();
1128 mActiveLayer = Layer::New();
1129 mActiveLayer.SetName ( "ActiveLayerActor" );
1131 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1132 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1133 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1135 self.Add( mActiveLayer );
1136 mActiveLayer.RaiseToTop();
1139 void TextInput::OnInitialize()
1141 CreateTextViewActor();
1145 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1146 // different positions depending on language)
1147 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1148 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1150 Actor self = Self();
1151 self.Add( mCursor );
1152 self.Add( mCursorRTL );
1154 mCursorVisibility = false;
1156 CreateActiveLayer(); // todo move this so layer only created when needed.
1158 // Assign names to image actors
1159 mCursor.SetName("mainCursor");
1160 mCursorRTL.SetName("rtlCursor");
1163 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1165 mDisplayedTextView.SetSize( targetSize );
1166 GetTextLayoutInfo();
1167 mActiveLayer.SetSize(targetSize);
1170 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1172 Relayout( mDisplayedTextView, size, container );
1173 Relayout( mPopupPanel.GetRootActor(), size, container );
1175 GetTextLayoutInfo();
1180 Vector3 TextInput::GetNaturalSize()
1182 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1184 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1186 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1187 naturalSize.height = mLineHeight;
1193 float TextInput::GetHeightForWidth( float width )
1195 float height = mDisplayedTextView.GetHeightForWidth( width );
1197 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1199 // If the height is zero, it means there is no text. Let's return the cursor height.
1200 height = mLineHeight;
1206 /*end of Virtual methods from parent*/
1208 // Private Internal methods
1210 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1212 switch (gesture.state)
1214 case Gesture::Started:
1215 // fall through so code not duplicated
1216 case Gesture::Continuing:
1218 if (actor == mGrabArea)
1220 SetCursorVisibility( true );
1221 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1222 MoveGrabHandle( gesture.displacement );
1223 HidePopup(); // Do not show popup whilst handle is moving
1225 else if (actor == mHandleOneGrabArea)
1227 // the displacement in PanGesture is affected by the actor's rotation.
1228 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1229 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1231 MoveSelectionHandle( HandleOne, gesture.displacement );
1233 mState = StateDraggingHandle;
1236 else if (actor == mHandleTwoGrabArea)
1238 // the displacement in PanGesture is affected by the actor's rotation.
1239 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1240 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1242 MoveSelectionHandle( HandleTwo, gesture.displacement );
1244 mState = StateDraggingHandle;
1250 case Gesture::Finished:
1252 // Revert back to non-pressed selection handle images
1253 if (actor == mGrabArea)
1255 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1256 SetCursorVisibility( true );
1257 SetUpPopupSelection();
1260 if (actor == mHandleOneGrabArea)
1262 // the displacement in PanGesture is affected by the actor's rotation.
1263 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1264 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1266 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1268 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1270 ShowPopupCutCopyPaste();
1272 if (actor == mHandleTwoGrabArea)
1274 // the displacement in PanGesture is affected by the actor's rotation.
1275 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1276 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1278 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1280 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1282 ShowPopupCutCopyPaste();
1291 // Stop the flashing animation so easy to see when moved.
1292 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1294 if (touch.GetPoint(0).state == TouchPoint::Down)
1296 SetCursorVisibility( true );
1297 StopCursorBlinkTimer();
1299 else if (touch.GetPoint(0).state == TouchPoint::Up)
1301 SetCursorVisibility( true );
1302 StartCursorBlinkTimer();
1307 // selection handle one
1308 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1310 if (touch.GetPoint(0).state == TouchPoint::Down)
1312 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1314 else if (touch.GetPoint(0).state == TouchPoint::Up)
1316 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1321 // selection handle two
1322 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1324 if (touch.GetPoint(0).state == TouchPoint::Down)
1326 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1328 else if (touch.GetPoint(0).state == TouchPoint::Up)
1330 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1335 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1337 // If text exists then select nearest word.
1338 if ( !mStyledText.empty())
1342 ShowGrabHandleAndSetVisibility( false );
1347 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1348 // converts the pre-edit word being displayed to a committed word.
1349 if ( !mUnderlinedPriorToPreEdit )
1352 style.SetUnderline( false );
1353 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1355 mPreEditFlag = false;
1356 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1357 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1358 PreEditReset( false );
1360 mCursorPosition = 0;
1362 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1363 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1365 std::size_t start = 0;
1366 std::size_t end = 0;
1367 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1369 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1371 ImfManager imfManager = ImfManager::Get();
1374 imfManager.SetCursorPosition ( mCursorPosition );
1375 imfManager.NotifyCursorPosition();
1378 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1380 SelectText( start, end );
1381 ShowPopupCutCopyPaste();
1385 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1386 HidePopup( false ); // Hide popup with setting to do auto show.
1387 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1391 else if ( mClipboard && mClipboard.NumberOfItems() )
1393 ShowPopupCutCopyPaste();
1396 // If no text and clipboard empty then do nothing
1399 // TODO: Change the function name to be more general.
1400 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1402 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1403 , (mEditOnTouch)?"true":"false"
1404 , (mEditModeActive)?"true":"false");
1406 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1411 if( mGrabArea == actor )
1413 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1415 SetUpPopupSelection();
1425 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1427 // Initially don't create the grab handle.
1428 bool createGrabHandle = false;
1430 if ( !mEditModeActive )
1432 // update line height before calculate the actual position.
1435 // Only start edit mode if TextInput configured to edit on touch
1438 // Set the initial cursor position in the tap point.
1439 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1445 // Show the keyboard if it was hidden.
1446 if (!VirtualKeyboard::IsVisible())
1448 VirtualKeyboard::Show();
1451 // Reset keyboard as tap event has occurred.
1452 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1453 PreEditReset( true );
1455 GetTextLayoutInfo();
1457 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1459 // 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.
1461 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1463 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1465 // Notify keyboard so it can 're-capture' word for predictive text.
1466 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1467 ImfManager imfManager = ImfManager::Get();
1470 imfManager.SetCursorPosition ( mCursorPosition );
1471 imfManager.NotifyCursorPosition();
1473 const TextStyle oldInputStyle( mInputStyle );
1475 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1479 // Create the grab handle.
1480 // Grab handle is created later.
1481 createGrabHandle = true;
1483 if( oldInputStyle != mInputStyle )
1485 // Updates the line height accordingly with the input style.
1488 EmitStyleChangedSignal();
1493 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1494 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1495 // otherwise the Grab handle will be shown when selecting.
1496 if ( createGrabHandle && IsGrabHandleEnabled() )
1498 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1499 bool altPositionValid; // Alternate cursor validity flag.
1500 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1501 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1503 if( altPositionValid )
1505 // Check which of the positions is the closest.
1506 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1508 cursorPosition = altPosition;
1514 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1515 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1516 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1517 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1522 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1524 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1526 // Ignore longpress if in selection mode already
1527 if( mHighlightMeshActor )
1532 if(longPress.state == Dali::Gesture::Started)
1534 // Start edit mode on long press
1535 if ( !mEditModeActive )
1540 // If text exists then select nearest word.
1541 if ( !mStyledText.empty())
1545 ShowGrabHandleAndSetVisibility( false );
1550 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1551 // converts the pre-edit word being displayed to a committed word.
1552 if ( !mUnderlinedPriorToPreEdit )
1555 style.SetUnderline( false );
1556 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1558 mPreEditFlag = false;
1559 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1560 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1561 PreEditReset( false );
1563 mCursorPosition = 0;
1565 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1566 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1568 std::size_t start = 0;
1569 std::size_t end = 0;
1570 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1572 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1574 ImfManager imfManager = ImfManager::Get();
1577 imfManager.SetCursorPosition ( mCursorPosition );
1578 imfManager.NotifyCursorPosition();
1581 SelectText( start, end );
1584 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1585 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1587 ShowPopupCutCopyPaste();
1592 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1594 const Text clipboardText( notifier.GetContent() );
1595 PasteText( clipboardText );
1597 SetCursorVisibility( true );
1598 StartCursorBlinkTimer();
1600 ShowGrabHandleAndSetVisibility( false );
1606 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1608 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1610 const std::string& name = button.GetName();
1612 if(name == TextInputPopup::OPTION_SELECT_WORD)
1614 std::size_t start = 0;
1615 std::size_t end = 0;
1616 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1618 SelectText( start, end );
1620 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1622 SetCursorVisibility(false);
1623 StopCursorBlinkTimer();
1625 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1626 std::size_t start = 0;
1628 SelectText( start, end );
1630 else if(name == TextInputPopup::OPTION_CUT)
1632 bool ret = CopySelectedTextToClipboard();
1636 DeleteHighlightedText( true );
1640 SetCursorVisibility( true );
1641 StartCursorBlinkTimer();
1645 else if(name == TextInputPopup::OPTION_COPY)
1647 CopySelectedTextToClipboard();
1651 SetCursorVisibility( true );
1652 StartCursorBlinkTimer();
1656 else if(name == TextInputPopup::OPTION_PASTE)
1658 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1660 PasteText(retrievedString);
1662 SetCursorVisibility( true );
1663 StartCursorBlinkTimer();
1665 ShowGrabHandleAndSetVisibility( false );
1669 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1671 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1672 // Hence pass the false parameter for signalFinished.
1673 HidePopup( true, false );
1674 mClipboard.ShowClipboard();
1680 bool TextInput::OnCursorBlinkTimerTick()
1683 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1684 if ( mCursorRTLEnabled )
1686 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1688 mCursorBlinkStatus = !mCursorBlinkStatus;
1693 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1695 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1697 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1698 if(mHighlightMeshActor && mState == StateEdit)
1700 ShowPopupCutCopyPaste();
1704 //FIXME this routine needs to be re-written as it contains too many branches.
1705 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1707 std::string keyName = event.keyPressedName;
1708 std::string keyString = event.keyPressed;
1710 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1712 // Do not consume "Tab" and "Escape" keys.
1713 if(keyName == "Tab" || keyName == "Escape")
1715 // Escape key to end the edit mode
1721 HidePopup(); // If Pop-up shown then hides it as editing text.
1723 // Update Flag, indicates whether to update the text-input contents or not.
1724 // Any key stroke that results in a visual change of the text-input should
1725 // set this flag to true.
1728 // Whether to scroll text to cursor position.
1729 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1730 bool scroll = false;
1732 if (keyName == "Return")
1734 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1736 bool preEditFlagPreviouslySet( mPreEditFlag );
1738 // replaces highlighted text with new line
1739 DeleteHighlightedText( false );
1741 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1743 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1744 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1747 mCommitByKeyInput = true;
1750 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1751 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1753 mPreEditFlag = true;
1754 mIgnoreCommitFlag = false;
1764 else if ( keyName == "space" )
1766 if ( mHighlightMeshActor )
1768 // Some text is selected so erase it before adding space.
1769 DeleteHighlightedText( true );
1772 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1774 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1775 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1778 mCommitByKeyInput = true;
1783 else if (keyName == "BackSpace")
1785 if ( mHighlightMeshActor )
1787 // Some text is selected so erase it
1788 DeleteHighlightedText( true );
1793 if ( mCursorPosition > 0 )
1795 DeleteCharacter( mCursorPosition );
1801 else if (keyName == "Right")
1806 else if (keyName == "Left")
1808 AdvanceCursor(true);
1811 else // event is a character
1813 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1814 if ( !keyString.empty() )
1816 // replaces highlighted text with new character
1817 DeleteHighlightedText( false );
1819 // Received key String
1820 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1826 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1827 // as this is a costly operation.
1833 if(update || scroll)
1835 if( IsScrollEnabled() )
1837 // Calculates the new cursor position (in actor coordinates)
1838 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1840 ScrollTextViewToMakeCursorVisible( cursorPosition );
1847 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1849 std::string keyName = event.keyPressedName;
1850 std::string keyString = event.keyPressed;
1852 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1854 // The selected text become deselected when the key code is DALI_KEY_BACK.
1855 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1864 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1865 const Vector3& cursorPositionTwo,
1866 bool altPositionValidOne,
1867 bool altPositionValidTwo,
1868 const Vector3& altPositionOne,
1869 const Vector3& altPositionTwo )
1871 // TODO VCC Valid for one line.
1872 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1873 if( cursorPositionOne != cursorPositionTwo )
1875 if( cursorPositionOne.x < cursorPositionTwo.x )
1877 mSelectionHandleOneActualPosition = cursorPositionOne;
1878 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1882 mSelectionHandleOneActualPosition = cursorPositionTwo;
1883 mSelectionHandleTwoActualPosition = cursorPositionOne;
1888 mSelectionHandleOneActualPosition = cursorPositionOne;
1889 if( altPositionValidOne )
1891 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1893 mSelectionHandleOneActualPosition = altPositionOne;
1896 if( altPositionValidTwo )
1898 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1900 mSelectionHandleOneActualPosition = altPositionTwo;
1904 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1905 if( altPositionValidTwo )
1907 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1909 mSelectionHandleTwoActualPosition = altPositionTwo;
1912 if( altPositionValidOne )
1914 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1916 mSelectionHandleTwoActualPosition = altPositionOne;
1922 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1924 // Updates the stored scroll position.
1925 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1927 const Vector3& controlSize = GetControlSize();
1928 Size cursorSize( CURSOR_THICKNESS, 0.f );
1930 // Updates the cursor and grab handle position and visibility.
1931 if( mGrabHandle || mCursor )
1933 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1935 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1936 bool altPositionValid; // Alternate cursor validity flag.
1937 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1938 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1940 if( altPositionValid )
1942 // Check which of the positions is the closest.
1943 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1945 cursorPosition = altPosition;
1949 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1951 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1955 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1956 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1961 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1962 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1966 // Updates the selection handles and highlighted text position and visibility.
1967 if( mSelectionHandleOne && mSelectionHandleTwo )
1969 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1970 bool altPositionValidOne; // Alternate cursor validity flag.
1971 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1972 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1974 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1975 bool altPositionValidTwo; // Alternate cursor validity flag.
1976 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1977 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1979 // VCC TODO: This method is a hack for one line.
1980 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1982 altPositionValidOne,
1983 altPositionValidTwo,
1987 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1988 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1989 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1990 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1992 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1993 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1994 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1995 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1997 if( mHighlightMeshActor )
1999 mHighlightMeshActor.SetVisible( true );
2005 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2007 // Scroll the text to make the cursor visible.
2008 const Size cursorSize( CURSOR_THICKNESS,
2009 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2011 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2013 const Vector3& controlSize = GetControlSize();
2015 // Calculates the new scroll position.
2016 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2017 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2019 scrollOffset.x += cursorPosition.x;
2022 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2024 scrollOffset.y += cursorPosition.y;
2027 // Sets the new scroll position.
2028 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2029 SetScrollPosition( scrollOffset );
2032 void TextInput::StartScrollTimer()
2036 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2037 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2040 if( !mScrollTimer.IsRunning() )
2042 mScrollTimer.Start();
2046 void TextInput::StopScrollTimer()
2050 mScrollTimer.Stop();
2054 bool TextInput::OnScrollTimerTick()
2056 // TODO: need to set the new style accordingly the new handle position.
2058 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2060 // nothing to do if all handles are invisible or doesn't exist.
2066 // Choose between the grab handle or the selection handles.
2067 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2068 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2069 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2071 std::size_t newCursorPosition = 0;
2072 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2074 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2075 // the new selection handle's position needs to be different of the other one.
2076 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2077 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2078 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2080 if( differentSelectionHandles )
2082 handlePosition = newCursorPosition;
2084 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2086 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2088 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2089 scrollPosition += scrollDelta;
2090 SetScrollPosition( scrollPosition );
2092 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2097 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2100 actualHandlePosition.x += mScrollDisplacement.x;
2101 actualHandlePosition.y += mScrollDisplacement.y;
2106 // Public Internal Methods (public for testing purpose)
2108 void TextInput::SetUpTouchEvents()
2110 if ( !mTapDetector )
2112 mTapDetector = TapGestureDetector::New();
2113 // Attach the actors and connect the signal
2114 mTapDetector.Attach(Self());
2116 // As contains children which may register for tap the default control detector is not used.
2117 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2120 if ( !mDoubleTapDetector )
2122 mDoubleTapDetector = TapGestureDetector::New();
2123 mDoubleTapDetector.SetTapsRequired( 2 );
2124 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2126 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2127 // so that we do not, unnecessarily, have a double tap request all the time
2130 if ( !mPanGestureDetector )
2132 mPanGestureDetector = PanGestureDetector::New();
2133 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2136 if ( !mLongPressDetector )
2138 mLongPressDetector = LongPressGestureDetector::New();
2139 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2140 mLongPressDetector.Attach(Self());
2144 void TextInput::CreateTextViewActor()
2146 mDisplayedTextView = Toolkit::TextView::New();
2147 mDisplayedTextView.SetName( "DisplayedTextView ");
2148 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2149 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2150 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2151 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2152 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2153 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2154 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2155 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2156 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2157 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2159 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2161 Self().Add( mDisplayedTextView );
2164 // Start a timer to initiate, used by the cursor to blink.
2165 void TextInput::StartCursorBlinkTimer()
2167 if ( !mCursorBlinkTimer )
2169 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2170 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2173 if ( !mCursorBlinkTimer.IsRunning() )
2175 mCursorBlinkTimer.Start();
2179 // Start a timer to initiate, used by the cursor to blink.
2180 void TextInput::StopCursorBlinkTimer()
2182 if ( mCursorBlinkTimer )
2184 mCursorBlinkTimer.Stop();
2188 void TextInput::StartEditMode()
2190 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2192 if(!mEditModeActive)
2197 if ( mDoubleTapDetector )
2199 mDoubleTapDetector.Attach( Self() );
2203 void TextInput::EndEditMode()
2205 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2207 ClearKeyInputFocus();
2209 if ( mDoubleTapDetector )
2211 mDoubleTapDetector.Detach( Self() );
2215 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2217 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2219 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2221 style.SetUnderline( true );
2222 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2226 void TextInput::RemovePreEditStyle()
2228 if ( !mUnderlinedPriorToPreEdit )
2231 style.SetUnderline( false );
2232 SetActiveStyle( style, TextStyle::UNDERLINE );
2236 // IMF related methods
2239 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2241 bool update( false );
2242 bool preeditResetRequired ( false );
2244 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2246 HidePopup(); // If Pop-up shown then hides it as editing text.
2249 switch ( imfEvent.eventName )
2251 case ImfManager::PREEDIT:
2253 mIgnoreFirstCommitFlag = false;
2255 // 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
2256 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2258 // replaces highlighted text with new character
2259 DeleteHighlightedText( false );
2262 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2264 if( IsScrollEnabled() )
2266 // Calculates the new cursor position (in actor coordinates)
2267 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2268 ScrollTextViewToMakeCursorVisible( cursorPosition );
2275 case ImfManager::COMMIT:
2277 if( mIgnoreFirstCommitFlag )
2279 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2280 mIgnoreFirstCommitFlag = false;
2284 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2286 // 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
2287 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2289 // replaces highlighted text with new character
2290 DeleteHighlightedText( false );
2293 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2294 // not needed, one such scenario is when the pre-edit word is too long to fit.
2295 if ( !mIgnoreCommitFlag )
2297 update = CommitReceived( imfEvent.predictiveString );
2301 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2307 if( IsScrollEnabled() )
2309 // Calculates the new cursor position (in actor coordinates)
2310 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2312 ScrollTextViewToMakeCursorVisible( cursorPosition );
2317 case ImfManager::DELETESURROUNDING:
2319 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2320 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2322 mPreEditFlag = false;
2324 std::size_t toDelete = 0;
2325 std::size_t numberOfCharacters = 0;
2327 if( mHighlightMeshActor )
2329 // delete highlighted text.
2330 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2331 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2335 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2337 toDelete = mCursorPosition + imfEvent.cursorOffset;
2339 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2341 numberOfCharacters = mStyledText.size() - toDelete;
2345 numberOfCharacters = imfEvent.numberOfChars;
2348 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2349 DeleteRange( toDelete, numberOfCharacters );
2351 mCursorPosition = toDelete;
2352 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2356 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2359 case ImfManager::GETSURROUNDING:
2361 // 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
2362 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2363 if (! ( mHighlightMeshActor || mSelectingText ) )
2365 std::string text( GetText() );
2366 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2368 imfManager.SetCursorPosition( mCursorPosition );
2369 imfManager.SetSurroundingText( text );
2372 if( 0 != mNumberOfSurroundingCharactersDeleted )
2374 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2375 mNumberOfSurroundingCharactersDeleted = 0;
2377 if( mStyledText.empty() )
2379 ShowPlaceholderText( mStyledPlaceHolderText );
2384 case ImfManager::VOID:
2386 DALI_ASSERT_DEBUG( false );
2390 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2392 return callbackData;
2395 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2397 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2399 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2400 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2402 bool preeditResetRequest ( false );
2404 if( mPreEditFlag ) // Already in pre-edit state.
2406 if( mStyledText.size() >= mMaxStringLength )
2408 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2409 // Cannot fit these characters into field, clear pre-edit.
2410 if ( !mUnderlinedPriorToPreEdit )
2413 style.SetUnderline( false );
2414 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2416 mIgnoreCommitFlag = true;
2417 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2418 mPreEditFlag = false;
2419 EmitMaxInputCharactersReachedSignal();
2423 // delete existing pre-edit string
2424 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2426 // Store new pre-edit string
2427 mPreEditString.SetText( keyString );
2429 if ( keyString.empty() )
2431 mPreEditFlag = false;
2432 mCursorPosition = mPreEditStartPosition;
2434 if( mStyledText.empty() )
2436 ShowPlaceholderText( mStyledPlaceHolderText );
2440 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2443 GetTextLayoutInfo();
2448 // Insert new pre-edit string. InsertAt updates the size and position table.
2449 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2450 // 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.
2451 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2452 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2453 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2456 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2460 else // mPreEditFlag not set
2462 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2464 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2465 // new pre-edit so move into pre-edit state by setting flag
2466 mPreEditFlag = true;
2467 mPreEditString.SetText( keyString ); // store new pre-edit string
2468 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2469 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2470 // 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.
2471 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2472 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2473 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2474 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2480 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2484 return preeditResetRequest;
2487 bool TextInput::CommitReceived(const std::string& keyString )
2489 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2490 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2492 bool update( false );
2494 RemovePreEditStyle();
2496 const std::size_t styledTextSize( mStyledText.size() );
2497 if( styledTextSize >= mMaxStringLength )
2499 // Cannot fit these characters into field, clear pre-edit.
2502 mIgnoreCommitFlag = true;
2503 mPreEditFlag = false;
2505 EmitMaxInputCharactersReachedSignal();
2511 // delete existing pre-edit string
2512 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2513 mPreEditFlag = false;
2515 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2516 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2518 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2520 // No need to update cursor position as Cursor location given by touch.
2521 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2522 mPreserveCursorPosition = false;
2526 // Cursor not set by touch so needs to be re-positioned to input more text
2527 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2529 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2530 if ( mCommitByKeyInput )
2532 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2533 mCommitByKeyInput = false;
2539 if ( mSelectTextOnCommit )
2541 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2546 else // mPreEditFlag not set
2548 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2550 if( mStyledText.empty() && mPlaceHolderSet )
2552 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2553 mDisplayedTextView.SetText( "" );
2554 mNumberOfSurroundingCharactersDeleted = 0;
2555 mPlaceHolderSet = false;
2557 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2559 mNumberOfSurroundingCharactersDeleted = 0;
2564 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2569 mSelectTextOnCommit = false;
2571 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2572 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2577 // End of IMF related methods
2579 std::size_t TextInput::DeletePreEdit()
2581 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2583 DALI_ASSERT_DEBUG( mPreEditFlag );
2585 const std::size_t preEditStringLength = mPreEditString.GetLength();
2586 const std::size_t styledTextSize = mStyledText.size();
2588 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2590 // Prevents erase items outside mStyledText bounds.
2591 if( mPreEditStartPosition > styledTextSize )
2593 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2594 mPreEditStartPosition = styledTextSize;
2597 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2599 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2600 endPosition = styledTextSize;
2603 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2605 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2606 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2608 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2610 return preEditStringLength;
2613 void TextInput::PreEditReset( bool preserveCursorPosition )
2615 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2616 preserveCursorPosition, mCursorPosition);
2618 // 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.
2619 mPreserveCursorPosition = preserveCursorPosition;
2621 // Reset incase we are in a pre-edit state.
2622 ImfManager imfManager = ImfManager::Get();
2625 imfManager.Reset(); // Will trigger a commit message
2629 void TextInput::CursorUpdate()
2633 ImfManager imfManager = ImfManager::Get();
2636 std::string text( GetText() );
2637 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2638 imfManager.SetCursorPosition ( mCursorPosition );
2639 imfManager.NotifyCursorPosition();
2643 /* Delete highlighted characters redisplay*/
2644 void TextInput::DeleteHighlightedText( bool inheritStyle )
2646 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2648 if( mHighlightMeshActor )
2650 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2652 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2653 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2655 // Get the styled text of the characters to be deleted as it may be needed if
2656 // the "exceed the text-input's boundaries" option is disabled.
2657 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2659 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2661 mStyledText.erase( start, end ); // erase range of characters
2663 // Remove text from TextView and update place holder text if required
2665 // Set the placeholder text only if the styled text is empty.
2666 if( mStyledText.empty() )
2668 ShowPlaceholderText( mStyledPlaceHolderText );
2672 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2674 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2676 // It may happen than after removing a white space or a new line character,
2677 // two words merge, this new word could be big enough to not fit in its
2678 // current line, so moved to the next one, and make some part of the text to
2679 // exceed the text-input's boundary.
2680 if( !mExceedEnabled )
2682 // Get the new text layout after removing some characters.
2683 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2685 // Get text-input's size.
2686 const Vector3& size = GetControlSize();
2688 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2689 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2691 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2693 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2694 styledCharactersToDelete.begin(),
2695 styledCharactersToDelete.end() );
2699 GetTextLayoutInfo();
2707 const TextStyle oldInputStyle( mInputStyle );
2709 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2711 if( oldInputStyle != mInputStyle )
2713 // Updates the line height accordingly with the input style.
2716 EmitStyleChangedSignal();
2722 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2724 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2725 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2727 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2730 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2732 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2733 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2735 mStyledText.erase(itStart, itEnd);
2737 // update the selection handles if they are visible.
2738 if( mHighlightMeshActor )
2740 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2741 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2743 if( minHandle >= start + ncharacters )
2745 minHandle -= ncharacters;
2747 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2752 if( maxHandle >= start + ncharacters )
2754 maxHandle -= ncharacters;
2756 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2762 // 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.
2765 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2767 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2768 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2769 // Mean we do not re-draw the text more than we have too.
2772 /* Delete character at current cursor position and redisplay*/
2773 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2775 // Ensure positionToDelete is not out of bounds.
2776 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2777 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2778 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2780 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2783 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2785 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2787 // Get the styled text of the character to be deleted as it may be needed if
2788 // the "exceed the text-input's boundaries" option is disabled.
2789 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2791 mStyledText.erase(it); // erase the character left of positionToDelete
2793 if( mStyledText.empty() )
2795 ShowPlaceholderText( mStyledPlaceHolderText );
2799 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2801 const Character characterToDelete = styledCharacterToDelete.mText[0];
2803 // It may happen than after removing a white space or a new line character,
2804 // two words merge, this new word could be big enough to not fit in its
2805 // current line, so moved to the next one, and make some part of the text to
2806 // exceed the text-input's boundary.
2807 if( !mExceedEnabled )
2809 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2811 // Get the new text layout after removing one character.
2812 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2814 // Get text-input's size.
2815 const Vector3& size = GetControlSize();
2817 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2818 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2820 MarkupProcessor::StyledTextArray array;
2821 array.push_back( styledCharacterToDelete );
2822 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2824 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2829 GetTextLayoutInfo();
2831 ShowGrabHandleAndSetVisibility( false );
2833 mCursorPosition = positionToDelete -1;
2835 const TextStyle oldInputStyle( mInputStyle );
2837 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2839 if( oldInputStyle != mInputStyle )
2841 // Updates the line height accordingly with the input style.
2844 EmitStyleChangedSignal();
2849 /*Insert new character into the string and (optionally) redisplay text-input*/
2850 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2852 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2854 // Ensure insertionPosition is not out of bounds.
2855 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2857 bool textExceedsMaximunNumberOfCharacters = false;
2858 bool textExceedsBoundary = false;
2859 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2861 ShowGrabHandleAndSetVisibility( false );
2863 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2867 mIgnoreCommitFlag = true;
2868 mPreEditFlag = false;
2869 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2870 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2873 if( textExceedsMaximunNumberOfCharacters )
2875 EmitMaxInputCharactersReachedSignal();
2878 if( textExceedsBoundary )
2880 EmitInputTextExceedsBoundariesSignal();
2881 PreEditReset( false );
2885 return insertedStringLength;
2888 ImageActor TextInput::CreateCursor( const Vector4& color)
2891 cursor = CreateSolidColorActor(color);
2892 cursor.SetName( "Cursor" );
2894 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2895 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2896 cursor.SetVisible(false);
2901 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2903 // As cursor is not moving due to grab handle, handle should be hidden.
2904 ShowGrabHandleAndSetVisibility( false );
2906 bool cursorPositionChanged = false;
2909 if ( mCursorPosition >= places )
2911 mCursorPosition = mCursorPosition - places;
2912 cursorPositionChanged = true;
2917 if ((mCursorPosition + places) <= mStyledText.size())
2919 mCursorPosition = mCursorPosition + places;
2920 cursorPositionChanged = true;
2924 if( cursorPositionChanged )
2926 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2928 const TextStyle oldInputStyle( mInputStyle );
2929 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2933 if( oldInputStyle != mInputStyle )
2935 // Updates the line height accordingly with the input style.
2938 EmitStyleChangedSignal();
2941 ImfManager imfManager = ImfManager::Get();
2944 imfManager.SetCursorPosition ( mCursorPosition );
2945 imfManager.NotifyCursorPosition();
2950 void TextInput::DrawCursor()
2952 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2954 // Get height of cursor and set its size
2955 Size size( CURSOR_THICKNESS, 0.0f );
2956 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2958 size.height = rowRect.height;
2962 // Measure Font so know how big text will be if no initial text to measure.
2963 size.height = mLineHeight;
2966 mCursor.SetSize(size);
2968 // If the character is italic then the cursor also tilts.
2969 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2971 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2973 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2975 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2976 bool altPositionValid; // Alternate cursor validity flag.
2977 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2978 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2980 SetAltCursorEnabled( altPositionValid );
2982 if( !altPositionValid )
2984 mCursor.SetPosition( position + UI_OFFSET );
2988 size.height *= 0.5f;
2989 mCursor.SetSize(size);
2990 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2992 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2993 size.height = rowRect.height * 0.5f;
2994 mCursorRTL.SetSize(size);
2995 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2998 if( IsScrollEnabled() )
3000 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3001 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3006 void TextInput::SetAltCursorEnabled( bool enabled )
3008 mCursorRTLEnabled = enabled;
3009 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3012 void TextInput::SetCursorVisibility( bool visible )
3014 mCursorVisibility = visible;
3015 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3016 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3019 void TextInput::CreateGrabHandle( Dali::Image image )
3025 mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3029 mGrabHandleImage = image;
3032 mGrabHandle = ImageActor::New(mGrabHandleImage);
3033 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3034 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3036 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3038 ShowGrabHandleAndSetVisibility( false );
3040 CreateGrabArea( mGrabHandle );
3042 mActiveLayer.Add(mGrabHandle);
3046 void TextInput::CreateGrabArea( Actor& parent )
3048 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3049 mGrabArea.SetName( "GrabArea" );
3050 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3051 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3052 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3053 mTapDetector.Attach( mGrabArea );
3054 mPanGestureDetector.Attach( mGrabArea );
3055 mLongPressDetector.Attach( mGrabArea );
3057 parent.Add(mGrabArea);
3060 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3062 Vector3 actualHandlePosition;
3066 mActualGrabHandlePosition.x += displacement.x;
3067 mActualGrabHandlePosition.y += displacement.y;
3069 // Grab handle should jump to the nearest character and take cursor with it
3070 std::size_t newCursorPosition = 0;
3071 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3073 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3074 bool altPositionValid; // Alternate cursor validity flag.
3075 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3076 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3078 if( altPositionValid )
3080 // Check which of the positions is the closest.
3081 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3083 actualHandlePosition = altPosition;
3087 bool handleVisible = true;
3089 if( IsScrollEnabled() )
3091 const Vector3 controlSize = GetControlSize();
3092 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3093 // Scrolls the text if the handle is not in a visible position
3094 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3101 mCurrentHandlePosition = actualHandlePosition;
3102 mScrollDisplacement = Vector2::ZERO;
3106 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3108 mScrollDisplacement.x = -SCROLL_SPEED;
3110 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3112 mScrollDisplacement.x = SCROLL_SPEED;
3114 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3116 mScrollDisplacement.y = -SCROLL_SPEED;
3118 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3120 mScrollDisplacement.y = SCROLL_SPEED;
3126 if( handleVisible && // Only redraw cursor and do updates if position changed
3127 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3129 mCursorPosition = newCursorPosition;
3131 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3133 const TextStyle oldInputStyle( mInputStyle );
3135 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3137 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3139 if( oldInputStyle != mInputStyle )
3141 // Updates the line height accordingly with the input style.
3144 EmitStyleChangedSignal();
3149 return actualHandlePosition;
3152 void TextInput::ShowGrabHandle( bool visible )
3154 if ( IsGrabHandleEnabled() )
3158 mGrabHandle.SetVisible( mGrabHandleVisibility );
3160 StartMonitoringStageForTouch();
3164 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3166 mGrabHandleVisibility = visible;
3167 ShowGrabHandle( visible );
3170 // Callbacks connected to be Property notifications for Boundary checking.
3172 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3174 mIsSelectionHandleOneFlipped = true;
3175 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3176 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3179 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3181 mIsSelectionHandleOneFlipped = false;
3182 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3183 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3186 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3188 mIsSelectionHandleTwoFlipped = true;
3189 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3190 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3193 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3195 mIsSelectionHandleTwoFlipped = false;
3196 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3197 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3200 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3201 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3203 mSelectionHandleOne.SetOpacity(0.0f);
3206 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3208 mSelectionHandleOne.SetOpacity(1.0f);
3211 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3213 mSelectionHandleTwo.SetOpacity(0.0f);
3216 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3218 mSelectionHandleTwo.SetOpacity(1.0f);
3221 // End of Callbacks connected to be Property notifications for Boundary checking.
3223 void TextInput::SetUpHandlePropertyNotifications()
3225 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3227 Vector3 handlesize = GetSelectionHandleSize();
3229 // Exceeding horizontal boundary
3230 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3231 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3233 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3234 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3236 // Within horizontal boundary
3237 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3238 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3240 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3241 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3243 // Exceeding vertical boundary
3244 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3245 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3246 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3247 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3249 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3250 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3251 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3252 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3254 // Within vertical boundary
3255 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3256 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3257 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3258 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3260 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3261 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3262 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3263 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3266 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3268 mSelectionHandleOnePosition = start;
3269 mSelectionHandleTwoPosition = end;
3271 if ( !mSelectionHandleOne )
3273 // create normal and pressed images
3274 mSelectionHandleOneImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3275 mSelectionHandleOneImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3277 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3278 mSelectionHandleOne.SetName("SelectionHandleOne");
3279 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3280 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3281 mIsSelectionHandleOneFlipped = false;
3282 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3284 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3285 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3287 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3288 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3290 mTapDetector.Attach( mHandleOneGrabArea );
3291 mPanGestureDetector.Attach( mHandleOneGrabArea );
3293 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3295 mSelectionHandleOne.Add( mHandleOneGrabArea );
3296 mActiveLayer.Add( mSelectionHandleOne );
3299 if ( !mSelectionHandleTwo )
3301 // create normal and pressed images
3302 mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3303 mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3305 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3306 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3307 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3308 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3309 mIsSelectionHandleTwoFlipped = false;
3310 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3312 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3313 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3314 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3315 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3317 mTapDetector.Attach( mHandleTwoGrabArea );
3318 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3320 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3322 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3324 mActiveLayer.Add( mSelectionHandleTwo );
3327 SetUpHandlePropertyNotifications();
3329 // update table as text may have changed.
3330 GetTextLayoutInfo();
3332 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3333 bool altPositionValidOne; // Alternate cursor validity flag.
3334 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3335 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3337 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3338 bool altPositionValidTwo; // Alternate cursor validity flag.
3339 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3340 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3342 // VCC TODO: This method is a hack for one line.
3343 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3345 altPositionValidOne,
3346 altPositionValidTwo,
3350 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3351 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3353 // Calculates and set the visibility if the scroll mode is enabled.
3354 bool isSelectionHandleOneVisible = true;
3355 bool isSelectionHandleTwoVisible = true;
3356 if( IsScrollEnabled() )
3358 const Vector3& controlSize( GetControlSize() );
3359 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3360 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3361 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3362 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3365 CreateHighlight(); // function will only create highlight if not already created.
3368 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3370 Vector3 actualHandlePosition;
3372 if ( mSelectionHandleOne && mSelectionHandleTwo )
3374 const Vector3& controlSize = GetControlSize();
3376 Size cursorSize( CURSOR_THICKNESS, 0.f );
3378 // Get a reference of the wanted selection handle (handle one or two).
3379 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3381 // Get a reference for the current position of the handle and a copy of its pair
3382 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3383 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3385 // Get a handle of the selection handle actor
3386 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3388 // Selection handles should jump to the nearest character
3389 std::size_t newHandlePosition = 0;
3390 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3392 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3393 bool altPositionValid; // Alternate cursor validity flag.
3394 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3395 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3396 if( altPositionValid )
3398 // Check which of the positions is the closest.
3399 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3401 actualHandlePosition = altPosition;
3405 bool handleVisible = true;
3407 if( IsScrollEnabled() )
3409 mCurrentSelectionId = handleId;
3411 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3412 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3413 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3420 mCurrentSelectionHandlePosition = actualHandlePosition;
3421 mScrollDisplacement = Vector2::ZERO;
3425 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3427 mScrollDisplacement.x = -SCROLL_SPEED;
3429 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3431 mScrollDisplacement.x = SCROLL_SPEED;
3433 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3435 mScrollDisplacement.y = -SCROLL_SPEED;
3437 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3439 mScrollDisplacement.y = SCROLL_SPEED;
3445 if ( handleVisible && // Ensure the handle is visible.
3446 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3447 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3449 currentSelectionHandlePosition = newHandlePosition;
3451 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3452 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3456 if ( handleId == HandleOne )
3458 const TextStyle oldInputStyle( mInputStyle );
3460 // Set Active Style to that of first character in selection
3461 if( mSelectionHandleOnePosition < mStyledText.size() )
3463 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3466 if( oldInputStyle != mInputStyle )
3468 // Updates the line height accordingly with the input style.
3471 EmitStyleChangedSignal();
3477 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3480 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3482 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3483 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3485 if ( selectionHandleActor )
3487 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3488 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3489 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3491 if( IsScrollEnabled() )
3493 const Size cursorSize( CURSOR_THICKNESS,
3494 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3495 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3497 GetControlSize() ) );
3502 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3504 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3506 // VCC Set true/false in logical order. TODO : It needs to be checked.
3508 if( startSelection > endSelection )
3510 std::swap( startSelection, endSelection );
3512 std::size_t index = 0u;
3513 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3515 if( ( index < startSelection ) || ( endSelection <= index ) )
3526 // Calculate the dimensions of the quads they will make the highlight mesh
3527 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3529 // At the moment there is no public API to modify the block alignment option.
3531 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3533 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3535 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3536 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3538 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3539 std::vector<bool> selectedVisualText;
3540 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3541 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3542 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3544 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3545 float rowLeft = 0.0f;
3546 float rowRight = 0.0f;
3547 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3548 float maxRowLeft = std::numeric_limits<float>::max();
3549 float maxRowRight = 0.0f;
3551 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3553 // Scan through entire text.
3556 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3558 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3559 bool charSelected = false;
3560 if( selectedIt != selectedEndIt )
3562 charSelected = *selectedIt++;
3565 if( selectionState == SelectionNone )
3569 selectionState = SelectionStarted;
3570 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3571 rowRight = rowLeft + charInfo.mSize.width;
3574 else if( selectionState == SelectionStarted )
3576 // break selection on:
3577 // 1. new line causing selection break. (\n or wordwrap)
3578 // 2. character not selected.
3579 if( !charSelected ||
3580 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3582 // finished selection.
3583 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3584 // that it resides on. That way this enumeration is not necessary.
3586 if(lastIt->mIsNewParagraphChar)
3588 // 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.
3589 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3591 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3592 maxRowLeft = std::min(maxRowLeft, min.x);
3593 maxRowRight = std::max(maxRowRight, max.x);
3594 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3595 float rowTop = rowBottom - rowSize.height;
3597 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3600 rowRight = std::numeric_limits<float>::max();
3602 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3604 selectionState = SelectionNone;
3606 // Still selected? start a new selection
3609 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3611 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3612 selectionState = SelectionStarted;
3617 // build up highlight(s) with this selection data.
3618 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3619 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3626 // If reached end, and still on selection, then close selection.
3629 if(selectionState == SelectionStarted)
3631 // finished selection.
3633 if(lastIt->mIsNewParagraphChar)
3635 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3637 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3638 maxRowLeft = std::min(maxRowLeft, min.x);
3639 maxRowRight = std::max(maxRowRight, max.x);
3640 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3641 float rowTop = rowBottom - rowSize.height;
3642 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3646 // Get the top left and bottom right corners.
3647 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3648 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3649 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3651 // Clamp quads so they appear to clip to borders of the whole text.
3652 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3654 // For block-align align Further Clamp quads to max left and right extents
3655 // BlockAlign: Will adjust highlight to block:
3657 // H[ello] (top row right = max of all rows right)
3658 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3659 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3660 // [text] (bottom row left = min of all rows left)
3661 // (common in SMS messaging selection)
3663 // As opposed to the default which is tight text highlighting.
3668 // (common in regular text editors/web browser selection)
3669 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3671 // Finally clamp quads again so they don't exceed the boundry of the control.
3672 const Vector3& controlSize = GetControlSize();
3673 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3676 return mNewHighlightInfo;
3679 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3680 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3681 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3683 // At the moment there is no public API to modify the block alignment option.
3685 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3687 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3689 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3690 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3692 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3693 std::vector<bool> selectedVisualText;
3694 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3695 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3696 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3698 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3699 float rowLeft = 0.0f;
3700 float rowRight = 0.0f;
3702 // VCC TODO this is valid for one line.
3704 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3706 // Scan through entire text.
3709 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3711 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3712 bool charSelected = false;
3713 if( selectedIt != selectedEndIt )
3715 charSelected = *selectedIt++;
3720 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3721 rowRight = rowLeft + charInfo.mSize.width;
3723 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3724 float rowTop = rowBottom - rowSize.height;
3725 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3731 // Finally clamp quads again so they don't exceed the boundry of the control.
3732 const Vector3& controlSize = GetControlSize();
3733 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3736 return mNewHighlightInfo;
3739 void TextInput::UpdateHighlight()
3741 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3743 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3745 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3746 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3747 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3748 // [BOTTOM] [ MIDDLE ]
3751 // Each quad is created as 2 triangles.
3752 // Middle is just 1 quad regardless of its size.
3766 if ( mHighlightMeshActor )
3768 // vertex and triangle buffers should always be present if MeshActor is alive.
3769 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3770 MeshData::VertexContainer vertices;
3771 Dali::MeshData::FaceIndices faceIndices;
3773 if( !newHighlightInfo.mQuadList.empty() )
3775 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3776 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3778 // vertex position defaults to (0 0 0)
3779 MeshData::Vertex vertex;
3780 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3783 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3785 // Add each quad geometry (a sub-selection) to the mesh data.
3795 QuadCoordinates& quad = *iter;
3797 vertex.x = quad.min.x;
3798 vertex.y = quad.min.y;
3799 vertices.push_back( vertex );
3802 vertex.x = quad.max.x;
3803 vertex.y = quad.min.y;
3804 vertices.push_back( vertex );
3806 // bottom-left (v+2)
3807 vertex.x = quad.min.x;
3808 vertex.y = quad.max.y;
3809 vertices.push_back( vertex );
3811 // bottom-right (v+3)
3812 vertex.x = quad.max.x;
3813 vertex.y = quad.max.y;
3814 vertices.push_back( vertex );
3816 // triangle A (3, 1, 0)
3817 faceIndices.push_back( v + 3 );
3818 faceIndices.push_back( v + 1 );
3819 faceIndices.push_back( v );
3821 // triangle B (0, 2, 3)
3822 faceIndices.push_back( v );
3823 faceIndices.push_back( v + 2 );
3824 faceIndices.push_back( v + 3 );
3826 mMeshData.SetFaceIndices( faceIndices );
3829 BoneContainer bones(0); // passed empty as bones not required
3830 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3831 mHighlightMesh.UpdateMeshData(mMeshData);
3836 void TextInput::ClearPopup()
3838 mPopupPanel.Clear();
3841 void TextInput::AddPopupOptions()
3843 mPopupPanel.AddPopupOptions();
3846 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3848 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3850 Vector3 clampedPosition ( position );
3851 Vector3 tailOffsetPosition ( position );
3853 float xOffSet( 0.0f );
3855 Actor self = Self();
3856 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3858 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3859 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3861 // Clamp to left or right or of boundary
3862 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3864 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3866 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3868 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3871 clampedPosition.x = position.x + xOffSet;
3872 tailOffsetPosition.x = -xOffSet;
3874 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3875 bool flipTail( false );
3877 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3879 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3883 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3884 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3887 void TextInput::HidePopup(bool animate, bool signalFinished )
3889 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3891 mPopupPanel.Hide( animate );
3893 if( animate && signalFinished )
3895 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3900 void TextInput::ShowPopup( bool animate )
3903 Vector2 alternativePopupPosition;
3905 if(mHighlightMeshActor && mState == StateEdit)
3908 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3910 // When text is selected, show popup above top handle (and text), or below bottom handle.
3911 // topHandle: referring to the top most point of the handle or the top line of selection.
3912 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3914 topHandle = mSelectionHandleOneActualPosition;
3915 bottomHandle = mSelectionHandleTwoActualPosition;
3916 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3920 topHandle = mSelectionHandleTwoActualPosition;
3921 bottomHandle = mSelectionHandleOneActualPosition;
3922 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3924 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3925 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3927 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3929 position.x = xPosition;
3931 // Alternative position if no upper space
3932 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3933 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3937 // When no text is selected, show popup at world position of grab handle or cursor
3938 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3939 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3940 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3941 // if can't be positioned above, then position below row.
3942 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3945 // If grab handle enabled then position pop-up below the grab handle.
3946 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3950 SetPopupPosition( position, alternativePopupPosition );
3953 mPopupPanel.Show( Self(), animate );
3954 StartMonitoringStageForTouch();
3956 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3959 void TextInput::ShowPopupCutCopyPaste()
3963 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3964 // Check the selected text is whole text or not.
3965 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3967 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3970 if ( !mStyledText.empty() && IsTextSelected() )
3972 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3973 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3976 if( mClipboard && mClipboard.NumberOfItems() )
3978 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3979 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3984 mPopupPanel.Hide(false);
3988 void TextInput::SetUpPopupSelection( bool showCutButton )
3991 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3992 // If no text exists then don't offer to select
3993 if ( !mStyledText.empty() )
3995 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3996 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3997 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
3999 // if clipboard has valid contents then offer paste option
4000 if( mClipboard && mClipboard.NumberOfItems() )
4002 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4003 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4008 mPopupPanel.Hide(false);
4011 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4016 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4017 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4018 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4019 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4021 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4023 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4025 float closestYdifference = std::numeric_limits<float>::max();
4026 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4027 std::size_t numberOfMatchedCharacters = 0;
4029 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4030 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4032 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4034 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4035 float baselinePosition = info.mPosition.y - info.mDescender;
4037 if( info.mIsVisible )
4039 // store difference between source y point and the y position of the current character
4040 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4042 if( currentYdifference < closestYdifference )
4044 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4045 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4046 closestYdifference = currentYdifference;
4047 matchedCharacters.clear();
4048 numberOfMatchedCharacters = 0; // reset count
4051 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4052 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4054 // ignore new line character.
4055 if( !info.mIsNewParagraphChar )
4057 matchedCharacters.push_back( info );
4058 numberOfMatchedCharacters++;
4062 } // End of loop checking each character's y position in the character layout table
4064 // Check if last character is a newline, if it is
4065 // then need pretend there is an imaginary line afterwards,
4066 // and check if user is touching below previous line.
4067 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4069 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4071 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4075 // 2 Iterate through matching list of y positions and find closest matching X position.
4077 bool matched( false );
4079 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4080 std::size_t visualIndex = 0u;
4081 const std::size_t matchedCharactersSize = matchedCharacters.size();
4082 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4084 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4086 if( info.mIsVisible )
4088 // stop when on left side of character's center.
4089 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4090 if( sourceScrollOffset.x < characterMidPointPosition )
4092 if(info.mIsRightToLeftCharacter)
4094 rightToLeftChar = true;
4096 glyphIntersection = info.mPosition.x;
4101 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4105 if( visualIndex == matchedCharactersSize )
4107 rightToLeftChar = lastRightToLeftChar;
4110 closestIndex = lineOffset + visualIndex;
4112 mClosestCursorPositionEOL = false; // reset
4113 if( ( visualIndex == matchedCharactersSize ) && !matched )
4115 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4118 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4119 if( rightToLeftChar && lastRightToLeftChar )
4121 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4126 // closestIndex is the visual index, need to convert it to the logical index
4127 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4129 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4131 // Checks for situations where user is touching between LTR and RTL
4132 // characters. To identify if the user means the end of a LTR string
4133 // or the beginning of an RTL string, and vice versa.
4134 if( closestIndex > 0 )
4136 if( rightToLeftChar && !lastRightToLeftChar )
4141 // A: In this touch range, the user is indicating that they wish to place
4142 // the cursor at the end of the LTR text.
4143 // B: In this touch range, the user is indicating that they wish to place
4144 // the cursor at the end of the RTL text.
4146 // Result of touching A area:
4147 // [.....LTR]|[RTL......]+
4149 // |: primary cursor (for typing LTR chars)
4150 // +: secondary cursor (for typing RTL chars)
4152 // Result of touching B area:
4153 // [.....LTR]+[RTL......]|
4155 // |: primary cursor (for typing RTL chars)
4156 // +: secondary cursor (for typing LTR chars)
4158 if( sourceScrollOffset.x < glyphIntersection )
4163 else if( !rightToLeftChar && lastRightToLeftChar )
4165 if( sourceScrollOffset.x < glyphIntersection )
4172 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4173 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4174 // one further ahead
4175 if( rightToLeftChar && !lastRightToLeftChar )
4180 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4182 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4184 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4193 float TextInput::GetLineJustificationPosition() const
4195 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4196 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4197 float alignmentOffset = 0.f;
4199 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4200 if( alignment & Toolkit::Alignment::HorizontalLeft )
4202 alignmentOffset = 0.f;
4204 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4206 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4208 else if( alignment & Toolkit::Alignment::HorizontalRight )
4210 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4213 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4214 float justificationOffset = 0.f;
4216 switch( justification )
4218 case Toolkit::TextView::Left:
4220 justificationOffset = 0.f;
4223 case Toolkit::TextView::Center:
4225 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4228 case Toolkit::TextView::Right:
4230 justificationOffset = mTextLayoutInfo.mTextSize.width;
4233 case Toolkit::TextView::Justified:
4235 justificationOffset = 0.f;
4240 DALI_ASSERT_ALWAYS( false );
4244 return alignmentOffset + justificationOffset;
4247 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4249 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4250 A newline character is not inserted in this case */
4252 Vector3 cursorPosition;
4254 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4258 if( characterPosition > 0u )
4260 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4262 // If previous character on a different line then use current characters position
4263 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4265 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4266 if ( mClosestCursorPositionEOL )
4268 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4272 cursorPosition = Vector3( currentCharInfo.mPosition );
4281 // If the character is left to right, the position is the character's position plus its width.
4282 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4284 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4285 cursorPosition.y = currentCharInfo.mPosition.y;
4288 return cursorPosition;
4291 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4293 bool direction = false;
4294 Vector3 alternatePosition;
4295 bool alternatePositionValid = false;
4297 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4300 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4302 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4303 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4304 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4306 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4308 alternatePositionValid = false;
4309 directionRTL = false;
4311 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4313 if( characterPosition == 0u )
4315 // When the cursor position is at the beginning, it should be at the start of the current character.
4316 // If the current character is LTR, then the start is on the right side of the glyph.
4317 // If the current character is RTL, then the start is on the left side of the glyph.
4319 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4321 characterPosition = FindVisibleCharacter( Right, 0u );
4324 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4325 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4327 cursorPosition.x = info.mPosition.x + rtlOffset;
4328 cursorPosition.y = info.mPosition.y;
4329 directionRTL = info.mIsRightToLeftCharacter;
4331 else if( characterPosition > 0u )
4333 // Get the direction of the paragraph.
4334 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4335 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4337 // When cursor is not at beginning, consider possibility of
4338 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4340 // Cursor position should be the end of the last character.
4341 // If the last character is LTR, then the end is on the right side of the glyph.
4342 // If the last character is RTL, then the end is on the left side of the glyph.
4344 --characterPosition;
4346 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4348 characterPosition = FindVisibleCharacter( Left, characterPosition );
4351 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4352 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4354 // VCC TODO : check for a new paragraph character.
4356 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4357 const Vector3& size = GetControlSize();
4359 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4361 --characterPosition;
4363 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4366 if( !info.mIsNewParagraphChar )
4368 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4372 // VCC TODO : check for a new paragraph character.
4374 // When cursor points to first character on new line, position cursor at the start of this glyph.
4375 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4377 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4378 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4380 cursorPosition.x = infoNext.mPosition.x + start;
4381 cursorPosition.y = infoNext.mPosition.y;
4385 // If cursor points to the end of text, then can only position
4386 // cursor where the new line starts based on the line-justification position.
4387 cursorPosition.x = GetLineJustificationPosition();
4389 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4391 // If this is after the last character, then we can assume that the new cursor
4392 // should be exactly one row below the current row.
4394 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4395 cursorPosition.y = info.mPosition.y + rowRect.height;
4399 // If this is not after last character, then we can use this row's height.
4400 // should be exactly one row below the current row.
4402 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4403 cursorPosition.y = info.mPosition.y + rowRect.height;
4408 directionRTL = info.mIsRightToLeftCharacter;
4410 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4412 // 1. When the cursor is neither at the beginning or the end,
4413 // we can show multiple cursors under situations when the cursor is
4414 // between RTL and LTR text...
4415 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4417 std::size_t characterAltPosition = characterPosition + 1u;
4419 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4421 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4423 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4424 // Text: [...LTR...]|[...RTL...]
4426 // Alternate cursor pos: ^
4427 // In which case we need to display an alternate cursor for the RTL text.
4429 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4430 alternatePosition.y = infoAlt.mPosition.y;
4431 alternatePositionValid = true;
4433 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4435 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4436 // Text: |[...RTL...] [...LTR....]
4438 // Alternate cursor pos: ^
4439 // In which case we need to display an alternate cursor for the RTL text.
4441 alternatePosition.x = infoAlt.mPosition.x;
4442 alternatePosition.y = infoAlt.mPosition.y;
4443 alternatePositionValid = true;
4448 // 2. When the cursor is at the end of the text,
4449 // and we have multi-directional text,
4450 // we can also consider showing mulitple cursors.
4451 // The rule here is:
4452 // If first and last characters on row are different
4453 // Directions, then two cursors need to be displayed.
4455 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4457 // The last character's direction is differernt than the first one of current paragraph.
4460 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4462 if(info.mIsRightToLeftCharacter)
4464 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4465 // Text: [...LTR...]|[...RTL...]
4467 // Alternate cursor pos: ^
4468 // In which case we need to display an alternate cursor for the RTL text, this cursor
4469 // should be at the end of the given line.
4471 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4472 alternatePosition.y = infoStart.mPosition.y;
4473 alternatePositionValid = true;
4475 else if(!info.mIsRightToLeftCharacter) // starting RTL
4477 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4478 // Text: |[...RTL...] [...LTR....]
4480 // Alternate cursor pos: ^
4481 // In which case we need to display an alternate cursor for the RTL text.
4483 alternatePosition.x = infoStart.mPosition.x;
4484 alternatePosition.y = infoStart.mPosition.y;
4485 alternatePositionValid = true;
4490 } // characterPosition > 0
4494 // If the character table is void, place the cursor accordingly the text alignment.
4495 const Vector3& size = GetControlSize();
4497 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4498 float alignmentOffset = 0.f;
4500 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4501 if( alignment & Toolkit::Alignment::HorizontalLeft )
4503 alignmentOffset = 0.f;
4505 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4507 alignmentOffset = 0.5f * ( size.width );
4509 else if( alignment & Toolkit::Alignment::HorizontalRight )
4511 alignmentOffset = size.width;
4514 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4515 cursorPosition.x = alignmentOffset;
4517 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4518 if( alignment & Toolkit::Alignment::VerticalTop )
4520 cursorPosition.y = mLineHeight;
4522 else if( alignment & Toolkit::Alignment::VerticalCenter )
4524 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4526 else if( alignment & Toolkit::Alignment::VerticalBottom )
4528 cursorPosition.y = size.height;
4532 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4533 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4535 if( alternatePositionValid )
4537 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4538 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4541 return cursorPosition;
4544 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4546 // scan string from current position to beginning of current line to note direction of line
4547 while( logicalPosition )
4550 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4557 return logicalPosition;
4560 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4562 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4564 while( logicalPosition )
4567 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4574 return logicalPosition;
4577 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4581 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4584 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4586 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4587 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4589 min = Vector2::ZERO;
4590 max = Vector2(0.0f, mLineHeight);
4594 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4596 // Initializes the min and max position.
4597 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4598 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4602 // 1) Find the line where the character is laid-out.
4603 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4604 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4607 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4609 // Index within the whole text to the last character of the current line.
4610 std::size_t lastCharacterOfLine = 0u;
4612 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4613 if( lineNextIt != lineEndIt )
4615 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4619 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4622 // Check if the given chracter position is within the line.
4623 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4625 // 2) Get the row rect of all laid-out characters on the line.
4627 // Need to scan all characters of the line because they are in the logical position.
4628 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4629 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4633 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4635 min.x = std::min( min.x, characterInfo.mPosition.x );
4636 min.y = std::min( min.y, characterInfo.mPosition.y );
4637 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4638 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4645 return Size( max.x - min.x, max.y - min.y );
4648 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4650 Actor popUpPanel = mPopupPanel.GetRootActor();
4652 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4658 Dali::Actor parent( touchedActor.GetParent() );
4662 return WasTouchedCheck( parent );
4669 void TextInput::StartMonitoringStageForTouch()
4671 Stage stage = Stage::GetCurrent();
4672 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4675 void TextInput::EndMonitoringStageForTouch()
4677 Stage stage = Stage::GetCurrent();
4678 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4681 void TextInput::OnStageTouched(const TouchEvent& event)
4683 if( event.GetPointCount() > 0 )
4685 if ( TouchPoint::Down == event.GetPoint(0).state )
4687 const Actor touchedActor(event.GetPoint(0).hitActor);
4689 bool popUpShown( false );
4691 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4696 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4698 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4700 EndMonitoringStageForTouch();
4701 HidePopup( true, false );
4704 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4706 EndMonitoringStageForTouch();
4707 ShowGrabHandleAndSetVisibility( false );
4713 void TextInput::SelectText(std::size_t start, std::size_t end)
4715 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4716 IsGrabHandleEnabled()?"true":"false",
4717 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4718 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4719 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4721 StartMonitoringStageForTouch();
4723 if ( mEditModeActive ) // Only allow text selection when in edit mode
4725 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4726 mSelectingText = true;
4728 std::size_t selectionStartPosition = std::min( start, end );
4730 // Hide grab handle when selecting.
4731 ShowGrabHandleAndSetVisibility( false );
4733 if( start != end ) // something to select
4735 SetCursorVisibility( false );
4736 StopCursorBlinkTimer();
4738 CreateSelectionHandles(start, end);
4741 const TextStyle oldInputStyle( mInputStyle );
4742 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4744 if( oldInputStyle != mInputStyle )
4746 // Updates the line height accordingly with the input style.
4749 EmitStyleChangedSignal();
4755 mSelectingText = false;
4759 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4761 MarkupProcessor::StyledTextArray currentSelectedText;
4763 if ( IsTextSelected() )
4765 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4766 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4768 for(; it != end; ++it)
4770 MarkupProcessor::StyledText& styledText( *it );
4771 currentSelectedText.push_back( styledText );
4774 return currentSelectedText;
4777 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4779 const std::size_t beginIndex = std::min( begin, end );
4780 const std::size_t endIndex = std::max( begin, end );
4783 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4785 // Create a styled text array used to replace the text into the text-view.
4786 MarkupProcessor::StyledTextArray text;
4787 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4789 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4790 GetTextLayoutInfo();
4792 if( IsScrollEnabled() )
4794 // Need to set the scroll position as the text's size may have changed.
4795 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4798 ShowGrabHandleAndSetVisibility( false );
4804 // Set Handle positioning as the new style may have repositioned the characters.
4805 SetSelectionHandlePosition(HandleOne);
4806 SetSelectionHandlePosition(HandleTwo);
4809 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4811 // Just hide the grab handle when keyboard is hidden.
4812 if (!keyboardShown )
4814 ShowGrabHandleAndSetVisibility( false );
4816 // If the keyboard is not now being shown, then hide the popup panel
4817 mPopupPanel.Hide( true );
4821 // Removes highlight and resumes edit mode state
4822 void TextInput::RemoveHighlight( bool hidePopup )
4824 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4826 if ( mHighlightMeshActor )
4828 if ( mSelectionHandleOne )
4830 mActiveLayer.Remove( mSelectionHandleOne );
4831 mSelectionHandleOne.Reset();
4832 mSelectionHandleOneOffset.x = 0.0f;
4834 if ( mSelectionHandleTwo )
4836 mActiveLayer.Remove( mSelectionHandleTwo );
4837 mSelectionHandleTwo.Reset();
4838 mSelectionHandleTwoOffset.x = 0.0f;
4841 mNewHighlightInfo.mQuadList.clear();
4843 Self().Remove( mHighlightMeshActor );
4845 SetCursorVisibility( true );
4846 StartCursorBlinkTimer();
4848 mHighlightMeshActor.Reset();
4849 // NOTE: We cannot dereference mHighlightMesh, due
4850 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4858 mSelectionHandleOnePosition = 0;
4859 mSelectionHandleTwoPosition = 0;
4862 void TextInput::CreateHighlight()
4864 if ( !mHighlightMeshActor )
4866 mMeshData = MeshData( );
4867 mMeshData.SetHasNormals( true );
4869 mCustomMaterial = Material::New("CustomMaterial");
4870 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4872 mMeshData.SetMaterial( mCustomMaterial );
4874 mHighlightMesh = Mesh::New( mMeshData );
4876 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4877 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4878 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4879 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4880 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4881 mHighlightMeshActor.SetAffectedByLighting(false);
4883 Self().Add(mHighlightMeshActor);
4888 bool TextInput::CopySelectedTextToClipboard()
4890 mCurrentCopySelecton.clear();
4892 mCurrentCopySelecton = GetSelectedText();
4894 std::string stringToStore;
4896 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4897 * a marked up string.
4899 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4900 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4902 bool success = mClipboard.SetItem( stringToStore );
4906 void TextInput::PasteText( const Text& text )
4908 // Update Flag, indicates whether to update the text-input contents or not.
4909 // Any key stroke that results in a visual change of the text-input should
4910 // set this flag to true.
4911 bool update = false;
4912 if( mHighlightMeshActor )
4914 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4915 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4917 ImfManager imfManager = ImfManager::Get();
4920 imfManager.SetCursorPosition( mCursorPosition );
4921 imfManager.NotifyCursorPosition();
4923 DeleteHighlightedText( true );
4927 bool textExceedsMaximunNumberOfCharacters = false;
4928 bool textExceedsBoundary = false;
4930 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4932 mCursorPosition += insertedStringLength;
4933 ImfManager imfManager = ImfManager::Get();
4936 imfManager.SetCursorPosition ( mCursorPosition );
4937 imfManager.NotifyCursorPosition();
4940 update = update || ( insertedStringLength > 0 );
4947 if( insertedStringLength < text.GetLength() )
4949 EmitMaxInputCharactersReachedSignal();
4952 if( textExceedsBoundary )
4954 EmitInputTextExceedsBoundariesSignal();
4958 void TextInput::SetTextDirection()
4960 // Put the cursor to the right if we are empty and an RTL language is being used.
4961 if ( mStyledText.empty() )
4963 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4965 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4966 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4968 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4969 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4971 int alignment( mDisplayedTextView.GetTextAlignment() &
4972 ( Toolkit::Alignment::VerticalTop |
4973 Toolkit::Alignment::VerticalCenter |
4974 Toolkit::Alignment::VerticalBottom |
4975 Toolkit::Alignment::HorizontalCenter ) );
4976 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4978 // If our alignment is in the center, then do not change.
4979 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4981 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4984 // If our justification is in the center, then do not change.
4985 if ( justification != Toolkit::TextView::Center )
4987 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4990 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4991 mDisplayedTextView.SetLineJustification( justification );
4995 void TextInput::UpdateLineHeight()
4997 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4998 mLineHeight = font.GetLineHeight();
5000 // 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.
5002 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5004 if( !mExceedEnabled || shrink )
5006 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5010 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5012 // VCC check if we need do this in the visual order ...
5013 std::size_t position = 0u;
5015 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5021 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5023 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5025 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5031 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5032 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5034 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5040 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5045 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5052 void TextInput::SetSortModifier( float depthOffset )
5054 if(mDisplayedTextView)
5056 mDisplayedTextView.SetSortModifier(depthOffset);
5060 void TextInput::SetSnapshotModeEnabled( bool enable )
5062 if(mDisplayedTextView)
5064 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5068 bool TextInput::IsSnapshotModeEnabled() const
5070 bool snapshotEnabled = false;
5072 if(mDisplayedTextView)
5074 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5077 return snapshotEnabled;
5080 void TextInput::SetMarkupProcessingEnabled( bool enable )
5082 mMarkUpEnabled = enable;
5085 bool TextInput::IsMarkupProcessingEnabled() const
5087 return mMarkUpEnabled;
5090 void TextInput::SetScrollEnabled( bool enable )
5092 if( mDisplayedTextView )
5094 mDisplayedTextView.SetScrollEnabled( enable );
5099 // Don't set cursor's and handle's visibility to false if they are outside the
5100 // boundaries of the text-input.
5101 mIsCursorInScrollArea = true;
5102 mIsGrabHandleInScrollArea = true;
5103 if( mSelectionHandleOne && mSelectionHandleTwo )
5105 mSelectionHandleOne.SetVisible( true );
5106 mSelectionHandleTwo.SetVisible( true );
5108 if( mHighlightMeshActor )
5110 mHighlightMeshActor.SetVisible( true );
5116 bool TextInput::IsScrollEnabled() const
5118 bool scrollEnabled = false;
5120 if( mDisplayedTextView )
5122 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5125 return scrollEnabled;
5128 void TextInput::SetScrollPosition( const Vector2& position )
5130 if( mDisplayedTextView )
5132 mDisplayedTextView.SetScrollPosition( position );
5136 Vector2 TextInput::GetScrollPosition() const
5138 Vector2 scrollPosition;
5140 if( mDisplayedTextView )
5142 scrollPosition = mDisplayedTextView.GetScrollPosition();
5145 return scrollPosition;
5148 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5150 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5151 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5152 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5154 // Add style to the new input text.
5155 MarkupProcessor::StyledTextArray textToInsert;
5156 for( std::size_t i = 0; i < insertedStringLength; ++i )
5158 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5159 textToInsert.push_back( newStyledCharacter );
5162 //Insert text to the TextView.
5163 const bool emptyTextView = mStyledText.empty();
5164 if( emptyTextView && mPlaceHolderSet )
5166 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5167 mDisplayedTextView.SetText( textToInsert );
5171 if( 0 == numberOfCharactersToReplace )
5173 mDisplayedTextView.InsertTextAt( position, textToInsert );
5177 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5180 mPlaceHolderSet = false;
5182 if( textToInsert.empty() )
5184 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5185 GetTextLayoutInfo();
5189 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5190 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5193 textExceedsBoundary = false;
5195 if( !mExceedEnabled )
5197 const Vector3& size = GetControlSize();
5199 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5201 // If new text does not fit within TextView
5202 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5203 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5204 GetTextLayoutInfo();
5205 textExceedsBoundary = true;
5206 insertedStringLength = 0;
5209 if( textExceedsBoundary )
5211 // Add the part of the text which fits on the text-input.
5213 // Split the text which doesn't fit in two halves.
5214 MarkupProcessor::StyledTextArray firstHalf;
5215 MarkupProcessor::StyledTextArray secondHalf;
5216 SplitText( textToInsert, firstHalf, secondHalf );
5218 // Clear text. This text will be filled with the text inserted.
5219 textToInsert.clear();
5221 // Where to insert the text.
5222 std::size_t positionToInsert = position;
5224 bool end = text.GetLength() <= 1;
5227 // Insert text and check ...
5228 const std::size_t textLength = firstHalf.size();
5229 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5230 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5232 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5234 // Inserted text doesn't fit.
5236 // Remove inserted text
5237 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5238 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5240 // The iteration finishes when only one character doesn't fit.
5241 end = textLength <= 1;
5245 // Prepare next two halves for next iteration.
5246 MarkupProcessor::StyledTextArray copyText = firstHalf;
5247 SplitText( copyText, firstHalf, secondHalf );
5254 // store text to be inserted in mStyledText.
5255 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5257 // Increase the inserted characters counter.
5258 insertedStringLength += textLength;
5260 // Prepare next two halves for next iteration.
5261 MarkupProcessor::StyledTextArray copyText = secondHalf;
5262 SplitText( copyText, firstHalf, secondHalf );
5264 // Update where next text has to be inserted
5265 positionToInsert += textLength;
5271 if( textToInsert.empty() && emptyTextView )
5273 // No character has been added and the text-view was empty.
5274 // Show the placeholder text.
5275 ShowPlaceholderText( mStyledPlaceHolderText );
5279 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5280 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5281 mPlaceHolderSet = false;
5284 return insertedStringLength;
5287 void TextInput::GetTextLayoutInfo()
5289 if( mStyledText.empty() )
5291 // The text-input has no text, clear the text-view's layout info.
5292 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5296 if( mDisplayedTextView )
5298 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5302 // There is no text-view.
5303 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5308 void TextInput::SetOffsetFromText( const Vector4& offset )
5310 mPopupOffsetFromText = offset;
5313 const Vector4& TextInput::GetOffsetFromText() const
5315 return mPopupOffsetFromText;
5318 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5320 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5324 TextInput& textInputImpl( GetImpl( textInput ) );
5326 switch ( propertyIndex )
5328 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5330 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5333 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5335 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5338 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5340 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5343 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5345 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5348 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5350 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5353 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5355 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5358 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5360 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5363 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5365 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5368 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5370 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5373 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5375 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5378 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5380 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5383 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5385 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5388 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5390 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5393 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5395 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5398 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5400 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5403 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5405 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5411 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5413 Property::Value value;
5415 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5419 TextInput& textInputImpl( GetImpl( textInput ) );
5421 switch ( propertyIndex )
5423 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5425 value = textInputImpl.GetMaterialDiffuseColor();
5428 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5430 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5433 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5435 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5438 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5440 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5443 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5445 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5448 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5450 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5453 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5455 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5458 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5460 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5463 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5465 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5468 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5470 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5473 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5475 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5478 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5480 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5483 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5485 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5488 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5490 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5493 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5495 value = textInputImpl.GetOffsetFromText();
5498 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5500 value = textInputImpl.mCursor.GetCurrentColor();
5507 void TextInput::EmitStyleChangedSignal()
5509 // emit signal if input style changes.
5510 Toolkit::TextInput handle( GetOwner() );
5511 mStyleChangedSignal.Emit( handle, mInputStyle );
5514 void TextInput::EmitTextModified()
5516 // emit signal when text changes.
5517 Toolkit::TextInput handle( GetOwner() );
5518 mTextModifiedSignal.Emit( handle );
5522 void TextInput::EmitMaxInputCharactersReachedSignal()
5524 // emit signal if max characters is reached during text input.
5525 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5527 Toolkit::TextInput handle( GetOwner() );
5528 mMaxInputCharactersReachedSignal.Emit( handle );
5531 void TextInput::EmitInputTextExceedsBoundariesSignal()
5533 // Emit a signal when the input text exceeds the boundaries of the text input.
5535 Toolkit::TextInput handle( GetOwner() );
5536 mInputTextExceedBoundariesSignal.Emit( handle );
5539 } // namespace Internal
5541 } // namespace Toolkit