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>
35 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
36 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
37 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
38 #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::InputSignalV2& TextInput::InputStartedSignal()
544 return mInputStartedSignalV2;
547 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
549 return mInputFinishedSignalV2;
552 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
554 return mCutAndPasteToolBarDisplayedV2;
557 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
559 return mStyleChangedSignalV2;
562 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
564 return mTextModifiedSignal;
567 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
569 return mMaxInputCharactersReachedSignalV2;
572 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
574 return mInputTextExceedBoundariesSignalV2;
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 mInputStartedSignalV2.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.SetRestoreAferFocusLost( 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.SetRestoreAferFocusLost( 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 mInputFinishedSignalV2.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::OnRelaidOut( 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, 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, 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, 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, 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 );
1773 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1775 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1776 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1779 mCommitByKeyInput = true;
1784 else if (keyName == "BackSpace")
1786 if ( mHighlightMeshActor )
1788 // Some text is selected so erase it
1789 DeleteHighlightedText( true );
1794 if ( mCursorPosition > 0 )
1796 DeleteCharacter( mCursorPosition );
1802 else if (keyName == "Right")
1807 else if (keyName == "Left")
1809 AdvanceCursor(true);
1812 else // event is a character
1814 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1815 if ( !keyString.empty() )
1817 // replaces highlighted text with new character
1818 DeleteHighlightedText( false );
1820 // Received key String
1821 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1827 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1828 // as this is a costly operation.
1834 if(update || scroll)
1836 if( IsScrollEnabled() )
1838 // Calculates the new cursor position (in actor coordinates)
1839 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1841 ScrollTextViewToMakeCursorVisible( cursorPosition );
1848 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1850 std::string keyName = event.keyPressedName;
1851 std::string keyString = event.keyPressed;
1853 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1855 // The selected text become deselected when the key code is DALI_KEY_BACK.
1856 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1865 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1866 const Vector3& cursorPositionTwo,
1867 bool altPositionValidOne,
1868 bool altPositionValidTwo,
1869 const Vector3& altPositionOne,
1870 const Vector3& altPositionTwo )
1872 // TODO VCC Valid for one line.
1873 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1874 if( cursorPositionOne != cursorPositionTwo )
1876 if( cursorPositionOne.x < cursorPositionTwo.x )
1878 mSelectionHandleOneActualPosition = cursorPositionOne;
1879 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1883 mSelectionHandleOneActualPosition = cursorPositionTwo;
1884 mSelectionHandleTwoActualPosition = cursorPositionOne;
1889 mSelectionHandleOneActualPosition = cursorPositionOne;
1890 if( altPositionValidOne )
1892 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1894 mSelectionHandleOneActualPosition = altPositionOne;
1897 if( altPositionValidTwo )
1899 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1901 mSelectionHandleOneActualPosition = altPositionTwo;
1905 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1906 if( altPositionValidTwo )
1908 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1910 mSelectionHandleTwoActualPosition = altPositionTwo;
1913 if( altPositionValidOne )
1915 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1917 mSelectionHandleTwoActualPosition = altPositionOne;
1923 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1925 // Updates the stored scroll position.
1926 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1928 const Vector3& controlSize = GetControlSize();
1929 Size cursorSize( CURSOR_THICKNESS, 0.f );
1931 // Updates the cursor and grab handle position and visibility.
1932 if( mGrabHandle || mCursor )
1934 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1936 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1937 bool altPositionValid; // Alternate cursor validity flag.
1938 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1939 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1941 if( altPositionValid )
1943 // Check which of the positions is the closest.
1944 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1946 cursorPosition = altPosition;
1950 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1952 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1956 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1957 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1962 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1963 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1967 // Updates the selection handles and highlighted text position and visibility.
1968 if( mSelectionHandleOne && mSelectionHandleTwo )
1970 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1971 bool altPositionValidOne; // Alternate cursor validity flag.
1972 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1973 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1975 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1976 bool altPositionValidTwo; // Alternate cursor validity flag.
1977 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1978 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1980 // VCC TODO: This method is a hack for one line.
1981 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1983 altPositionValidOne,
1984 altPositionValidTwo,
1988 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1989 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1990 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1991 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1993 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1994 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1995 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1996 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1998 if( mHighlightMeshActor )
2000 mHighlightMeshActor.SetVisible( true );
2006 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2008 // Scroll the text to make the cursor visible.
2009 const Size cursorSize( CURSOR_THICKNESS,
2010 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2012 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2014 const Vector3& controlSize = GetControlSize();
2016 // Calculates the new scroll position.
2017 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2018 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2020 scrollOffset.x += cursorPosition.x;
2023 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2025 scrollOffset.y += cursorPosition.y;
2028 // Sets the new scroll position.
2029 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2030 SetScrollPosition( scrollOffset );
2033 void TextInput::StartScrollTimer()
2037 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2038 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2041 if( !mScrollTimer.IsRunning() )
2043 mScrollTimer.Start();
2047 void TextInput::StopScrollTimer()
2051 mScrollTimer.Stop();
2055 bool TextInput::OnScrollTimerTick()
2057 // TODO: need to set the new style accordingly the new handle position.
2059 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2061 // nothing to do if all handles are invisible or doesn't exist.
2067 // Choose between the grab handle or the selection handles.
2068 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2069 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2070 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2072 std::size_t newCursorPosition = 0;
2073 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2075 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2076 // the new selection handle's position needs to be different of the other one.
2077 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2078 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2079 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2081 if( differentSelectionHandles )
2083 handlePosition = newCursorPosition;
2085 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2087 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2089 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2090 scrollPosition += scrollDelta;
2091 SetScrollPosition( scrollPosition );
2093 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2098 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2101 actualHandlePosition.x += mScrollDisplacement.x;
2102 actualHandlePosition.y += mScrollDisplacement.y;
2107 // Public Internal Methods (public for testing purpose)
2109 void TextInput::SetUpTouchEvents()
2111 if ( !mTapDetector )
2113 mTapDetector = TapGestureDetector::New();
2114 // Attach the actors and connect the signal
2115 mTapDetector.Attach(Self());
2117 // As contains children which may register for tap the default control detector is not used.
2118 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2121 if ( !mDoubleTapDetector )
2123 mDoubleTapDetector = TapGestureDetector::New();
2124 mDoubleTapDetector.SetTapsRequired( 2 );
2125 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2127 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2128 // so that we do not, unnecessarily, have a double tap request all the time
2131 if ( !mPanGestureDetector )
2133 mPanGestureDetector = PanGestureDetector::New();
2134 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2137 if ( !mLongPressDetector )
2139 mLongPressDetector = LongPressGestureDetector::New();
2140 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2141 mLongPressDetector.Attach(Self());
2145 void TextInput::CreateTextViewActor()
2147 mDisplayedTextView = Toolkit::TextView::New();
2148 mDisplayedTextView.SetName( "DisplayedTextView ");
2149 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2150 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2151 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2152 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2153 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2154 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2155 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2156 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2157 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2158 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2160 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2162 Self().Add( mDisplayedTextView );
2165 // Start a timer to initiate, used by the cursor to blink.
2166 void TextInput::StartCursorBlinkTimer()
2168 if ( !mCursorBlinkTimer )
2170 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2171 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2174 if ( !mCursorBlinkTimer.IsRunning() )
2176 mCursorBlinkTimer.Start();
2180 // Start a timer to initiate, used by the cursor to blink.
2181 void TextInput::StopCursorBlinkTimer()
2183 if ( mCursorBlinkTimer )
2185 mCursorBlinkTimer.Stop();
2189 void TextInput::StartEditMode()
2191 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2193 if(!mEditModeActive)
2198 if ( mDoubleTapDetector )
2200 mDoubleTapDetector.Attach( Self() );
2204 void TextInput::EndEditMode()
2206 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2208 ClearKeyInputFocus();
2210 if ( mDoubleTapDetector )
2212 mDoubleTapDetector.Detach( Self() );
2216 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2218 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2220 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2222 style.SetUnderline( true );
2223 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2227 void TextInput::RemovePreEditStyle()
2229 if ( !mUnderlinedPriorToPreEdit )
2232 style.SetUnderline( false );
2233 SetActiveStyle( style, TextStyle::UNDERLINE );
2237 // IMF related methods
2240 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2242 bool update( false );
2243 bool preeditResetRequired ( false );
2245 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2247 HidePopup(); // If Pop-up shown then hides it as editing text.
2250 switch ( imfEvent.eventName )
2252 case ImfManager::PREEDIT:
2254 mIgnoreFirstCommitFlag = false;
2256 // 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
2257 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2259 // replaces highlighted text with new character
2260 DeleteHighlightedText( false );
2263 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2265 if( IsScrollEnabled() )
2267 // Calculates the new cursor position (in actor coordinates)
2268 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2269 ScrollTextViewToMakeCursorVisible( cursorPosition );
2276 case ImfManager::COMMIT:
2278 if( mIgnoreFirstCommitFlag )
2280 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2281 mIgnoreFirstCommitFlag = false;
2285 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2287 // 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
2288 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2290 // replaces highlighted text with new character
2291 DeleteHighlightedText( false );
2294 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2295 // not needed, one such scenario is when the pre-edit word is too long to fit.
2296 if ( !mIgnoreCommitFlag )
2298 update = CommitReceived( imfEvent.predictiveString );
2302 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2308 if( IsScrollEnabled() )
2310 // Calculates the new cursor position (in actor coordinates)
2311 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2313 ScrollTextViewToMakeCursorVisible( cursorPosition );
2318 case ImfManager::DELETESURROUNDING:
2320 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2321 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2323 mPreEditFlag = false;
2325 std::size_t toDelete = 0;
2326 std::size_t numberOfCharacters = 0;
2328 if( mHighlightMeshActor )
2330 // delete highlighted text.
2331 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2332 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2336 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2338 toDelete = mCursorPosition + imfEvent.cursorOffset;
2340 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2342 numberOfCharacters = mStyledText.size() - toDelete;
2346 numberOfCharacters = imfEvent.numberOfChars;
2349 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2350 DeleteRange( toDelete, numberOfCharacters );
2352 mCursorPosition = toDelete;
2353 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2357 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2360 case ImfManager::GETSURROUNDING:
2362 // 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
2363 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2364 if (! ( mHighlightMeshActor || mSelectingText ) )
2366 std::string text( GetText() );
2367 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2369 imfManager.SetCursorPosition( mCursorPosition );
2370 imfManager.SetSurroundingText( text );
2373 if( 0 != mNumberOfSurroundingCharactersDeleted )
2375 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2376 mNumberOfSurroundingCharactersDeleted = 0;
2378 if( mStyledText.empty() )
2380 ShowPlaceholderText( mStyledPlaceHolderText );
2385 case ImfManager::VOID:
2387 DALI_ASSERT_DEBUG( false );
2391 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2393 return callbackData;
2396 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2398 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2400 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2401 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2403 bool preeditResetRequest ( false );
2405 if( mPreEditFlag ) // Already in pre-edit state.
2407 if( mStyledText.size() >= mMaxStringLength )
2409 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2410 // Cannot fit these characters into field, clear pre-edit.
2411 if ( !mUnderlinedPriorToPreEdit )
2414 style.SetUnderline( false );
2415 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2417 mIgnoreCommitFlag = true;
2418 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2419 mPreEditFlag = false;
2420 EmitMaxInputCharactersReachedSignal();
2424 // delete existing pre-edit string
2425 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2427 // Store new pre-edit string
2428 mPreEditString.SetText( keyString );
2430 if ( keyString.empty() )
2432 mPreEditFlag = false;
2433 mCursorPosition = mPreEditStartPosition;
2435 if( mStyledText.empty() )
2437 ShowPlaceholderText( mStyledPlaceHolderText );
2441 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2444 GetTextLayoutInfo();
2449 // Insert new pre-edit string. InsertAt updates the size and position table.
2450 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2451 // 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.
2452 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2453 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2454 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2457 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2461 else // mPreEditFlag not set
2463 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2465 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2466 // new pre-edit so move into pre-edit state by setting flag
2467 mPreEditFlag = true;
2468 mPreEditString.SetText( keyString ); // store new pre-edit string
2469 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2470 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2471 // 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.
2472 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2473 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2474 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2475 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2481 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2485 return preeditResetRequest;
2488 bool TextInput::CommitReceived(const std::string& keyString )
2490 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2491 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2493 bool update( false );
2495 RemovePreEditStyle();
2497 const std::size_t styledTextSize( mStyledText.size() );
2498 if( styledTextSize >= mMaxStringLength )
2500 // Cannot fit these characters into field, clear pre-edit.
2503 mIgnoreCommitFlag = true;
2504 mPreEditFlag = false;
2506 EmitMaxInputCharactersReachedSignal();
2512 // delete existing pre-edit string
2513 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2514 mPreEditFlag = false;
2516 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2517 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2519 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2521 // No need to update cursor position as Cursor location given by touch.
2522 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2523 mPreserveCursorPosition = false;
2527 // Cursor not set by touch so needs to be re-positioned to input more text
2528 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2530 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2531 if ( mCommitByKeyInput )
2533 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2534 mCommitByKeyInput = false;
2540 if ( mSelectTextOnCommit )
2542 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2547 else // mPreEditFlag not set
2549 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2551 if( mStyledText.empty() && mPlaceHolderSet )
2553 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2554 mDisplayedTextView.SetText( "" );
2555 mNumberOfSurroundingCharactersDeleted = 0;
2556 mPlaceHolderSet = false;
2558 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2560 mNumberOfSurroundingCharactersDeleted = 0;
2565 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2570 mSelectTextOnCommit = false;
2572 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2573 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2578 // End of IMF related methods
2580 std::size_t TextInput::DeletePreEdit()
2582 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2584 DALI_ASSERT_DEBUG( mPreEditFlag );
2586 const std::size_t preEditStringLength = mPreEditString.GetLength();
2587 const std::size_t styledTextSize = mStyledText.size();
2589 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2591 // Prevents erase items outside mStyledText bounds.
2592 if( mPreEditStartPosition > styledTextSize )
2594 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2595 mPreEditStartPosition = styledTextSize;
2598 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2600 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2601 endPosition = styledTextSize;
2604 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2606 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2607 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2609 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2611 return preEditStringLength;
2614 void TextInput::PreEditReset( bool preserveCursorPosition )
2616 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2617 preserveCursorPosition, mCursorPosition);
2619 // 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.
2620 mPreserveCursorPosition = preserveCursorPosition;
2622 // Reset incase we are in a pre-edit state.
2623 ImfManager imfManager = ImfManager::Get();
2626 imfManager.Reset(); // Will trigger a commit message
2630 void TextInput::CursorUpdate()
2634 ImfManager imfManager = ImfManager::Get();
2637 std::string text( GetText() );
2638 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2639 imfManager.SetCursorPosition ( mCursorPosition );
2640 imfManager.NotifyCursorPosition();
2644 /* Delete highlighted characters redisplay*/
2645 void TextInput::DeleteHighlightedText( bool inheritStyle )
2647 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2649 if( mHighlightMeshActor )
2651 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2653 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2654 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2656 // Get the styled text of the characters to be deleted as it may be needed if
2657 // the "exceed the text-input's boundaries" option is disabled.
2658 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2660 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2662 mStyledText.erase( start, end ); // erase range of characters
2664 // Remove text from TextView and update place holder text if required
2666 // Set the placeholder text only if the styled text is empty.
2667 if( mStyledText.empty() )
2669 ShowPlaceholderText( mStyledPlaceHolderText );
2673 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2675 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2677 // It may happen than after removing a white space or a new line character,
2678 // two words merge, this new word could be big enough to not fit in its
2679 // current line, so moved to the next one, and make some part of the text to
2680 // exceed the text-input's boundary.
2681 if( !mExceedEnabled )
2683 // Get the new text layout after removing some characters.
2684 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2686 // Get text-input's size.
2687 const Vector3& size = GetControlSize();
2689 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2690 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2692 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2694 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2695 styledCharactersToDelete.begin(),
2696 styledCharactersToDelete.end() );
2700 GetTextLayoutInfo();
2708 const TextStyle oldInputStyle( mInputStyle );
2710 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2712 if( oldInputStyle != mInputStyle )
2714 // Updates the line height accordingly with the input style.
2717 EmitStyleChangedSignal();
2723 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2725 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2726 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2728 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2731 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2733 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2734 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2736 mStyledText.erase(itStart, itEnd);
2738 // update the selection handles if they are visible.
2739 if( mHighlightMeshActor )
2741 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2742 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2744 if( minHandle >= start + ncharacters )
2746 minHandle -= ncharacters;
2748 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2753 if( maxHandle >= start + ncharacters )
2755 maxHandle -= ncharacters;
2757 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2763 // 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.
2766 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2768 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2769 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2770 // Mean we do not re-draw the text more than we have too.
2773 /* Delete character at current cursor position and redisplay*/
2774 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2776 // Ensure positionToDelete is not out of bounds.
2777 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2778 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2779 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2781 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2784 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2786 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2788 // Get the styled text of the character to be deleted as it may be needed if
2789 // the "exceed the text-input's boundaries" option is disabled.
2790 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2792 mStyledText.erase(it); // erase the character left of positionToDelete
2794 if( mStyledText.empty() )
2796 ShowPlaceholderText( mStyledPlaceHolderText );
2800 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2802 const Character characterToDelete = styledCharacterToDelete.mText[0];
2804 // It may happen than after removing a white space or a new line character,
2805 // two words merge, this new word could be big enough to not fit in its
2806 // current line, so moved to the next one, and make some part of the text to
2807 // exceed the text-input's boundary.
2808 if( !mExceedEnabled )
2810 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2812 // Get the new text layout after removing one character.
2813 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2815 // Get text-input's size.
2816 const Vector3& size = GetControlSize();
2818 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2819 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2821 MarkupProcessor::StyledTextArray array;
2822 array.push_back( styledCharacterToDelete );
2823 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2825 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2830 GetTextLayoutInfo();
2832 ShowGrabHandleAndSetVisibility( false );
2834 mCursorPosition = positionToDelete -1;
2836 const TextStyle oldInputStyle( mInputStyle );
2838 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2840 if( oldInputStyle != mInputStyle )
2842 // Updates the line height accordingly with the input style.
2845 EmitStyleChangedSignal();
2850 /*Insert new character into the string and (optionally) redisplay text-input*/
2851 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2853 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2855 // Ensure insertionPosition is not out of bounds.
2856 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2858 bool textExceedsMaximunNumberOfCharacters = false;
2859 bool textExceedsBoundary = false;
2860 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2862 ShowGrabHandleAndSetVisibility( false );
2864 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2868 mIgnoreCommitFlag = true;
2869 mPreEditFlag = false;
2870 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2871 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2874 if( textExceedsMaximunNumberOfCharacters )
2876 EmitMaxInputCharactersReachedSignal();
2879 if( textExceedsBoundary )
2881 EmitInputTextExceedsBoundariesSignal();
2882 PreEditReset( false );
2886 return insertedStringLength;
2889 ImageActor TextInput::CreateCursor( const Vector4& color)
2892 cursor = CreateSolidColorActor(color);
2893 cursor.SetName( "Cursor" );
2895 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2896 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2897 cursor.SetVisible(false);
2902 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2904 // As cursor is not moving due to grab handle, handle should be hidden.
2905 ShowGrabHandleAndSetVisibility( false );
2907 bool cursorPositionChanged = false;
2910 if ( mCursorPosition >= places )
2912 mCursorPosition = mCursorPosition - places;
2913 cursorPositionChanged = true;
2918 if ((mCursorPosition + places) <= mStyledText.size())
2920 mCursorPosition = mCursorPosition + places;
2921 cursorPositionChanged = true;
2925 if( cursorPositionChanged )
2927 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2929 const TextStyle oldInputStyle( mInputStyle );
2930 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2934 if( oldInputStyle != mInputStyle )
2936 // Updates the line height accordingly with the input style.
2939 EmitStyleChangedSignal();
2942 ImfManager imfManager = ImfManager::Get();
2945 imfManager.SetCursorPosition ( mCursorPosition );
2946 imfManager.NotifyCursorPosition();
2951 void TextInput::DrawCursor()
2953 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2955 // Get height of cursor and set its size
2956 Size size( CURSOR_THICKNESS, 0.0f );
2957 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2959 size.height = rowRect.height;
2963 // Measure Font so know how big text will be if no initial text to measure.
2964 size.height = mLineHeight;
2967 mCursor.SetSize(size);
2969 // If the character is italic then the cursor also tilts.
2970 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2972 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2974 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2976 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2977 bool altPositionValid; // Alternate cursor validity flag.
2978 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2979 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2981 SetAltCursorEnabled( altPositionValid );
2983 if( !altPositionValid )
2985 mCursor.SetPosition( position + UI_OFFSET );
2989 size.height *= 0.5f;
2990 mCursor.SetSize(size);
2991 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2993 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2994 size.height = rowRect.height * 0.5f;
2995 mCursorRTL.SetSize(size);
2996 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2999 if( IsScrollEnabled() )
3001 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3002 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3007 void TextInput::SetAltCursorEnabled( bool enabled )
3009 mCursorRTLEnabled = enabled;
3010 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3013 void TextInput::SetCursorVisibility( bool visible )
3015 mCursorVisibility = visible;
3016 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3017 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3020 void TextInput::CreateGrabHandle( Dali::Image image )
3026 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
3030 mGrabHandleImage = image;
3033 mGrabHandle = ImageActor::New(mGrabHandleImage);
3034 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3035 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3037 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3039 ShowGrabHandleAndSetVisibility( false );
3041 CreateGrabArea( mGrabHandle );
3043 mActiveLayer.Add(mGrabHandle);
3047 void TextInput::CreateGrabArea( Actor& parent )
3049 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3050 mGrabArea.SetName( "GrabArea" );
3051 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3052 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3053 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3054 mTapDetector.Attach( mGrabArea );
3055 mPanGestureDetector.Attach( mGrabArea );
3056 mLongPressDetector.Attach( mGrabArea );
3058 parent.Add(mGrabArea);
3061 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3063 Vector3 actualHandlePosition;
3067 mActualGrabHandlePosition.x += displacement.x;
3068 mActualGrabHandlePosition.y += displacement.y;
3070 // Grab handle should jump to the nearest character and take cursor with it
3071 std::size_t newCursorPosition = 0;
3072 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3074 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3075 bool altPositionValid; // Alternate cursor validity flag.
3076 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3077 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3079 if( altPositionValid )
3081 // Check which of the positions is the closest.
3082 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3084 actualHandlePosition = altPosition;
3088 bool handleVisible = true;
3090 if( IsScrollEnabled() )
3092 const Vector3 controlSize = GetControlSize();
3093 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3094 // Scrolls the text if the handle is not in a visible position
3095 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3102 mCurrentHandlePosition = actualHandlePosition;
3103 mScrollDisplacement = Vector2::ZERO;
3107 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3109 mScrollDisplacement.x = -SCROLL_SPEED;
3111 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3113 mScrollDisplacement.x = SCROLL_SPEED;
3115 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3117 mScrollDisplacement.y = -SCROLL_SPEED;
3119 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3121 mScrollDisplacement.y = SCROLL_SPEED;
3127 if( handleVisible && // Only redraw cursor and do updates if position changed
3128 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3130 mCursorPosition = newCursorPosition;
3132 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3134 const TextStyle oldInputStyle( mInputStyle );
3136 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3138 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3140 if( oldInputStyle != mInputStyle )
3142 // Updates the line height accordingly with the input style.
3145 EmitStyleChangedSignal();
3150 return actualHandlePosition;
3153 void TextInput::ShowGrabHandle( bool visible )
3155 if ( IsGrabHandleEnabled() )
3159 mGrabHandle.SetVisible( mGrabHandleVisibility );
3161 StartMonitoringStageForTouch();
3165 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3167 mGrabHandleVisibility = visible;
3168 ShowGrabHandle( visible );
3171 // Callbacks connected to be Property notifications for Boundary checking.
3173 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3175 mIsSelectionHandleOneFlipped = true;
3176 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3177 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3180 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3182 mIsSelectionHandleOneFlipped = false;
3183 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3184 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3187 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3189 mIsSelectionHandleTwoFlipped = true;
3190 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3191 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3194 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3196 mIsSelectionHandleTwoFlipped = false;
3197 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3198 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3201 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3202 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3204 mSelectionHandleOne.SetOpacity(0.0f);
3207 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3209 mSelectionHandleOne.SetOpacity(1.0f);
3212 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3214 mSelectionHandleTwo.SetOpacity(0.0f);
3217 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3219 mSelectionHandleTwo.SetOpacity(1.0f);
3222 // End of Callbacks connected to be Property notifications for Boundary checking.
3224 void TextInput::SetUpHandlePropertyNotifications()
3226 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3228 Vector3 handlesize = GetSelectionHandleSize();
3230 // Exceeding horizontal boundary
3231 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3232 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3234 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3235 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3237 // Within horizontal boundary
3238 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3239 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3241 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3242 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3244 // Exceeding vertical boundary
3245 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3246 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3247 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3248 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3250 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3251 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3252 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3253 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3255 // Within vertical boundary
3256 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3257 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3258 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3259 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3261 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3262 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3263 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3264 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3267 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3269 mSelectionHandleOnePosition = start;
3270 mSelectionHandleTwoPosition = end;
3272 if ( !mSelectionHandleOne )
3274 // create normal and pressed images
3275 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3276 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3278 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3279 mSelectionHandleOne.SetName("SelectionHandleOne");
3280 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3281 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3282 mIsSelectionHandleOneFlipped = false;
3283 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3285 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3286 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3288 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3289 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3291 mTapDetector.Attach( mHandleOneGrabArea );
3292 mPanGestureDetector.Attach( mHandleOneGrabArea );
3294 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3296 mSelectionHandleOne.Add( mHandleOneGrabArea );
3297 mActiveLayer.Add( mSelectionHandleOne );
3300 if ( !mSelectionHandleTwo )
3302 // create normal and pressed images
3303 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3304 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3306 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3307 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3308 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3309 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3310 mIsSelectionHandleTwoFlipped = false;
3311 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3313 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3314 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3315 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3316 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3318 mTapDetector.Attach( mHandleTwoGrabArea );
3319 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3321 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3323 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3325 mActiveLayer.Add( mSelectionHandleTwo );
3328 SetUpHandlePropertyNotifications();
3330 // update table as text may have changed.
3331 GetTextLayoutInfo();
3333 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3334 bool altPositionValidOne; // Alternate cursor validity flag.
3335 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3336 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3338 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3339 bool altPositionValidTwo; // Alternate cursor validity flag.
3340 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3341 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3343 // VCC TODO: This method is a hack for one line.
3344 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3346 altPositionValidOne,
3347 altPositionValidTwo,
3351 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3352 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3354 // Calculates and set the visibility if the scroll mode is enabled.
3355 bool isSelectionHandleOneVisible = true;
3356 bool isSelectionHandleTwoVisible = true;
3357 if( IsScrollEnabled() )
3359 const Vector3& controlSize( GetControlSize() );
3360 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3361 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3362 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3363 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3366 CreateHighlight(); // function will only create highlight if not already created.
3369 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3371 Vector3 actualHandlePosition;
3373 if ( mSelectionHandleOne && mSelectionHandleTwo )
3375 const Vector3& controlSize = GetControlSize();
3377 Size cursorSize( CURSOR_THICKNESS, 0.f );
3379 // Get a reference of the wanted selection handle (handle one or two).
3380 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3382 // Get a reference for the current position of the handle and a copy of its pair
3383 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3384 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3386 // Get a handle of the selection handle actor
3387 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3389 // Selection handles should jump to the nearest character
3390 std::size_t newHandlePosition = 0;
3391 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3393 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3394 bool altPositionValid; // Alternate cursor validity flag.
3395 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3396 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3397 if( altPositionValid )
3399 // Check which of the positions is the closest.
3400 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3402 actualHandlePosition = altPosition;
3406 bool handleVisible = true;
3408 if( IsScrollEnabled() )
3410 mCurrentSelectionId = handleId;
3412 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3413 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3414 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3421 mCurrentSelectionHandlePosition = actualHandlePosition;
3422 mScrollDisplacement = Vector2::ZERO;
3426 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3428 mScrollDisplacement.x = -SCROLL_SPEED;
3430 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3432 mScrollDisplacement.x = SCROLL_SPEED;
3434 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3436 mScrollDisplacement.y = -SCROLL_SPEED;
3438 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3440 mScrollDisplacement.y = SCROLL_SPEED;
3446 if ( handleVisible && // Ensure the handle is visible.
3447 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3448 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3450 currentSelectionHandlePosition = newHandlePosition;
3452 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3453 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3457 if ( handleId == HandleOne )
3459 const TextStyle oldInputStyle( mInputStyle );
3461 // Set Active Style to that of first character in selection
3462 if( mSelectionHandleOnePosition < mStyledText.size() )
3464 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3467 if( oldInputStyle != mInputStyle )
3469 // Updates the line height accordingly with the input style.
3472 EmitStyleChangedSignal();
3478 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3481 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3483 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3484 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3486 if ( selectionHandleActor )
3488 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3489 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3490 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3492 if( IsScrollEnabled() )
3494 const Size cursorSize( CURSOR_THICKNESS,
3495 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3496 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3498 GetControlSize() ) );
3503 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3505 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3507 // VCC Set true/false in logical order. TODO : It needs to be checked.
3509 if( startSelection > endSelection )
3511 std::swap( startSelection, endSelection );
3513 std::size_t index = 0u;
3514 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3516 if( ( index < startSelection ) || ( endSelection <= index ) )
3527 // Calculate the dimensions of the quads they will make the highlight mesh
3528 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3530 // At the moment there is no public API to modify the block alignment option.
3531 const bool blockAlignEnabled = true;
3533 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3535 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3537 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3538 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3540 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3541 std::vector<bool> selectedVisualText;
3542 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3543 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3544 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3546 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3547 float rowLeft = 0.0f;
3548 float rowRight = 0.0f;
3549 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3550 float maxRowLeft = std::numeric_limits<float>::max();
3551 float maxRowRight = 0.0f;
3553 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3555 // Scan through entire text.
3558 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3560 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3561 bool charSelected = false;
3562 if( selectedIt != selectedEndIt )
3564 charSelected = *selectedIt++;
3567 if( selectionState == SelectionNone )
3571 selectionState = SelectionStarted;
3572 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3573 rowRight = rowLeft + charInfo.mSize.width;
3576 else if( selectionState == SelectionStarted )
3578 // break selection on:
3579 // 1. new line causing selection break. (\n or wordwrap)
3580 // 2. character not selected.
3581 if( !charSelected ||
3582 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3584 // finished selection.
3585 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3586 // that it resides on. That way this enumeration is not necessary.
3588 if(lastIt->mIsNewParagraphChar)
3590 // 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.
3591 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3593 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3594 maxRowLeft = std::min(maxRowLeft, min.x);
3595 maxRowRight = std::max(maxRowRight, max.x);
3596 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3597 float rowTop = rowBottom - rowSize.height;
3599 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3600 if(charSelected && blockAlignEnabled)
3602 rowRight = std::numeric_limits<float>::max();
3604 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3606 selectionState = SelectionNone;
3608 // Still selected? start a new selection
3611 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3612 rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3613 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3614 selectionState = SelectionStarted;
3619 // build up highlight(s) with this selection data.
3620 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3621 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3628 // If reached end, and still on selection, then close selection.
3631 if(selectionState == SelectionStarted)
3633 // finished selection.
3635 if(lastIt->mIsNewParagraphChar)
3637 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3639 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3640 maxRowLeft = std::min(maxRowLeft, min.x);
3641 maxRowRight = std::max(maxRowRight, max.x);
3642 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3643 float rowTop = rowBottom - rowSize.height;
3644 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3648 // Get the top left and bottom right corners.
3649 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3650 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3651 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3653 // Clamp quads so they appear to clip to borders of the whole text.
3654 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3656 // For block-align align Further Clamp quads to max left and right extents
3657 if(blockAlignEnabled)
3659 // BlockAlign: Will adjust highlight to block:
3661 // H[ello] (top row right = max of all rows right)
3662 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3663 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3664 // [text] (bottom row left = min of all rows left)
3665 // (common in SMS messaging selection)
3667 // As opposed to the default which is tight text highlighting.
3672 // (common in regular text editors/web browser selection)
3674 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3677 // Finally clamp quads again so they don't exceed the boundry of the control.
3678 const Vector3& controlSize = GetControlSize();
3679 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3682 return mNewHighlightInfo;
3685 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3686 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3687 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3689 // At the moment there is no public API to modify the block alignment option.
3691 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3693 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3695 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3696 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3698 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3699 std::vector<bool> selectedVisualText;
3700 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3701 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3702 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3704 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3705 float rowLeft = 0.0f;
3706 float rowRight = 0.0f;
3708 // VCC TODO this is valid for one line.
3710 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3712 // Scan through entire text.
3715 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3717 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3718 bool charSelected = false;
3719 if( selectedIt != selectedEndIt )
3721 charSelected = *selectedIt++;
3726 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3727 rowRight = rowLeft + charInfo.mSize.width;
3729 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3730 float rowTop = rowBottom - rowSize.height;
3731 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3737 // Finally clamp quads again so they don't exceed the boundry of the control.
3738 const Vector3& controlSize = GetControlSize();
3739 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3742 return mNewHighlightInfo;
3745 void TextInput::UpdateHighlight()
3747 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3749 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3751 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3752 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3753 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3754 // [BOTTOM] [ MIDDLE ]
3757 // Each quad is created as 2 triangles.
3758 // Middle is just 1 quad regardless of its size.
3772 if ( mHighlightMeshActor )
3774 // vertex and triangle buffers should always be present if MeshActor is alive.
3775 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3776 MeshData::VertexContainer vertices;
3777 Dali::MeshData::FaceIndices faceIndices;
3779 if( !newHighlightInfo.mQuadList.empty() )
3781 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3782 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3784 // vertex position defaults to (0 0 0)
3785 MeshData::Vertex vertex;
3786 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3789 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3791 // Add each quad geometry (a sub-selection) to the mesh data.
3801 QuadCoordinates& quad = *iter;
3803 vertex.x = quad.min.x;
3804 vertex.y = quad.min.y;
3805 vertices.push_back( vertex );
3808 vertex.x = quad.max.x;
3809 vertex.y = quad.min.y;
3810 vertices.push_back( vertex );
3812 // bottom-left (v+2)
3813 vertex.x = quad.min.x;
3814 vertex.y = quad.max.y;
3815 vertices.push_back( vertex );
3817 // bottom-right (v+3)
3818 vertex.x = quad.max.x;
3819 vertex.y = quad.max.y;
3820 vertices.push_back( vertex );
3822 // triangle A (3, 1, 0)
3823 faceIndices.push_back( v + 3 );
3824 faceIndices.push_back( v + 1 );
3825 faceIndices.push_back( v );
3827 // triangle B (0, 2, 3)
3828 faceIndices.push_back( v );
3829 faceIndices.push_back( v + 2 );
3830 faceIndices.push_back( v + 3 );
3832 mMeshData.SetFaceIndices( faceIndices );
3835 BoneContainer bones(0); // passed empty as bones not required
3836 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3837 mHighlightMesh.UpdateMeshData(mMeshData);
3842 void TextInput::ClearPopup()
3844 mPopupPanel.Clear();
3847 void TextInput::AddPopupOptions()
3849 mPopupPanel.AddPopupOptions();
3852 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3854 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3856 Vector3 clampedPosition ( position );
3857 Vector3 tailOffsetPosition ( position );
3859 float xOffSet( 0.0f );
3861 Actor self = Self();
3862 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3864 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3865 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3867 // Clamp to left or right or of boundary
3868 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3870 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3872 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3874 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3877 clampedPosition.x = position.x + xOffSet;
3878 tailOffsetPosition.x = -xOffSet;
3880 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3881 bool flipTail( false );
3883 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3885 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3889 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3890 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3893 void TextInput::HidePopup(bool animate, bool signalFinished )
3895 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3897 mPopupPanel.Hide( animate );
3899 if( animate && signalFinished )
3901 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3906 void TextInput::ShowPopup( bool animate )
3909 Vector2 alternativePopupPosition;
3911 if(mHighlightMeshActor && mState == StateEdit)
3914 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3916 // When text is selected, show popup above top handle (and text), or below bottom handle.
3917 // topHandle: referring to the top most point of the handle or the top line of selection.
3918 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3920 topHandle = mSelectionHandleOneActualPosition;
3921 bottomHandle = mSelectionHandleTwoActualPosition;
3922 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3926 topHandle = mSelectionHandleTwoActualPosition;
3927 bottomHandle = mSelectionHandleOneActualPosition;
3928 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3930 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3931 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3933 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3935 position.x = xPosition;
3937 // Alternative position if no upper space
3938 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3939 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3943 // When no text is selected, show popup at world position of grab handle or cursor
3944 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3945 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3946 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3947 // if can't be positioned above, then position below row.
3948 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3951 // If grab handle enabled then position pop-up below the grab handle.
3952 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3956 SetPopupPosition( position, alternativePopupPosition );
3959 mPopupPanel.Show( Self(), animate );
3960 StartMonitoringStageForTouch();
3962 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3965 void TextInput::ShowPopupCutCopyPaste()
3969 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3970 // Check the selected text is whole text or not.
3971 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3973 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3976 if ( !mStyledText.empty() && IsTextSelected() )
3978 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3979 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3982 if( mClipboard && mClipboard.NumberOfItems() )
3984 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3985 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3990 mPopupPanel.Hide(false);
3994 void TextInput::SetUpPopupSelection( bool showCutButton )
3997 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3998 // If no text exists then don't offer to select
3999 if ( !mStyledText.empty() )
4001 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
4002 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
4003 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
4005 // if clipboard has valid contents then offer paste option
4006 if( mClipboard && mClipboard.NumberOfItems() )
4008 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4009 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4014 mPopupPanel.Hide(false);
4017 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4022 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4023 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4024 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4025 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4027 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4029 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4031 float closestYdifference = std::numeric_limits<float>::max();
4032 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4033 std::size_t numberOfMatchedCharacters = 0;
4035 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4036 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4038 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4040 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4041 float baselinePosition = info.mPosition.y - info.mDescender;
4043 if( info.mIsVisible )
4045 // store difference between source y point and the y position of the current character
4046 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4048 if( currentYdifference < closestYdifference )
4050 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4051 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4052 closestYdifference = currentYdifference;
4053 matchedCharacters.clear();
4054 numberOfMatchedCharacters = 0; // reset count
4057 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4058 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4060 // ignore new line character.
4061 if( !info.mIsNewParagraphChar )
4063 matchedCharacters.push_back( info );
4064 numberOfMatchedCharacters++;
4068 } // End of loop checking each character's y position in the character layout table
4070 // Check if last character is a newline, if it is
4071 // then need pretend there is an imaginary line afterwards,
4072 // and check if user is touching below previous line.
4073 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4075 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4077 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4081 // 2 Iterate through matching list of y positions and find closest matching X position.
4083 bool matched( false );
4085 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4086 std::size_t visualIndex = 0u;
4087 const std::size_t matchedCharactersSize = matchedCharacters.size();
4088 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4090 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4092 if( info.mIsVisible )
4094 // stop when on left side of character's center.
4095 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4096 if( sourceScrollOffset.x < characterMidPointPosition )
4098 if(info.mIsRightToLeftCharacter)
4100 rightToLeftChar = true;
4102 glyphIntersection = info.mPosition.x;
4107 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4111 if( visualIndex == matchedCharactersSize )
4113 rightToLeftChar = lastRightToLeftChar;
4116 closestIndex = lineOffset + visualIndex;
4118 mClosestCursorPositionEOL = false; // reset
4119 if( ( visualIndex == matchedCharactersSize ) && !matched )
4121 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4124 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4125 if( rightToLeftChar && lastRightToLeftChar )
4127 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4132 // closestIndex is the visual index, need to convert it to the logical index
4133 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4135 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4137 // Checks for situations where user is touching between LTR and RTL
4138 // characters. To identify if the user means the end of a LTR string
4139 // or the beginning of an RTL string, and vice versa.
4140 if( closestIndex > 0 )
4142 if( rightToLeftChar && !lastRightToLeftChar )
4147 // A: In this touch range, the user is indicating that they wish to place
4148 // the cursor at the end of the LTR text.
4149 // B: In this touch range, the user is indicating that they wish to place
4150 // the cursor at the end of the RTL text.
4152 // Result of touching A area:
4153 // [.....LTR]|[RTL......]+
4155 // |: primary cursor (for typing LTR chars)
4156 // +: secondary cursor (for typing RTL chars)
4158 // Result of touching B area:
4159 // [.....LTR]+[RTL......]|
4161 // |: primary cursor (for typing RTL chars)
4162 // +: secondary cursor (for typing LTR chars)
4164 if( sourceScrollOffset.x < glyphIntersection )
4169 else if( !rightToLeftChar && lastRightToLeftChar )
4171 if( sourceScrollOffset.x < glyphIntersection )
4178 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4179 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4180 // one further ahead
4181 if( rightToLeftChar && !lastRightToLeftChar )
4186 else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4188 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4190 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4199 float TextInput::GetLineJustificationPosition() const
4201 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4202 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4203 float alignmentOffset = 0.f;
4205 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4206 if( alignment & Toolkit::Alignment::HorizontalLeft )
4208 alignmentOffset = 0.f;
4210 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4212 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4214 else if( alignment & Toolkit::Alignment::HorizontalRight )
4216 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4219 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4220 float justificationOffset = 0.f;
4222 switch( justification )
4224 case Toolkit::TextView::Left:
4226 justificationOffset = 0.f;
4229 case Toolkit::TextView::Center:
4231 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4234 case Toolkit::TextView::Right:
4236 justificationOffset = mTextLayoutInfo.mTextSize.width;
4239 case Toolkit::TextView::Justified:
4241 justificationOffset = 0.f;
4246 DALI_ASSERT_ALWAYS( false );
4250 return alignmentOffset + justificationOffset;
4253 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4255 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4256 A newline character is not inserted in this case */
4258 Vector3 cursorPosition;
4260 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4264 if( characterPosition > 0u )
4266 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4268 // If previous character on a different line then use current characters position
4269 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4271 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4272 if ( mClosestCursorPositionEOL )
4274 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4278 cursorPosition = Vector3( currentCharInfo.mPosition );
4287 // If the character is left to right, the position is the character's position plus its width.
4288 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4290 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4291 cursorPosition.y = currentCharInfo.mPosition.y;
4294 return cursorPosition;
4297 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4299 bool direction = false;
4300 Vector3 alternatePosition;
4301 bool alternatePositionValid = false;
4303 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4306 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4308 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4309 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4310 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4312 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4314 alternatePositionValid = false;
4315 directionRTL = false;
4317 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4319 if( characterPosition == 0u )
4321 // When the cursor position is at the beginning, it should be at the start of the current character.
4322 // If the current character is LTR, then the start is on the right side of the glyph.
4323 // If the current character is RTL, then the start is on the left side of the glyph.
4325 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4327 characterPosition = FindVisibleCharacter( Right, 0u );
4330 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4331 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4333 cursorPosition.x = info.mPosition.x + rtlOffset;
4334 cursorPosition.y = info.mPosition.y;
4335 directionRTL = info.mIsRightToLeftCharacter;
4337 else if( characterPosition > 0u )
4339 // Get the direction of the paragraph.
4340 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4341 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4343 // When cursor is not at beginning, consider possibility of
4344 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4346 // Cursor position should be the end of the last character.
4347 // If the last character is LTR, then the end is on the right side of the glyph.
4348 // If the last character is RTL, then the end is on the left side of the glyph.
4350 --characterPosition;
4352 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4354 characterPosition = FindVisibleCharacter( Left, characterPosition );
4357 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4358 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4360 // VCC TODO : check for a new paragraph character.
4362 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4363 const Vector3& size = GetControlSize();
4365 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4367 --characterPosition;
4369 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4372 if( !info.mIsNewParagraphChar )
4374 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4378 // VCC TODO : check for a new paragraph character.
4380 // When cursor points to first character on new line, position cursor at the start of this glyph.
4381 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4383 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4384 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4386 cursorPosition.x = infoNext.mPosition.x + start;
4387 cursorPosition.y = infoNext.mPosition.y;
4391 // If cursor points to the end of text, then can only position
4392 // cursor where the new line starts based on the line-justification position.
4393 cursorPosition.x = GetLineJustificationPosition();
4395 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4397 // If this is after the last character, then we can assume that the new cursor
4398 // should be exactly one row below the current row.
4400 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4401 cursorPosition.y = info.mPosition.y + rowRect.height;
4405 // If this is not after last character, then we can use this row's height.
4406 // should be exactly one row below the current row.
4408 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4409 cursorPosition.y = info.mPosition.y + rowRect.height;
4414 directionRTL = info.mIsRightToLeftCharacter;
4416 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4418 // 1. When the cursor is neither at the beginning or the end,
4419 // we can show multiple cursors under situations when the cursor is
4420 // between RTL and LTR text...
4421 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4423 std::size_t characterAltPosition = characterPosition + 1u;
4425 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4427 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4429 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4430 // Text: [...LTR...]|[...RTL...]
4432 // Alternate cursor pos: ^
4433 // In which case we need to display an alternate cursor for the RTL text.
4435 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4436 alternatePosition.y = infoAlt.mPosition.y;
4437 alternatePositionValid = true;
4439 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4441 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4442 // Text: |[...RTL...] [...LTR....]
4444 // Alternate cursor pos: ^
4445 // In which case we need to display an alternate cursor for the RTL text.
4447 alternatePosition.x = infoAlt.mPosition.x;
4448 alternatePosition.y = infoAlt.mPosition.y;
4449 alternatePositionValid = true;
4454 // 2. When the cursor is at the end of the text,
4455 // and we have multi-directional text,
4456 // we can also consider showing mulitple cursors.
4457 // The rule here is:
4458 // If first and last characters on row are different
4459 // Directions, then two cursors need to be displayed.
4461 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4463 // The last character's direction is differernt than the first one of current paragraph.
4466 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4468 if(info.mIsRightToLeftCharacter)
4470 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4471 // Text: [...LTR...]|[...RTL...]
4473 // Alternate cursor pos: ^
4474 // In which case we need to display an alternate cursor for the RTL text, this cursor
4475 // should be at the end of the given line.
4477 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4478 alternatePosition.y = infoStart.mPosition.y;
4479 alternatePositionValid = true;
4481 else if(!info.mIsRightToLeftCharacter) // starting RTL
4483 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4484 // Text: |[...RTL...] [...LTR....]
4486 // Alternate cursor pos: ^
4487 // In which case we need to display an alternate cursor for the RTL text.
4489 alternatePosition.x = infoStart.mPosition.x;
4490 alternatePosition.y = infoStart.mPosition.y;
4491 alternatePositionValid = true;
4496 } // characterPosition > 0
4500 // If the character table is void, place the cursor accordingly the text alignment.
4501 const Vector3& size = GetControlSize();
4503 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4504 float alignmentOffset = 0.f;
4506 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4507 if( alignment & Toolkit::Alignment::HorizontalLeft )
4509 alignmentOffset = 0.f;
4511 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4513 alignmentOffset = 0.5f * ( size.width );
4515 else if( alignment & Toolkit::Alignment::HorizontalRight )
4517 alignmentOffset = size.width;
4520 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4521 cursorPosition.x = alignmentOffset;
4523 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4524 if( alignment & Toolkit::Alignment::VerticalTop )
4526 cursorPosition.y = mLineHeight;
4528 else if( alignment & Toolkit::Alignment::VerticalCenter )
4530 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4532 else if( alignment & Toolkit::Alignment::VerticalBottom )
4534 cursorPosition.y = size.height;
4538 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4539 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4541 if( alternatePositionValid )
4543 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4544 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4547 return cursorPosition;
4550 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4552 // scan string from current position to beginning of current line to note direction of line
4553 while( logicalPosition )
4556 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4563 return logicalPosition;
4566 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4568 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4570 while( logicalPosition )
4573 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4580 return logicalPosition;
4583 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4587 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4590 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4592 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4593 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4595 min = Vector2::ZERO;
4596 max = Vector2(0.0f, mLineHeight);
4600 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4602 // Initializes the min and max position.
4603 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4604 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4608 // 1) Find the line where the character is laid-out.
4609 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4610 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4613 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4615 // Index within the whole text to the last character of the current line.
4616 std::size_t lastCharacterOfLine = 0u;
4618 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4619 if( lineNextIt != lineEndIt )
4621 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4625 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4628 // Check if the given chracter position is within the line.
4629 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4631 // 2) Get the row rect of all laid-out characters on the line.
4633 // Need to scan all characters of the line because they are in the logical position.
4634 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4635 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4639 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4641 min.x = std::min( min.x, characterInfo.mPosition.x );
4642 min.y = std::min( min.y, characterInfo.mPosition.y );
4643 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4644 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4651 return Size( max.x - min.x, max.y - min.y );
4654 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4656 Actor popUpPanel = mPopupPanel.GetRootActor();
4658 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4664 Dali::Actor parent( touchedActor.GetParent() );
4668 return WasTouchedCheck( parent );
4675 void TextInput::StartMonitoringStageForTouch()
4677 Stage stage = Stage::GetCurrent();
4678 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4681 void TextInput::EndMonitoringStageForTouch()
4683 Stage stage = Stage::GetCurrent();
4684 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4687 void TextInput::OnStageTouched(const TouchEvent& event)
4689 if( event.GetPointCount() > 0 )
4691 if ( TouchPoint::Down == event.GetPoint(0).state )
4693 const Actor touchedActor(event.GetPoint(0).hitActor);
4695 bool popUpShown( false );
4697 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4702 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4704 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4706 EndMonitoringStageForTouch();
4707 HidePopup( true, false );
4710 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4712 EndMonitoringStageForTouch();
4713 ShowGrabHandleAndSetVisibility( false );
4719 void TextInput::SelectText(std::size_t start, std::size_t end)
4721 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4722 IsGrabHandleEnabled()?"true":"false",
4723 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4724 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4725 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4727 StartMonitoringStageForTouch();
4729 if ( mEditModeActive ) // Only allow text selection when in edit mode
4731 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4732 mSelectingText = true;
4734 std::size_t selectionStartPosition = std::min( start, end );
4736 // Hide grab handle when selecting.
4737 ShowGrabHandleAndSetVisibility( false );
4739 if( start != end ) // something to select
4741 SetCursorVisibility( false );
4742 StopCursorBlinkTimer();
4744 CreateSelectionHandles(start, end);
4747 const TextStyle oldInputStyle( mInputStyle );
4748 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4750 if( oldInputStyle != mInputStyle )
4752 // Updates the line height accordingly with the input style.
4755 EmitStyleChangedSignal();
4761 mSelectingText = false;
4765 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4767 MarkupProcessor::StyledTextArray currentSelectedText;
4769 if ( IsTextSelected() )
4771 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4772 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4774 for(; it != end; ++it)
4776 MarkupProcessor::StyledText& styledText( *it );
4777 currentSelectedText.push_back( styledText );
4780 return currentSelectedText;
4783 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4785 const std::size_t beginIndex = std::min( begin, end );
4786 const std::size_t endIndex = std::max( begin, end );
4789 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4791 // Create a styled text array used to replace the text into the text-view.
4792 MarkupProcessor::StyledTextArray text;
4793 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4795 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4796 GetTextLayoutInfo();
4798 if( IsScrollEnabled() )
4800 // Need to set the scroll position as the text's size may have changed.
4801 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4804 ShowGrabHandleAndSetVisibility( false );
4810 // Set Handle positioning as the new style may have repositioned the characters.
4811 SetSelectionHandlePosition(HandleOne);
4812 SetSelectionHandlePosition(HandleTwo);
4815 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4817 // Just hide the grab handle when keyboard is hidden.
4818 if (!keyboardShown )
4820 ShowGrabHandleAndSetVisibility( false );
4822 // If the keyboard is not now being shown, then hide the popup panel
4823 mPopupPanel.Hide( true );
4827 // Removes highlight and resumes edit mode state
4828 void TextInput::RemoveHighlight( bool hidePopup )
4830 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4832 if ( mHighlightMeshActor )
4834 if ( mSelectionHandleOne )
4836 mActiveLayer.Remove( mSelectionHandleOne );
4837 mSelectionHandleOne.Reset();
4838 mSelectionHandleOneOffset.x = 0.0f;
4840 if ( mSelectionHandleTwo )
4842 mActiveLayer.Remove( mSelectionHandleTwo );
4843 mSelectionHandleTwo.Reset();
4844 mSelectionHandleTwoOffset.x = 0.0f;
4847 mNewHighlightInfo.mQuadList.clear();
4849 Self().Remove( mHighlightMeshActor );
4851 SetCursorVisibility( true );
4852 StartCursorBlinkTimer();
4854 mHighlightMeshActor.Reset();
4855 // NOTE: We cannot dereference mHighlightMesh, due
4856 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4864 mSelectionHandleOnePosition = 0;
4865 mSelectionHandleTwoPosition = 0;
4868 void TextInput::CreateHighlight()
4870 if ( !mHighlightMeshActor )
4872 mMeshData = MeshData( );
4873 mMeshData.SetHasNormals( true );
4875 mCustomMaterial = Material::New("CustomMaterial");
4876 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4878 mMeshData.SetMaterial( mCustomMaterial );
4880 mHighlightMesh = Mesh::New( mMeshData );
4882 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4883 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4884 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4885 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4886 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4887 mHighlightMeshActor.SetAffectedByLighting(false);
4889 Self().Add(mHighlightMeshActor);
4894 bool TextInput::CopySelectedTextToClipboard()
4896 mCurrentCopySelecton.clear();
4898 mCurrentCopySelecton = GetSelectedText();
4900 std::string stringToStore;
4902 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4903 * a marked up string.
4905 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4906 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4908 bool success = mClipboard.SetItem( stringToStore );
4912 void TextInput::PasteText( const Text& text )
4914 // Update Flag, indicates whether to update the text-input contents or not.
4915 // Any key stroke that results in a visual change of the text-input should
4916 // set this flag to true.
4917 bool update = false;
4918 if( mHighlightMeshActor )
4920 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4921 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4923 ImfManager imfManager = ImfManager::Get();
4926 imfManager.SetCursorPosition( mCursorPosition );
4927 imfManager.NotifyCursorPosition();
4929 DeleteHighlightedText( true );
4933 bool textExceedsMaximunNumberOfCharacters = false;
4934 bool textExceedsBoundary = false;
4936 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4938 mCursorPosition += insertedStringLength;
4939 ImfManager imfManager = ImfManager::Get();
4942 imfManager.SetCursorPosition ( mCursorPosition );
4943 imfManager.NotifyCursorPosition();
4946 update = update || ( insertedStringLength > 0 );
4953 if( insertedStringLength < text.GetLength() )
4955 EmitMaxInputCharactersReachedSignal();
4958 if( textExceedsBoundary )
4960 EmitInputTextExceedsBoundariesSignal();
4964 void TextInput::SetTextDirection()
4966 // Put the cursor to the right if we are empty and an RTL language is being used.
4967 if ( mStyledText.empty() )
4969 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4971 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4972 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4974 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4975 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4977 int alignment( mDisplayedTextView.GetTextAlignment() &
4978 ( Toolkit::Alignment::VerticalTop |
4979 Toolkit::Alignment::VerticalCenter |
4980 Toolkit::Alignment::VerticalBottom |
4981 Toolkit::Alignment::HorizontalCenter ) );
4982 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4984 // If our alignment is in the center, then do not change.
4985 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4987 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4990 // If our justification is in the center, then do not change.
4991 if ( justification != Toolkit::TextView::Center )
4993 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4996 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4997 mDisplayedTextView.SetLineJustification( justification );
5001 void TextInput::UpdateLineHeight()
5003 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
5004 mLineHeight = font.GetLineHeight();
5006 // 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.
5008 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5010 if( !mExceedEnabled || shrink )
5012 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5016 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5018 // VCC check if we need do this in the visual order ...
5019 std::size_t position = 0u;
5021 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5027 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5029 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5031 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5037 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5038 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5040 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5046 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5051 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5058 void TextInput::SetSortModifier( float depthOffset )
5060 if(mDisplayedTextView)
5062 mDisplayedTextView.SetSortModifier(depthOffset);
5066 void TextInput::SetSnapshotModeEnabled( bool enable )
5068 if(mDisplayedTextView)
5070 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5074 bool TextInput::IsSnapshotModeEnabled() const
5076 bool snapshotEnabled = false;
5078 if(mDisplayedTextView)
5080 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5083 return snapshotEnabled;
5086 void TextInput::SetMarkupProcessingEnabled( bool enable )
5088 mMarkUpEnabled = enable;
5091 bool TextInput::IsMarkupProcessingEnabled() const
5093 return mMarkUpEnabled;
5096 void TextInput::SetScrollEnabled( bool enable )
5098 if( mDisplayedTextView )
5100 mDisplayedTextView.SetScrollEnabled( enable );
5105 // Don't set cursor's and handle's visibility to false if they are outside the
5106 // boundaries of the text-input.
5107 mIsCursorInScrollArea = true;
5108 mIsGrabHandleInScrollArea = true;
5109 if( mSelectionHandleOne && mSelectionHandleTwo )
5111 mSelectionHandleOne.SetVisible( true );
5112 mSelectionHandleTwo.SetVisible( true );
5114 if( mHighlightMeshActor )
5116 mHighlightMeshActor.SetVisible( true );
5122 bool TextInput::IsScrollEnabled() const
5124 bool scrollEnabled = false;
5126 if( mDisplayedTextView )
5128 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5131 return scrollEnabled;
5134 void TextInput::SetScrollPosition( const Vector2& position )
5136 if( mDisplayedTextView )
5138 mDisplayedTextView.SetScrollPosition( position );
5142 Vector2 TextInput::GetScrollPosition() const
5144 Vector2 scrollPosition;
5146 if( mDisplayedTextView )
5148 scrollPosition = mDisplayedTextView.GetScrollPosition();
5151 return scrollPosition;
5154 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5156 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5157 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5158 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5160 // Add style to the new input text.
5161 MarkupProcessor::StyledTextArray textToInsert;
5162 for( std::size_t i = 0; i < insertedStringLength; ++i )
5164 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5165 textToInsert.push_back( newStyledCharacter );
5168 //Insert text to the TextView.
5169 const bool emptyTextView = mStyledText.empty();
5170 if( emptyTextView && mPlaceHolderSet )
5172 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5173 mDisplayedTextView.SetText( textToInsert );
5177 if( 0 == numberOfCharactersToReplace )
5179 mDisplayedTextView.InsertTextAt( position, textToInsert );
5183 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5186 mPlaceHolderSet = false;
5188 if( textToInsert.empty() )
5190 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5191 GetTextLayoutInfo();
5195 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5196 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5199 textExceedsBoundary = false;
5201 if( !mExceedEnabled )
5203 const Vector3& size = GetControlSize();
5205 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5207 // If new text does not fit within TextView
5208 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5209 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5210 GetTextLayoutInfo();
5211 textExceedsBoundary = true;
5212 insertedStringLength = 0;
5215 if( textExceedsBoundary )
5217 // Add the part of the text which fits on the text-input.
5219 // Split the text which doesn't fit in two halves.
5220 MarkupProcessor::StyledTextArray firstHalf;
5221 MarkupProcessor::StyledTextArray secondHalf;
5222 SplitText( textToInsert, firstHalf, secondHalf );
5224 // Clear text. This text will be filled with the text inserted.
5225 textToInsert.clear();
5227 // Where to insert the text.
5228 std::size_t positionToInsert = position;
5230 bool end = text.GetLength() <= 1;
5233 // Insert text and check ...
5234 const std::size_t textLength = firstHalf.size();
5235 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5236 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5238 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5240 // Inserted text doesn't fit.
5242 // Remove inserted text
5243 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5244 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5246 // The iteration finishes when only one character doesn't fit.
5247 end = textLength <= 1;
5251 // Prepare next two halves for next iteration.
5252 MarkupProcessor::StyledTextArray copyText = firstHalf;
5253 SplitText( copyText, firstHalf, secondHalf );
5260 // store text to be inserted in mStyledText.
5261 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5263 // Increase the inserted characters counter.
5264 insertedStringLength += textLength;
5266 // Prepare next two halves for next iteration.
5267 MarkupProcessor::StyledTextArray copyText = secondHalf;
5268 SplitText( copyText, firstHalf, secondHalf );
5270 // Update where next text has to be inserted
5271 positionToInsert += textLength;
5277 if( textToInsert.empty() && emptyTextView )
5279 // No character has been added and the text-view was empty.
5280 // Show the placeholder text.
5281 ShowPlaceholderText( mStyledPlaceHolderText );
5285 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5286 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5287 mPlaceHolderSet = false;
5290 return insertedStringLength;
5293 void TextInput::GetTextLayoutInfo()
5295 if( mStyledText.empty() )
5297 // The text-input has no text, clear the text-view's layout info.
5298 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5302 if( mDisplayedTextView )
5304 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5308 // There is no text-view.
5309 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5314 void TextInput::SetOffsetFromText( const Vector4& offset )
5316 mPopupOffsetFromText = offset;
5319 const Vector4& TextInput::GetOffsetFromText() const
5321 return mPopupOffsetFromText;
5324 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5326 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5330 TextInput& textInputImpl( GetImpl( textInput ) );
5332 switch ( propertyIndex )
5334 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5336 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5339 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5341 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5344 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5346 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5349 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5351 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5354 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5356 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5359 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5361 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5364 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5366 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5369 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5371 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5374 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5376 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5379 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5381 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5384 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5386 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5389 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5391 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5394 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5396 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5399 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5401 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5404 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5406 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5409 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5411 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5417 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5419 Property::Value value;
5421 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5425 TextInput& textInputImpl( GetImpl( textInput ) );
5427 switch ( propertyIndex )
5429 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5431 value = textInputImpl.GetMaterialDiffuseColor();
5434 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5436 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5439 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5441 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5444 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5446 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5449 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5451 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5454 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5456 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5459 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5461 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5464 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5466 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5469 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5471 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5474 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5476 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5479 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5481 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5484 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5486 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5489 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5491 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5494 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5496 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5499 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5501 value = textInputImpl.GetOffsetFromText();
5504 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5506 value = textInputImpl.mCursor.GetCurrentColor();
5513 void TextInput::EmitStyleChangedSignal()
5515 // emit signal if input style changes.
5516 Toolkit::TextInput handle( GetOwner() );
5517 mStyleChangedSignalV2.Emit( handle, mInputStyle );
5520 void TextInput::EmitTextModified()
5522 // emit signal when text changes.
5523 Toolkit::TextInput handle( GetOwner() );
5524 mTextModifiedSignal.Emit( handle );
5528 void TextInput::EmitMaxInputCharactersReachedSignal()
5530 // emit signal if max characters is reached during text input.
5531 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5533 Toolkit::TextInput handle( GetOwner() );
5534 mMaxInputCharactersReachedSignalV2.Emit( handle );
5537 void TextInput::EmitInputTextExceedsBoundariesSignal()
5539 // Emit a signal when the input text exceeds the boundaries of the text input.
5541 Toolkit::TextInput handle( GetOwner() );
5542 mInputTextExceedBoundariesSignalV2.Emit( handle );
5545 } // namespace Internal
5547 } // namespace Toolkit