2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
25 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/key-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/property-notification.h>
32 #include <dali/integration-api/debug.h>
33 #include <dali/public-api/images/resource-image.h>
36 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
37 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
38 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
39 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
51 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
52 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
53 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
54 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
55 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
56 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
58 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
59 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
60 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
61 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
62 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
64 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
65 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
66 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
67 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
68 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
70 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
71 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
72 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
73 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
74 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
75 const float CURSOR_THICKNESS(4.0f);
76 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
77 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
79 const std::string NEWLINE( "\n" );
81 const TextStyle DEFAULT_TEXT_STYLE;
83 const unsigned int SCROLL_TICK_INTERVAL = 50u;
84 const float SCROLL_THRESHOLD = 10.f;
85 const float SCROLL_SPEED = 15.f;
88 * Selection state enumeration (FSM)
92 SelectionNone, ///< Currently not encountered selected section.
93 SelectionStarted, ///< Encountered selected section
94 SelectionFinished ///< Finished selected section
97 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
99 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
103 if( ( *it ).mIsVisible )
105 return --cursorPosition;
114 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
116 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
118 if( ( *it ).mIsVisible )
120 return cursorPosition;
126 return cursorPosition;
130 * Whether the given position plus the cursor size offset is inside the given boundary.
132 * @param[in] position The given position.
133 * @param[in] cursorSize The cursor size.
134 * @param[in] controlSize The given boundary.
136 * @return whether the given position is inside the given boundary.
138 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
140 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
141 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
142 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
143 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
147 * Splits a text in two halves.
149 * If the text's number of characters is odd, firstHalf has one more character.
151 * @param[in] text The text to be split.
152 * @param[out] firstHalf The first half of the text.
153 * @param[out] secondHalf The second half of the text.
155 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
156 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
157 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
162 const std::size_t textLength = text.size();
163 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
165 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
166 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
169 } // end of namespace
177 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
178 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
179 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
180 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
181 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
182 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
183 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
184 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
185 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
186 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
187 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
188 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
189 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
190 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
191 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
192 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
203 return Toolkit::TextInput::New();
206 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
208 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
209 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
210 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
211 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
212 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
213 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
217 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
225 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
226 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
227 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
228 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
229 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
230 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
231 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
232 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
235 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
237 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
239 QuadCoordinates quad(x1, y1, x2, y2);
240 mQuadList.push_back( quad );
243 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
245 for(std::size_t i = 0;i < mQuadList.size(); i++)
247 QuadCoordinates& quad = mQuadList[i];
249 quad.min.Clamp(min, max);
250 quad.max.Clamp(min, max);
254 // [TextInput] ////////////////////////////////////////////////////////////////
256 Dali::Toolkit::TextInput TextInput::New()
258 // Create the implementation
259 TextInputPtr textInput(new TextInput());
260 // Pass ownership to CustomActor via derived handle
261 Dali::Toolkit::TextInput handle(*textInput);
262 handle.SetName( "TextInput");
264 textInput->Initialize();
268 TextInput::TextInput()
269 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
274 mDisplayedTextView(),
275 mStyledPlaceHolderText(),
276 mMaxStringLength( DEFAULT_MAX_SIZE ),
277 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
278 mCursorPosition( 0 ),
279 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
280 mIsSelectionHandleOneFlipped( false ),
281 mIsSelectionHandleTwoFlipped( false ),
282 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
283 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
284 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
285 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
286 mSelectionHandleOnePosition( 0 ),
287 mSelectionHandleTwoPosition( 0 ),
289 mPreEditStartPosition( 0 ),
290 mPreEditLength ( 0 ),
291 mNumberOfSurroundingCharactersDeleted( 0 ),
292 mTouchStartTime( 0 ),
294 mCurrentCopySelecton(),
297 mScrollDisplacement(),
298 mCurrentHandlePosition(),
299 mCurrentSelectionId(),
300 mCurrentSelectionHandlePosition(),
301 mRequestedSelection( 0, 0 ),
302 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
303 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
305 mMaterialColor( LIGHTBLUE ),
306 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
307 mOverrideAutomaticAlignment( false ),
308 mCursorRTLEnabled( false ),
309 mClosestCursorPositionEOL ( false ),
310 mCursorBlinkStatus( true ),
311 mCursorVisibility( false ),
312 mGrabHandleVisibility( false ),
313 mIsCursorInScrollArea( true ),
314 mIsGrabHandleInScrollArea( true ),
315 mEditModeActive( false ),
316 mEditOnTouch( true ),
317 mTextSelection( true ),
318 mExceedEnabled( true ),
319 mGrabHandleEnabled( true ),
320 mIsSelectionHandleFlipEnabled( true ),
321 mPreEditFlag( false ),
322 mIgnoreCommitFlag( false ),
323 mIgnoreFirstCommitFlag( false ),
324 mSelectingText( false ),
325 mPreserveCursorPosition( false ),
326 mSelectTextOnCommit( false ),
327 mUnderlinedPriorToPreEdit ( false ),
328 mCommitByKeyInput( false ),
329 mPlaceHolderSet( false ),
330 mMarkUpEnabled( false )
332 // Updates the line height accordingly with the input style.
336 TextInput::~TextInput()
338 StopCursorBlinkTimer();
343 std::string TextInput::GetText() const
347 // Return text-view's text only if the text-input's text is not empty
348 // in order to not to return the placeholder text.
349 if( !mStyledText.empty() )
351 text = mDisplayedTextView.GetText();
357 std::string TextInput::GetMarkupText() const
359 std::string markupString;
360 MarkupProcessor::GetMarkupString( mStyledText, markupString );
365 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
367 mDisplayedTextView.SetText( stylePlaceHolderText );
368 mPlaceHolderSet = true;
369 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
372 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
374 // Get the placeholder styled text array from the markup string.
375 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
376 if( mStyledText.empty() )
378 ShowPlaceholderText( mStyledPlaceHolderText );
382 std::string TextInput::GetPlaceholderText()
384 // Traverses the styled placeholder array getting only the text.
385 // Note that for some languages a 'character' could be represented by more than one 'char'
387 std::string placeholderText;
388 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
390 placeholderText.append( (*it).mText.GetText() );
393 return placeholderText ;
396 void TextInput::SetInitialText(const std::string& initialText)
398 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
400 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
402 mPreEditFlag = false;
403 mIgnoreCommitFlag = true;
406 SetText( initialText );
407 PreEditReset( false ); // Reset keyboard as text changed
410 void TextInput::SetText(const std::string& initialText)
412 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
414 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
416 if( mStyledText.empty() )
418 ShowPlaceholderText( mStyledPlaceHolderText );
422 mDisplayedTextView.SetText( mStyledText );
423 mPlaceHolderSet = false;
428 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
430 ImfManager imfManager = ImfManager::Get();
433 imfManager.SetCursorPosition( mCursorPosition );
434 imfManager.SetSurroundingText( initialText );
435 imfManager.NotifyCursorPosition();
438 if( IsScrollEnabled() )
440 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
443 ShowGrabHandleAndSetVisibility( false );
452 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
454 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
456 mDisplayedTextView.SetText( styleText );
457 mPlaceHolderSet = false;
459 // If text alignment hasn't been manually set by application developer, then we
460 // automatically determine the alignment based on the content of the text i.e. what
461 // language the text begins with.
462 // TODO: This should determine different alignments for each line (broken by '\n') of text.
463 if(!mOverrideAutomaticAlignment)
465 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
466 bool leftToRight(true);
468 if( !styleText.empty() )
470 bool breakOut(false);
472 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
474 const Text& text = textIter->mText;
476 for( std::size_t i = 0; i < text.GetLength(); ++i )
478 Character character( text[i] );
479 if( character.GetCharacterDirection() != Character::Neutral )
481 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
489 // Based on this direction, either left or right align text if not manually set by application developer.
490 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
491 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
492 Toolkit::Alignment::VerticalTop ) );
493 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
499 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
501 mMaxStringLength = maxChars;
504 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
506 DALI_ASSERT_DEBUG( maxLines > 0 )
510 mNumberOflinesLimit = maxLines;
514 std::size_t TextInput::GetNumberOfLinesLimit() const
516 return mNumberOflinesLimit;
519 std::size_t TextInput::GetNumberOfCharacters() const
521 return mStyledText.size();
525 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
527 mMaterialColor = color;
528 if ( mCustomMaterial )
530 mCustomMaterial.SetDiffuseColor( mMaterialColor );
531 mMeshData.SetMaterial( mCustomMaterial );
535 const Vector4& TextInput::GetMaterialDiffuseColor() const
537 return mMaterialColor;
542 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
544 return mInputStartedSignal;
547 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
549 return mInputFinishedSignal;
552 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
554 return mCutAndPasteToolBarDisplayed;
557 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
559 return mStyleChangedSignal;
562 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
564 return mTextModifiedSignal;
567 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
569 return mMaxInputCharactersReachedSignal;
572 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
574 return mInputTextExceedBoundariesSignal;
577 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
579 Dali::BaseHandle handle( object );
581 bool connected( true );
582 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
584 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
586 textInput.InputStartedSignal().Connect( tracker, functor );
588 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
590 textInput.InputFinishedSignal().Connect( tracker, functor );
592 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
594 textInput.StyleChangedSignal().Connect( tracker, functor );
596 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
598 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
600 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
602 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
606 // signalName does not match any signal
613 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
617 // update line height before calculate the actual position.
622 if( setCursorOnTouchPoint )
624 // Sets the cursor position for the given touch point.
625 ReturnClosestIndex( touchPoint, mCursorPosition );
627 // Creates the grab handle.
628 if( IsGrabHandleEnabled() )
630 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
634 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
635 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
636 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
637 ShowGrabHandleAndSetVisibility( true );
639 // Scrolls the text-view if needed.
640 if( IsScrollEnabled() )
642 ScrollTextViewToMakeCursorVisible( cursorPosition );
648 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
660 bool TextInput::IsEditable() const
662 return mEditModeActive;
665 void TextInput::SetEditOnTouch( bool editOnTouch )
667 mEditOnTouch = editOnTouch;
670 bool TextInput::IsEditOnTouch() const
675 void TextInput::SetTextSelectable( bool textSelectable )
677 mTextSelection = textSelectable;
680 bool TextInput::IsTextSelectable() const
682 return mTextSelection;
685 bool TextInput::IsTextSelected() const
687 return mHighlightMeshActor;
690 void TextInput::DeSelectText()
697 void TextInput::SetGrabHandleImage(Dali::Image image )
701 CreateGrabHandle(image);
705 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
707 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
711 mCursor.SetImage( image );
712 mCursor.SetNinePatchBorder( border );
716 Vector3 TextInput::GetSelectionHandleSize()
718 return DEFAULT_SELECTION_HANDLE_SIZE;
721 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
723 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
727 mCursorRTL.SetImage( image);
728 mCursorRTL.SetNinePatchBorder( border );
732 void TextInput::EnableGrabHandle(bool toggle)
734 // enables grab handle with will in turn de-activate magnifier
735 mGrabHandleEnabled = toggle;
738 bool TextInput::IsGrabHandleEnabled()
740 // if false then magnifier will be shown instead.
741 return mGrabHandleEnabled;
744 void TextInput::EnableSelectionHandleFlip( bool toggle )
746 // Deprecated function. To be removed.
747 mIsSelectionHandleFlipEnabled = toggle;
750 bool TextInput::IsSelectionHandleFlipEnabled()
752 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
756 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
758 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
759 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
760 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
762 mSelectionHandleFlipMargin = margin;
765 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
767 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
768 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
770 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
771 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
773 const Vector4 boundary( originX,
775 originX + boundingRectangle.width,
776 originY + boundingRectangle.height );
778 mBoundingRectangleWorldCoordinates = boundary;
781 const Rect<float> TextInput::GetBoundingRectangle() const
783 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
785 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
786 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
788 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
793 const Vector4& TextInput::GetSelectionHandleFlipMargin()
795 return mSelectionHandleFlipMargin;
798 void TextInput::SetTextColor( const Vector4& color )
800 mDisplayedTextView.SetColor( color );
803 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
805 if( style != mInputStyle )
808 bool emitSignal = false;
810 // mask: modify style according to mask, if different emit signal.
811 const TextStyle oldInputStyle( mInputStyle );
813 // Copy the new style.
814 mInputStyle.Copy( style, mask );
816 // if style has changed, emit signal.
817 if( oldInputStyle != mInputStyle )
822 // Updates the line height accordingly with the input style.
825 // Changing font point size will require the cursor to be re-sized
830 EmitStyleChangedSignal();
835 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
837 if ( IsTextSelected() )
839 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
840 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
842 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
844 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
847 // Keeps the old style to be compared with the new one.
848 const TextStyle oldInputStyle( mInputStyle );
850 // Copy only those parameters from the style which are set in the mask.
851 mInputStyle.Copy( style, mask );
853 if( mInputStyle != oldInputStyle )
855 // Updates the line height accordingly with the input style.
858 EmitStyleChangedSignal();
863 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
865 if( !mStyledText.empty() )
867 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
871 TextStyle TextInput::GetStyleAtCursor() const
875 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
877 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
878 style = mStyledText.at( mCursorPosition-1 ).mStyle;
884 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
886 Dali::Font defaultFont = Dali::Font::New();
887 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
894 TextStyle TextInput::GetStyleAt( std::size_t position ) const
896 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
898 if( position >= mStyledText.size() )
900 position = mStyledText.size() - 1;
903 return mStyledText.at( position ).mStyle;
906 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
908 mDisplayedTextView.SetTextAlignment( align );
909 mOverrideAutomaticAlignment = true;
912 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
914 mDisplayedTextView.SetLineJustification( justification );
915 mOverrideAutomaticAlignment = true;
918 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
920 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
923 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
925 return mDisplayedTextView.GetFadeBoundary();
928 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
930 return mDisplayedTextView.GetTextAlignment();
933 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
935 mDisplayedTextView.SetMultilinePolicy( policy );
938 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
940 return mDisplayedTextView.GetMultilinePolicy();
943 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
945 mDisplayedTextView.SetWidthExceedPolicy( policy );
948 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
950 return mDisplayedTextView.GetWidthExceedPolicy();
953 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
955 mDisplayedTextView.SetHeightExceedPolicy( policy );
958 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
960 return mDisplayedTextView.GetHeightExceedPolicy();
963 void TextInput::SetExceedEnabled( bool enable )
965 mExceedEnabled = enable;
968 bool TextInput::GetExceedEnabled() const
970 return mExceedEnabled;
973 void TextInput::SetBackground(Dali::Image image )
975 // TODO Should add this function and add public api to match.
978 bool TextInput::OnTouchEvent(const TouchEvent& event)
983 bool TextInput::OnKeyEvent(const KeyEvent& event)
985 switch( event.state )
989 return OnKeyDownEvent(event);
995 return OnKeyUpEvent(event);
1007 void TextInput::OnKeyInputFocusGained()
1009 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1011 mEditModeActive = true;
1013 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1015 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1017 // Updates the line height accordingly with the input style.
1020 // Connect the signals to use in text input.
1021 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1022 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1024 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1027 GetTextLayoutInfo();
1030 SetCursorVisibility( true );
1031 StartCursorBlinkTimer();
1033 Toolkit::TextInput handle( GetOwner() );
1034 mInputStartedSignal.Emit( handle );
1036 ImfManager imfManager = ImfManager::Get();
1040 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1042 // Notify that the text editing start.
1043 imfManager.Activate();
1045 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1046 imfManager.SetRestoreAfterFocusLost( true );
1048 imfManager.SetCursorPosition( mCursorPosition );
1049 imfManager.NotifyCursorPosition();
1052 mClipboard = Clipboard::Get(); // Store handle to clipboard
1054 // Now in edit mode we can accept string to paste from clipboard
1055 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1058 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1062 void TextInput::OnKeyInputFocusLost()
1064 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1068 // If key input focus is lost, it removes the
1069 // underline from the last pre-edit text.
1070 RemovePreEditStyle();
1071 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1072 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1076 ImfManager imfManager = ImfManager::Get();
1079 // The text editing is finished. Therefore the imf manager don't have restore activation.
1080 imfManager.SetRestoreAfterFocusLost( false );
1082 // Notify that the text editing finish.
1083 imfManager.Deactivate();
1085 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1087 // Disconnect signal used the text input.
1088 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1090 Toolkit::TextInput handle( GetOwner() );
1091 mInputFinishedSignal.Emit( handle );
1092 mEditModeActive = false;
1093 mPreEditFlag = false;
1095 SetCursorVisibility( false );
1096 StopCursorBlinkTimer();
1098 ShowGrabHandleAndSetVisibility( false );
1101 // No longer in edit mode so do not want to receive string from clipboard
1102 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1105 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1108 Clipboard clipboard = Clipboard::Get();
1111 clipboard.HideClipboard();
1115 void TextInput::OnControlStageConnection()
1117 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1119 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1121 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1125 void TextInput::CreateActiveLayer()
1127 Actor self = Self();
1128 mActiveLayer = Layer::New();
1129 mActiveLayer.SetName ( "ActiveLayerActor" );
1131 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1132 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1133 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1135 self.Add( mActiveLayer );
1136 mActiveLayer.RaiseToTop();
1139 void TextInput::OnInitialize()
1141 CreateTextViewActor();
1145 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1146 // different positions depending on language)
1147 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1148 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1150 Actor self = Self();
1151 self.Add( mCursor );
1152 self.Add( mCursorRTL );
1154 mCursorVisibility = false;
1156 CreateActiveLayer(); // todo move this so layer only created when needed.
1158 // Assign names to image actors
1159 mCursor.SetName("mainCursor");
1160 mCursorRTL.SetName("rtlCursor");
1163 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1165 mDisplayedTextView.SetSize( targetSize );
1166 GetTextLayoutInfo();
1167 mActiveLayer.SetSize(targetSize);
1170 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1172 Relayout( mDisplayedTextView, size, container );
1173 Relayout( mPopupPanel.GetRootActor(), size, container );
1175 GetTextLayoutInfo();
1180 Vector3 TextInput::GetNaturalSize()
1182 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1184 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1186 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1187 naturalSize.height = mLineHeight;
1193 float TextInput::GetHeightForWidth( float width )
1195 float height = mDisplayedTextView.GetHeightForWidth( width );
1197 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1199 // If the height is zero, it means there is no text. Let's return the cursor height.
1200 height = mLineHeight;
1206 /*end of Virtual methods from parent*/
1208 // Private Internal methods
1210 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1212 switch (gesture.state)
1214 case Gesture::Started:
1215 // fall through so code not duplicated
1216 case Gesture::Continuing:
1218 if (actor == mGrabArea)
1220 SetCursorVisibility( true );
1221 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1222 MoveGrabHandle( gesture.displacement );
1223 HidePopup(); // Do not show popup whilst handle is moving
1225 else if (actor == mHandleOneGrabArea)
1227 // the displacement in PanGesture is affected by the actor's rotation.
1228 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1229 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1231 MoveSelectionHandle( HandleOne, gesture.displacement );
1233 mState = StateDraggingHandle;
1236 else if (actor == mHandleTwoGrabArea)
1238 // the displacement in PanGesture is affected by the actor's rotation.
1239 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1240 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1242 MoveSelectionHandle( HandleTwo, gesture.displacement );
1244 mState = StateDraggingHandle;
1250 case Gesture::Finished:
1252 // Revert back to non-pressed selection handle images
1253 if (actor == mGrabArea)
1255 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1256 SetCursorVisibility( true );
1257 SetUpPopupSelection();
1260 if (actor == mHandleOneGrabArea)
1262 // the displacement in PanGesture is affected by the actor's rotation.
1263 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1264 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1266 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1268 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1270 ShowPopupCutCopyPaste();
1272 if (actor == mHandleTwoGrabArea)
1274 // the displacement in PanGesture is affected by the actor's rotation.
1275 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1276 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1278 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1280 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1282 ShowPopupCutCopyPaste();
1291 // Stop the flashing animation so easy to see when moved.
1292 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1294 if (touch.GetPoint(0).state == TouchPoint::Down)
1296 SetCursorVisibility( true );
1297 StopCursorBlinkTimer();
1299 else if (touch.GetPoint(0).state == TouchPoint::Up)
1301 SetCursorVisibility( true );
1302 StartCursorBlinkTimer();
1307 // selection handle one
1308 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1310 if (touch.GetPoint(0).state == TouchPoint::Down)
1312 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1314 else if (touch.GetPoint(0).state == TouchPoint::Up)
1316 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1321 // selection handle two
1322 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1324 if (touch.GetPoint(0).state == TouchPoint::Down)
1326 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1328 else if (touch.GetPoint(0).state == TouchPoint::Up)
1330 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1335 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1337 // If text exists then select nearest word.
1338 if ( !mStyledText.empty())
1342 ShowGrabHandleAndSetVisibility( false );
1347 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1348 // converts the pre-edit word being displayed to a committed word.
1349 if ( !mUnderlinedPriorToPreEdit )
1352 style.SetUnderline( false );
1353 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1355 mPreEditFlag = false;
1356 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1357 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1358 PreEditReset( false );
1360 mCursorPosition = 0;
1362 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1363 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1365 std::size_t start = 0;
1366 std::size_t end = 0;
1367 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1369 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1371 ImfManager imfManager = ImfManager::Get();
1374 imfManager.SetCursorPosition ( mCursorPosition );
1375 imfManager.NotifyCursorPosition();
1378 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1380 SelectText( start, end );
1381 ShowPopupCutCopyPaste();
1385 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1386 HidePopup( false ); // Hide popup with setting to do auto show.
1387 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1391 else if ( mClipboard && mClipboard.NumberOfItems() )
1393 ShowPopupCutCopyPaste();
1396 // If no text and clipboard empty then do nothing
1399 // TODO: Change the function name to be more general.
1400 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1402 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1403 , (mEditOnTouch)?"true":"false"
1404 , (mEditModeActive)?"true":"false");
1406 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1411 if( mGrabArea == actor )
1413 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1415 SetUpPopupSelection();
1425 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1427 // Initially don't create the grab handle.
1428 bool createGrabHandle = false;
1430 if ( !mEditModeActive )
1432 // update line height before calculate the actual position.
1435 // Only start edit mode if TextInput configured to edit on touch
1438 // Set the initial cursor position in the tap point.
1439 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1445 // Show the keyboard if it was hidden.
1446 if (!VirtualKeyboard::IsVisible())
1448 VirtualKeyboard::Show();
1451 // Reset keyboard as tap event has occurred.
1452 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1453 PreEditReset( true );
1455 GetTextLayoutInfo();
1457 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1459 // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1461 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1463 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1465 // Notify keyboard so it can 're-capture' word for predictive text.
1466 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1467 ImfManager imfManager = ImfManager::Get();
1470 imfManager.SetCursorPosition ( mCursorPosition );
1471 imfManager.NotifyCursorPosition();
1473 const TextStyle oldInputStyle( mInputStyle );
1475 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1479 // Create the grab handle.
1480 // Grab handle is created later.
1481 createGrabHandle = true;
1483 if( oldInputStyle != mInputStyle )
1485 // Updates the line height accordingly with the input style.
1488 EmitStyleChangedSignal();
1493 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1494 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1495 // otherwise the Grab handle will be shown when selecting.
1496 if ( createGrabHandle && IsGrabHandleEnabled() )
1498 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1499 bool altPositionValid; // Alternate cursor validity flag.
1500 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1501 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1503 if( altPositionValid )
1505 // Check which of the positions is the closest.
1506 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1508 cursorPosition = altPosition;
1514 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1515 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1516 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1517 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1522 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1524 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1526 // Ignore longpress if in selection mode already
1527 if( mHighlightMeshActor )
1532 if(longPress.state == Dali::Gesture::Started)
1534 // Start edit mode on long press
1535 if ( !mEditModeActive )
1540 // If text exists then select nearest word.
1541 if ( !mStyledText.empty())
1545 ShowGrabHandleAndSetVisibility( false );
1550 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1551 // converts the pre-edit word being displayed to a committed word.
1552 if ( !mUnderlinedPriorToPreEdit )
1555 style.SetUnderline( false );
1556 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1558 mPreEditFlag = false;
1559 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1560 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1561 PreEditReset( false );
1563 mCursorPosition = 0;
1565 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1566 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1568 std::size_t start = 0;
1569 std::size_t end = 0;
1570 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1572 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1574 ImfManager imfManager = ImfManager::Get();
1577 imfManager.SetCursorPosition ( mCursorPosition );
1578 imfManager.NotifyCursorPosition();
1581 SelectText( start, end );
1584 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1585 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1587 ShowPopupCutCopyPaste();
1592 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1594 const Text clipboardText( notifier.GetContent() );
1595 PasteText( clipboardText );
1597 SetCursorVisibility( true );
1598 StartCursorBlinkTimer();
1600 ShowGrabHandleAndSetVisibility( false );
1606 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1608 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1610 const std::string& name = button.GetName();
1612 if(name == TextInputPopup::OPTION_SELECT_WORD)
1614 std::size_t start = 0;
1615 std::size_t end = 0;
1616 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1618 SelectText( start, end );
1620 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1622 SetCursorVisibility(false);
1623 StopCursorBlinkTimer();
1625 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1626 std::size_t start = 0;
1628 SelectText( start, end );
1630 else if(name == TextInputPopup::OPTION_CUT)
1632 bool ret = CopySelectedTextToClipboard();
1636 DeleteHighlightedText( true );
1640 SetCursorVisibility( true );
1641 StartCursorBlinkTimer();
1645 else if(name == TextInputPopup::OPTION_COPY)
1647 CopySelectedTextToClipboard();
1651 SetCursorVisibility( true );
1652 StartCursorBlinkTimer();
1656 else if(name == TextInputPopup::OPTION_PASTE)
1658 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1660 PasteText(retrievedString);
1662 SetCursorVisibility( true );
1663 StartCursorBlinkTimer();
1665 ShowGrabHandleAndSetVisibility( false );
1669 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1671 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1672 // Hence pass the false parameter for signalFinished.
1673 HidePopup( true, false );
1674 mClipboard.ShowClipboard();
1680 bool TextInput::OnCursorBlinkTimerTick()
1683 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1684 if ( mCursorRTLEnabled )
1686 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1688 mCursorBlinkStatus = !mCursorBlinkStatus;
1693 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1695 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1697 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1698 if(mHighlightMeshActor && mState == StateEdit)
1700 ShowPopupCutCopyPaste();
1704 //FIXME this routine needs to be re-written as it contains too many branches.
1705 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1707 std::string keyName = event.keyPressedName;
1708 std::string keyString = event.keyPressed;
1710 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1712 // Do not consume "Tab" and "Escape" keys.
1713 if(keyName == "Tab" || keyName == "Escape")
1715 // Escape key to end the edit mode
1721 HidePopup(); // If Pop-up shown then hides it as editing text.
1723 // Update Flag, indicates whether to update the text-input contents or not.
1724 // Any key stroke that results in a visual change of the text-input should
1725 // set this flag to true.
1728 // Whether to scroll text to cursor position.
1729 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1730 bool scroll = false;
1732 if (keyName == "Return")
1734 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1736 bool preEditFlagPreviouslySet( mPreEditFlag );
1738 // replaces highlighted text with new line
1739 DeleteHighlightedText( false );
1741 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1743 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1744 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1747 mCommitByKeyInput = true;
1750 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1751 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1753 mPreEditFlag = true;
1754 mIgnoreCommitFlag = false;
1764 else if ( keyName == "space" )
1766 if ( mHighlightMeshActor )
1768 // Some text is selected so erase it before adding space.
1769 DeleteHighlightedText( true );
1772 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1774 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1775 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1778 mCommitByKeyInput = true;
1783 else if (keyName == "BackSpace")
1785 if ( mHighlightMeshActor )
1787 // Some text is selected so erase it
1788 DeleteHighlightedText( true );
1793 if ( mCursorPosition > 0 )
1795 DeleteCharacter( mCursorPosition );
1801 else if (keyName == "Right")
1806 else if (keyName == "Left")
1808 AdvanceCursor(true);
1811 else // event is a character
1813 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1814 if ( !keyString.empty() )
1816 // replaces highlighted text with new character
1817 DeleteHighlightedText( false );
1819 // Received key String
1820 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1826 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1827 // as this is a costly operation.
1833 if(update || scroll)
1835 if( IsScrollEnabled() )
1837 // Calculates the new cursor position (in actor coordinates)
1838 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1840 ScrollTextViewToMakeCursorVisible( cursorPosition );
1847 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1849 std::string keyName = event.keyPressedName;
1850 std::string keyString = event.keyPressed;
1852 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1854 // The selected text become deselected when the key code is DALI_KEY_BACK.
1855 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1864 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1865 const Vector3& cursorPositionTwo,
1866 bool altPositionValidOne,
1867 bool altPositionValidTwo,
1868 const Vector3& altPositionOne,
1869 const Vector3& altPositionTwo )
1871 // TODO VCC Valid for one line.
1872 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1873 if( cursorPositionOne != cursorPositionTwo )
1875 if( cursorPositionOne.x < cursorPositionTwo.x )
1877 mSelectionHandleOneActualPosition = cursorPositionOne;
1878 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1882 mSelectionHandleOneActualPosition = cursorPositionTwo;
1883 mSelectionHandleTwoActualPosition = cursorPositionOne;
1888 mSelectionHandleOneActualPosition = cursorPositionOne;
1889 if( altPositionValidOne )
1891 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1893 mSelectionHandleOneActualPosition = altPositionOne;
1896 if( altPositionValidTwo )
1898 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1900 mSelectionHandleOneActualPosition = altPositionTwo;
1904 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1905 if( altPositionValidTwo )
1907 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1909 mSelectionHandleTwoActualPosition = altPositionTwo;
1912 if( altPositionValidOne )
1914 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1916 mSelectionHandleTwoActualPosition = altPositionOne;
1922 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1924 // Updates the stored scroll position.
1925 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1927 const Vector3& controlSize = GetControlSize();
1928 Size cursorSize( CURSOR_THICKNESS, 0.f );
1930 // Updates the cursor and grab handle position and visibility.
1931 if( mGrabHandle || mCursor )
1933 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1935 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1936 bool altPositionValid; // Alternate cursor validity flag.
1937 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1938 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1940 if( altPositionValid )
1942 // Check which of the positions is the closest.
1943 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1945 cursorPosition = altPosition;
1949 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1951 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1955 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1956 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1961 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1962 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1966 // Updates the selection handles and highlighted text position and visibility.
1967 if( mSelectionHandleOne && mSelectionHandleTwo )
1969 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1970 bool altPositionValidOne; // Alternate cursor validity flag.
1971 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1972 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1974 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1975 bool altPositionValidTwo; // Alternate cursor validity flag.
1976 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1977 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1979 // VCC TODO: This method is a hack for one line.
1980 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1982 altPositionValidOne,
1983 altPositionValidTwo,
1987 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1988 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1989 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1990 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1992 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1993 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1994 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1995 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1997 if( mHighlightMeshActor )
1999 mHighlightMeshActor.SetVisible( true );
2005 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2007 // Scroll the text to make the cursor visible.
2008 const Size cursorSize( CURSOR_THICKNESS,
2009 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2011 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2013 const Vector3& controlSize = GetControlSize();
2015 // Calculates the new scroll position.
2016 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2017 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2019 scrollOffset.x += cursorPosition.x;
2022 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2024 scrollOffset.y += cursorPosition.y;
2027 // Sets the new scroll position.
2028 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2029 SetScrollPosition( scrollOffset );
2032 void TextInput::StartScrollTimer()
2036 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2037 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2040 if( !mScrollTimer.IsRunning() )
2042 mScrollTimer.Start();
2046 void TextInput::StopScrollTimer()
2050 mScrollTimer.Stop();
2054 bool TextInput::OnScrollTimerTick()
2056 // TODO: need to set the new style accordingly the new handle position.
2058 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2060 // nothing to do if all handles are invisible or doesn't exist.
2066 // Choose between the grab handle or the selection handles.
2067 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2068 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2069 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2071 std::size_t newCursorPosition = 0;
2072 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2074 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2075 // the new selection handle's position needs to be different of the other one.
2076 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2077 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2078 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2080 if( differentSelectionHandles )
2082 handlePosition = newCursorPosition;
2084 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2086 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2088 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2089 scrollPosition += scrollDelta;
2090 SetScrollPosition( scrollPosition );
2092 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2097 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2100 actualHandlePosition.x += mScrollDisplacement.x;
2101 actualHandlePosition.y += mScrollDisplacement.y;
2106 // Public Internal Methods (public for testing purpose)
2108 void TextInput::SetUpTouchEvents()
2110 if ( !mTapDetector )
2112 mTapDetector = TapGestureDetector::New();
2113 // Attach the actors and connect the signal
2114 mTapDetector.Attach(Self());
2116 // As contains children which may register for tap the default control detector is not used.
2117 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2120 if ( !mDoubleTapDetector )
2122 mDoubleTapDetector = TapGestureDetector::New();
2123 mDoubleTapDetector.SetTapsRequired( 2 );
2124 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2126 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2127 // so that we do not, unnecessarily, have a double tap request all the time
2130 if ( !mPanGestureDetector )
2132 mPanGestureDetector = PanGestureDetector::New();
2133 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2136 if ( !mLongPressDetector )
2138 mLongPressDetector = LongPressGestureDetector::New();
2139 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2140 mLongPressDetector.Attach(Self());
2144 void TextInput::CreateTextViewActor()
2146 mDisplayedTextView = Toolkit::TextView::New();
2147 mDisplayedTextView.SetName( "DisplayedTextView ");
2148 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2149 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2150 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2151 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2152 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2153 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2154 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2155 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2156 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2157 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2159 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2161 Self().Add( mDisplayedTextView );
2164 // Start a timer to initiate, used by the cursor to blink.
2165 void TextInput::StartCursorBlinkTimer()
2167 if ( !mCursorBlinkTimer )
2169 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2170 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2173 if ( !mCursorBlinkTimer.IsRunning() )
2175 mCursorBlinkTimer.Start();
2179 // Start a timer to initiate, used by the cursor to blink.
2180 void TextInput::StopCursorBlinkTimer()
2182 if ( mCursorBlinkTimer )
2184 mCursorBlinkTimer.Stop();
2188 void TextInput::StartEditMode()
2190 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2192 if(!mEditModeActive)
2197 if ( mDoubleTapDetector )
2199 mDoubleTapDetector.Attach( Self() );
2203 void TextInput::EndEditMode()
2205 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2207 ClearKeyInputFocus();
2209 if ( mDoubleTapDetector )
2211 mDoubleTapDetector.Detach( Self() );
2215 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2217 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2219 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2221 style.SetUnderline( true );
2222 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2226 void TextInput::RemovePreEditStyle()
2228 if ( !mUnderlinedPriorToPreEdit )
2231 style.SetUnderline( false );
2232 SetActiveStyle( style, TextStyle::UNDERLINE );
2236 // IMF related methods
2239 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2241 bool update( false );
2242 bool preeditResetRequired ( false );
2244 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2246 HidePopup(); // If Pop-up shown then hides it as editing text.
2249 switch ( imfEvent.eventName )
2251 case ImfManager::PREEDIT:
2253 mIgnoreFirstCommitFlag = false;
2255 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2256 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2258 // replaces highlighted text with new character
2259 DeleteHighlightedText( false );
2262 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2264 if( IsScrollEnabled() )
2266 // Calculates the new cursor position (in actor coordinates)
2267 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2268 ScrollTextViewToMakeCursorVisible( cursorPosition );
2275 case ImfManager::COMMIT:
2277 if( mIgnoreFirstCommitFlag )
2279 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2280 mIgnoreFirstCommitFlag = false;
2284 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2286 // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2287 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2289 // replaces highlighted text with new character
2290 DeleteHighlightedText( false );
2293 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2294 // not needed, one such scenario is when the pre-edit word is too long to fit.
2295 if ( !mIgnoreCommitFlag )
2297 update = CommitReceived( imfEvent.predictiveString );
2301 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2307 if( IsScrollEnabled() )
2309 // Calculates the new cursor position (in actor coordinates)
2310 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2312 ScrollTextViewToMakeCursorVisible( cursorPosition );
2317 case ImfManager::DELETESURROUNDING:
2319 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2320 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2322 mPreEditFlag = false;
2324 std::size_t toDelete = 0;
2325 std::size_t numberOfCharacters = 0;
2327 if( mHighlightMeshActor )
2329 // delete highlighted text.
2330 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2331 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2335 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2337 toDelete = mCursorPosition + imfEvent.cursorOffset;
2339 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2341 numberOfCharacters = mStyledText.size() - toDelete;
2345 numberOfCharacters = imfEvent.numberOfChars;
2348 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2349 DeleteRange( toDelete, numberOfCharacters );
2351 mCursorPosition = toDelete;
2352 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2356 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2359 case ImfManager::GETSURROUNDING:
2361 // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2362 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2363 if (! ( mHighlightMeshActor || mSelectingText ) )
2365 std::string text( GetText() );
2366 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2368 imfManager.SetCursorPosition( mCursorPosition );
2369 imfManager.SetSurroundingText( text );
2372 if( 0 != mNumberOfSurroundingCharactersDeleted )
2374 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2375 mNumberOfSurroundingCharactersDeleted = 0;
2377 if( mStyledText.empty() )
2379 ShowPlaceholderText( mStyledPlaceHolderText );
2384 case ImfManager::VOID:
2386 DALI_ASSERT_DEBUG( false );
2390 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2392 return callbackData;
2395 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2397 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2399 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2400 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2402 bool preeditResetRequest ( false );
2404 if( mPreEditFlag ) // Already in pre-edit state.
2406 if( mStyledText.size() >= mMaxStringLength )
2408 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2409 // Cannot fit these characters into field, clear pre-edit.
2410 if ( !mUnderlinedPriorToPreEdit )
2413 style.SetUnderline( false );
2414 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2416 mIgnoreCommitFlag = true;
2417 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2418 mPreEditFlag = false;
2419 EmitMaxInputCharactersReachedSignal();
2423 // delete existing pre-edit string
2424 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2426 // Store new pre-edit string
2427 mPreEditString.SetText( keyString );
2429 if ( keyString.empty() )
2431 mPreEditFlag = false;
2432 mCursorPosition = mPreEditStartPosition;
2434 if( mStyledText.empty() )
2436 ShowPlaceholderText( mStyledPlaceHolderText );
2440 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2443 GetTextLayoutInfo();
2448 // Insert new pre-edit string. InsertAt updates the size and position table.
2449 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2450 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2451 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2452 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2453 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2456 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2460 else // mPreEditFlag not set
2462 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2464 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2465 // new pre-edit so move into pre-edit state by setting flag
2466 mPreEditFlag = true;
2467 mPreEditString.SetText( keyString ); // store new pre-edit string
2468 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2469 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2470 // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2471 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2472 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2473 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2474 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2480 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2484 return preeditResetRequest;
2487 bool TextInput::CommitReceived(const std::string& keyString )
2489 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2490 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2492 bool update( false );
2494 RemovePreEditStyle();
2496 const std::size_t styledTextSize( mStyledText.size() );
2497 if( styledTextSize >= mMaxStringLength )
2499 // Cannot fit these characters into field, clear pre-edit.
2502 mIgnoreCommitFlag = true;
2503 mPreEditFlag = false;
2505 EmitMaxInputCharactersReachedSignal();
2511 // delete existing pre-edit string
2512 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2513 mPreEditFlag = false;
2515 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2516 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2518 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2520 // No need to update cursor position as Cursor location given by touch.
2521 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2522 mPreserveCursorPosition = false;
2526 // Cursor not set by touch so needs to be re-positioned to input more text
2527 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2529 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2530 if ( mCommitByKeyInput )
2532 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2533 mCommitByKeyInput = false;
2539 if ( mSelectTextOnCommit )
2541 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2546 else // mPreEditFlag not set
2548 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2550 if( mStyledText.empty() && mPlaceHolderSet )
2552 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2553 mDisplayedTextView.SetText( "" );
2554 mNumberOfSurroundingCharactersDeleted = 0;
2555 mPlaceHolderSet = false;
2557 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2559 mNumberOfSurroundingCharactersDeleted = 0;
2564 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2569 mSelectTextOnCommit = false;
2571 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2572 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2577 // End of IMF related methods
2579 std::size_t TextInput::DeletePreEdit()
2581 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2583 DALI_ASSERT_DEBUG( mPreEditFlag );
2585 const std::size_t preEditStringLength = mPreEditString.GetLength();
2586 const std::size_t styledTextSize = mStyledText.size();
2588 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2590 // Prevents erase items outside mStyledText bounds.
2591 if( mPreEditStartPosition > styledTextSize )
2593 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2594 mPreEditStartPosition = styledTextSize;
2597 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2599 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2600 endPosition = styledTextSize;
2603 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2605 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2606 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2608 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2610 return preEditStringLength;
2613 void TextInput::PreEditReset( bool preserveCursorPosition )
2615 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2616 preserveCursorPosition, mCursorPosition);
2618 // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2619 mPreserveCursorPosition = preserveCursorPosition;
2621 // Reset incase we are in a pre-edit state.
2622 ImfManager imfManager = ImfManager::Get();
2625 imfManager.Reset(); // Will trigger a commit message
2629 void TextInput::CursorUpdate()
2633 ImfManager imfManager = ImfManager::Get();
2636 std::string text( GetText() );
2637 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2638 imfManager.SetCursorPosition ( mCursorPosition );
2639 imfManager.NotifyCursorPosition();
2643 /* Delete highlighted characters redisplay*/
2644 void TextInput::DeleteHighlightedText( bool inheritStyle )
2646 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2648 if( mHighlightMeshActor )
2650 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2652 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2653 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2655 // Get the styled text of the characters to be deleted as it may be needed if
2656 // the "exceed the text-input's boundaries" option is disabled.
2657 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2659 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2661 mStyledText.erase( start, end ); // erase range of characters
2663 // Remove text from TextView and update place holder text if required
2665 // Set the placeholder text only if the styled text is empty.
2666 if( mStyledText.empty() )
2668 ShowPlaceholderText( mStyledPlaceHolderText );
2672 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2674 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2676 // It may happen than after removing a white space or a new line character,
2677 // two words merge, this new word could be big enough to not fit in its
2678 // current line, so moved to the next one, and make some part of the text to
2679 // exceed the text-input's boundary.
2680 if( !mExceedEnabled )
2682 // Get the new text layout after removing some characters.
2683 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2685 // Get text-input's size.
2686 const Vector3& size = GetControlSize();
2688 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2689 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2691 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2693 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2694 styledCharactersToDelete.begin(),
2695 styledCharactersToDelete.end() );
2699 GetTextLayoutInfo();
2707 const TextStyle oldInputStyle( mInputStyle );
2709 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2711 if( oldInputStyle != mInputStyle )
2713 // Updates the line height accordingly with the input style.
2716 EmitStyleChangedSignal();
2722 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2724 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2725 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2727 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2730 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2732 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2733 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2735 mStyledText.erase(itStart, itEnd);
2737 // update the selection handles if they are visible.
2738 if( mHighlightMeshActor )
2740 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2741 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2743 if( minHandle >= start + ncharacters )
2745 minHandle -= ncharacters;
2747 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2752 if( maxHandle >= start + ncharacters )
2754 maxHandle -= ncharacters;
2756 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2762 // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2765 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2767 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2768 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2769 // Mean we do not re-draw the text more than we have too.
2772 /* Delete character at current cursor position and redisplay*/
2773 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2775 // Ensure positionToDelete is not out of bounds.
2776 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2777 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2778 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2780 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2783 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2785 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2787 // Get the styled text of the character to be deleted as it may be needed if
2788 // the "exceed the text-input's boundaries" option is disabled.
2789 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2791 mStyledText.erase(it); // erase the character left of positionToDelete
2793 if( mStyledText.empty() )
2795 ShowPlaceholderText( mStyledPlaceHolderText );
2799 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2801 const Character characterToDelete = styledCharacterToDelete.mText[0];
2803 // It may happen than after removing a white space or a new line character,
2804 // two words merge, this new word could be big enough to not fit in its
2805 // current line, so moved to the next one, and make some part of the text to
2806 // exceed the text-input's boundary.
2807 if( !mExceedEnabled )
2809 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2811 // Get the new text layout after removing one character.
2812 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2814 // Get text-input's size.
2815 const Vector3& size = GetControlSize();
2817 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2818 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2820 MarkupProcessor::StyledTextArray array;
2821 array.push_back( styledCharacterToDelete );
2822 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2824 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2829 GetTextLayoutInfo();
2831 ShowGrabHandleAndSetVisibility( false );
2833 mCursorPosition = positionToDelete -1;
2835 const TextStyle oldInputStyle( mInputStyle );
2837 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2839 if( oldInputStyle != mInputStyle )
2841 // Updates the line height accordingly with the input style.
2844 EmitStyleChangedSignal();
2849 /*Insert new character into the string and (optionally) redisplay text-input*/
2850 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2852 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2854 // Ensure insertionPosition is not out of bounds.
2855 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2857 bool textExceedsMaximunNumberOfCharacters = false;
2858 bool textExceedsBoundary = false;
2859 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2861 ShowGrabHandleAndSetVisibility( false );
2863 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2867 mIgnoreCommitFlag = true;
2868 mPreEditFlag = false;
2869 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2870 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2873 if( textExceedsMaximunNumberOfCharacters )
2875 EmitMaxInputCharactersReachedSignal();
2878 if( textExceedsBoundary )
2880 EmitInputTextExceedsBoundariesSignal();
2881 PreEditReset( false );
2885 return insertedStringLength;
2888 ImageActor TextInput::CreateCursor( const Vector4& color)
2891 cursor = CreateSolidColorActor(color);
2892 cursor.SetName( "Cursor" );
2894 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2895 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2896 cursor.SetVisible(false);
2901 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2903 // As cursor is not moving due to grab handle, handle should be hidden.
2904 ShowGrabHandleAndSetVisibility( false );
2906 bool cursorPositionChanged = false;
2909 if ( mCursorPosition >= places )
2911 mCursorPosition = mCursorPosition - places;
2912 cursorPositionChanged = true;
2917 if ((mCursorPosition + places) <= mStyledText.size())
2919 mCursorPosition = mCursorPosition + places;
2920 cursorPositionChanged = true;
2924 if( cursorPositionChanged )
2926 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2928 const TextStyle oldInputStyle( mInputStyle );
2929 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2933 if( oldInputStyle != mInputStyle )
2935 // Updates the line height accordingly with the input style.
2938 EmitStyleChangedSignal();
2941 ImfManager imfManager = ImfManager::Get();
2944 imfManager.SetCursorPosition ( mCursorPosition );
2945 imfManager.NotifyCursorPosition();
2950 void TextInput::DrawCursor()
2952 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2954 // Get height of cursor and set its size
2955 Size size( CURSOR_THICKNESS, 0.0f );
2956 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2958 size.height = rowRect.height;
2962 // Measure Font so know how big text will be if no initial text to measure.
2963 size.height = mLineHeight;
2966 mCursor.SetSize(size);
2968 // If the character is italic then the cursor also tilts.
2969 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2971 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2973 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2975 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2976 bool altPositionValid; // Alternate cursor validity flag.
2977 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2978 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2980 SetAltCursorEnabled( altPositionValid );
2982 if( !altPositionValid )
2984 mCursor.SetPosition( position + UI_OFFSET );
2988 size.height *= 0.5f;
2989 mCursor.SetSize(size);
2990 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2992 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2993 size.height = rowRect.height * 0.5f;
2994 mCursorRTL.SetSize(size);
2995 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2998 if( IsScrollEnabled() )
3000 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3001 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3006 void TextInput::SetAltCursorEnabled( bool enabled )
3008 mCursorRTLEnabled = enabled;
3009 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3012 void TextInput::SetCursorVisibility( bool visible )
3014 mCursorVisibility = visible;
3015 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3016 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3019 void TextInput::CreateGrabHandle( Dali::Image image )
3025 mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3029 mGrabHandleImage = image;
3032 mGrabHandle = ImageActor::New(mGrabHandleImage);
3033 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3034 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3036 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3038 ShowGrabHandleAndSetVisibility( false );
3040 CreateGrabArea( mGrabHandle );
3042 mActiveLayer.Add(mGrabHandle);
3046 void TextInput::CreateGrabArea( Actor& parent )
3048 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3049 mGrabArea.SetName( "GrabArea" );
3050 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3051 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3052 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
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 = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3276 mSelectionHandleOneImagePressed = ResourceImage::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.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3289 mHandleOneGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3290 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3292 mTapDetector.Attach( mHandleOneGrabArea );
3293 mPanGestureDetector.Attach( mHandleOneGrabArea );
3295 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3297 mSelectionHandleOne.Add( mHandleOneGrabArea );
3298 mActiveLayer.Add( mSelectionHandleOne );
3301 if ( !mSelectionHandleTwo )
3303 // create normal and pressed images
3304 mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3305 mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3307 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3308 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3309 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3310 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3311 mIsSelectionHandleTwoFlipped = false;
3312 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3314 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3315 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3316 mHandleTwoGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3317 mHandleTwoGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3318 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3320 mTapDetector.Attach( mHandleTwoGrabArea );
3321 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3323 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3325 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3327 mActiveLayer.Add( mSelectionHandleTwo );
3330 SetUpHandlePropertyNotifications();
3332 // update table as text may have changed.
3333 GetTextLayoutInfo();
3335 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3336 bool altPositionValidOne; // Alternate cursor validity flag.
3337 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3338 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3340 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3341 bool altPositionValidTwo; // Alternate cursor validity flag.
3342 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3343 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3345 // VCC TODO: This method is a hack for one line.
3346 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3348 altPositionValidOne,
3349 altPositionValidTwo,
3353 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3354 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3356 // Calculates and set the visibility if the scroll mode is enabled.
3357 bool isSelectionHandleOneVisible = true;
3358 bool isSelectionHandleTwoVisible = true;
3359 if( IsScrollEnabled() )
3361 const Vector3& controlSize( GetControlSize() );
3362 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3363 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3364 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3365 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3368 CreateHighlight(); // function will only create highlight if not already created.
3371 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3373 Vector3 actualHandlePosition;
3375 if ( mSelectionHandleOne && mSelectionHandleTwo )
3377 const Vector3& controlSize = GetControlSize();
3379 Size cursorSize( CURSOR_THICKNESS, 0.f );
3381 // Get a reference of the wanted selection handle (handle one or two).
3382 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3384 // Get a reference for the current position of the handle and a copy of its pair
3385 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3386 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3388 // Get a handle of the selection handle actor
3389 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3391 // Selection handles should jump to the nearest character
3392 std::size_t newHandlePosition = 0;
3393 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3395 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3396 bool altPositionValid; // Alternate cursor validity flag.
3397 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3398 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3399 if( altPositionValid )
3401 // Check which of the positions is the closest.
3402 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3404 actualHandlePosition = altPosition;
3408 bool handleVisible = true;
3410 if( IsScrollEnabled() )
3412 mCurrentSelectionId = handleId;
3414 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3415 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3416 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3423 mCurrentSelectionHandlePosition = actualHandlePosition;
3424 mScrollDisplacement = Vector2::ZERO;
3428 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3430 mScrollDisplacement.x = -SCROLL_SPEED;
3432 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3434 mScrollDisplacement.x = SCROLL_SPEED;
3436 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3438 mScrollDisplacement.y = -SCROLL_SPEED;
3440 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3442 mScrollDisplacement.y = SCROLL_SPEED;
3448 if ( handleVisible && // Ensure the handle is visible.
3449 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3450 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3452 currentSelectionHandlePosition = newHandlePosition;
3454 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3455 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3459 if ( handleId == HandleOne )
3461 const TextStyle oldInputStyle( mInputStyle );
3463 // Set Active Style to that of first character in selection
3464 if( mSelectionHandleOnePosition < mStyledText.size() )
3466 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3469 if( oldInputStyle != mInputStyle )
3471 // Updates the line height accordingly with the input style.
3474 EmitStyleChangedSignal();
3480 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3483 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3485 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3486 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3488 if ( selectionHandleActor )
3490 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3491 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3492 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3494 if( IsScrollEnabled() )
3496 const Size cursorSize( CURSOR_THICKNESS,
3497 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3498 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3500 GetControlSize() ) );
3505 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3507 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3509 // VCC Set true/false in logical order. TODO : It needs to be checked.
3511 if( startSelection > endSelection )
3513 std::swap( startSelection, endSelection );
3515 std::size_t index = 0u;
3516 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3518 if( ( index < startSelection ) || ( endSelection <= index ) )
3529 // Calculate the dimensions of the quads they will make the highlight mesh
3530 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3532 // At the moment there is no public API to modify the block alignment option.
3534 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3536 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3538 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3539 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3541 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3542 std::vector<bool> selectedVisualText;
3543 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3544 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3545 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3547 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3548 float rowLeft = 0.0f;
3549 float rowRight = 0.0f;
3550 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3551 float maxRowLeft = std::numeric_limits<float>::max();
3552 float maxRowRight = 0.0f;
3554 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3556 // Scan through entire text.
3559 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3561 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3562 bool charSelected = false;
3563 if( selectedIt != selectedEndIt )
3565 charSelected = *selectedIt++;
3568 if( selectionState == SelectionNone )
3572 selectionState = SelectionStarted;
3573 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3574 rowRight = rowLeft + charInfo.mSize.width;
3577 else if( selectionState == SelectionStarted )
3579 // break selection on:
3580 // 1. new line causing selection break. (\n or wordwrap)
3581 // 2. character not selected.
3582 if( !charSelected ||
3583 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3585 // finished selection.
3586 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3587 // that it resides on. That way this enumeration is not necessary.
3589 if(lastIt->mIsNewParagraphChar)
3591 // 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.
3592 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3594 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3595 maxRowLeft = std::min(maxRowLeft, min.x);
3596 maxRowRight = std::max(maxRowRight, max.x);
3597 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3598 float rowTop = rowBottom - rowSize.height;
3600 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3603 rowRight = std::numeric_limits<float>::max();
3605 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3607 selectionState = SelectionNone;
3609 // Still selected? start a new selection
3612 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3614 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3615 selectionState = SelectionStarted;
3620 // build up highlight(s) with this selection data.
3621 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3622 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3629 // If reached end, and still on selection, then close selection.
3632 if(selectionState == SelectionStarted)
3634 // finished selection.
3636 if(lastIt->mIsNewParagraphChar)
3638 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3640 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3641 maxRowLeft = std::min(maxRowLeft, min.x);
3642 maxRowRight = std::max(maxRowRight, max.x);
3643 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3644 float rowTop = rowBottom - rowSize.height;
3645 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3649 // Get the top left and bottom right corners.
3650 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3651 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3652 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3654 // Clamp quads so they appear to clip to borders of the whole text.
3655 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3657 // For block-align align Further Clamp quads to max left and right extents
3658 // BlockAlign: Will adjust highlight to block:
3660 // H[ello] (top row right = max of all rows right)
3661 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3662 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3663 // [text] (bottom row left = min of all rows left)
3664 // (common in SMS messaging selection)
3666 // As opposed to the default which is tight text highlighting.
3671 // (common in regular text editors/web browser selection)
3672 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3674 // Finally clamp quads again so they don't exceed the boundry of the control.
3675 const Vector3& controlSize = GetControlSize();
3676 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3679 return mNewHighlightInfo;
3682 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3683 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3684 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3686 // At the moment there is no public API to modify the block alignment option.
3688 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3690 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3692 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3693 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3695 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3696 std::vector<bool> selectedVisualText;
3697 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3698 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3699 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3701 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3702 float rowLeft = 0.0f;
3703 float rowRight = 0.0f;
3705 // VCC TODO this is valid for one line.
3707 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3709 // Scan through entire text.
3712 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3714 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3715 bool charSelected = false;
3716 if( selectedIt != selectedEndIt )
3718 charSelected = *selectedIt++;
3723 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3724 rowRight = rowLeft + charInfo.mSize.width;
3726 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3727 float rowTop = rowBottom - rowSize.height;
3728 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3734 // Finally clamp quads again so they don't exceed the boundry of the control.
3735 const Vector3& controlSize = GetControlSize();
3736 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3739 return mNewHighlightInfo;
3742 void TextInput::UpdateHighlight()
3744 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3746 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3748 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3749 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3750 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3751 // [BOTTOM] [ MIDDLE ]
3754 // Each quad is created as 2 triangles.
3755 // Middle is just 1 quad regardless of its size.
3769 if ( mHighlightMeshActor )
3771 // vertex and triangle buffers should always be present if MeshActor is alive.
3772 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3773 MeshData::VertexContainer vertices;
3774 Dali::MeshData::FaceIndices faceIndices;
3776 if( !newHighlightInfo.mQuadList.empty() )
3778 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3779 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3781 // vertex position defaults to (0 0 0)
3782 MeshData::Vertex vertex;
3783 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3786 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3788 // Add each quad geometry (a sub-selection) to the mesh data.
3798 QuadCoordinates& quad = *iter;
3800 vertex.x = quad.min.x;
3801 vertex.y = quad.min.y;
3802 vertices.push_back( vertex );
3805 vertex.x = quad.max.x;
3806 vertex.y = quad.min.y;
3807 vertices.push_back( vertex );
3809 // bottom-left (v+2)
3810 vertex.x = quad.min.x;
3811 vertex.y = quad.max.y;
3812 vertices.push_back( vertex );
3814 // bottom-right (v+3)
3815 vertex.x = quad.max.x;
3816 vertex.y = quad.max.y;
3817 vertices.push_back( vertex );
3819 // triangle A (3, 1, 0)
3820 faceIndices.push_back( v + 3 );
3821 faceIndices.push_back( v + 1 );
3822 faceIndices.push_back( v );
3824 // triangle B (0, 2, 3)
3825 faceIndices.push_back( v );
3826 faceIndices.push_back( v + 2 );
3827 faceIndices.push_back( v + 3 );
3829 mMeshData.SetFaceIndices( faceIndices );
3832 BoneContainer bones(0); // passed empty as bones not required
3833 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3834 mHighlightMesh.UpdateMeshData(mMeshData);
3839 void TextInput::ClearPopup()
3841 mPopupPanel.Clear();
3844 void TextInput::AddPopupOptions()
3846 mPopupPanel.AddPopupOptions();
3849 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3851 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3853 Vector3 clampedPosition ( position );
3854 Vector3 tailOffsetPosition ( position );
3856 float xOffSet( 0.0f );
3858 Actor self = Self();
3859 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3861 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3862 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3864 // Clamp to left or right or of boundary
3865 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3867 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3869 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3871 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3874 clampedPosition.x = position.x + xOffSet;
3875 tailOffsetPosition.x = -xOffSet;
3877 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3878 bool flipTail( false );
3880 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3882 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3886 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3887 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3890 void TextInput::HidePopup(bool animate, bool signalFinished )
3892 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3894 mPopupPanel.Hide( animate );
3896 if( animate && signalFinished )
3898 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3903 void TextInput::ShowPopup( bool animate )
3906 Vector2 alternativePopupPosition;
3908 if(mHighlightMeshActor && mState == StateEdit)
3911 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3913 // When text is selected, show popup above top handle (and text), or below bottom handle.
3914 // topHandle: referring to the top most point of the handle or the top line of selection.
3915 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3917 topHandle = mSelectionHandleOneActualPosition;
3918 bottomHandle = mSelectionHandleTwoActualPosition;
3919 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3923 topHandle = mSelectionHandleTwoActualPosition;
3924 bottomHandle = mSelectionHandleOneActualPosition;
3925 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3927 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3928 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3930 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3932 position.x = xPosition;
3934 // Alternative position if no upper space
3935 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3936 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3940 // When no text is selected, show popup at world position of grab handle or cursor
3941 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3942 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3943 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3944 // if can't be positioned above, then position below row.
3945 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3948 // If grab handle enabled then position pop-up below the grab handle.
3949 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3953 SetPopupPosition( position, alternativePopupPosition );
3956 mPopupPanel.Show( Self(), animate );
3957 StartMonitoringStageForTouch();
3959 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3962 void TextInput::ShowPopupCutCopyPaste()
3966 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3967 // Check the selected text is whole text or not.
3968 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3970 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3973 if ( !mStyledText.empty() && IsTextSelected() )
3975 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3976 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3979 if( mClipboard && mClipboard.NumberOfItems() )
3981 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3982 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3987 mPopupPanel.Hide(false);
3991 void TextInput::SetUpPopupSelection( bool showCutButton )
3994 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3995 // If no text exists then don't offer to select
3996 if ( !mStyledText.empty() )
3998 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3999 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
4000 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
4002 // if clipboard has valid contents then offer paste option
4003 if( mClipboard && mClipboard.NumberOfItems() )
4005 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4006 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4011 mPopupPanel.Hide(false);
4014 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4019 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4020 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4021 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4022 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4024 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4026 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4028 float closestYdifference = std::numeric_limits<float>::max();
4029 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4030 std::size_t numberOfMatchedCharacters = 0;
4032 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4033 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4035 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4037 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4038 float baselinePosition = info.mPosition.y - info.mDescender;
4040 if( info.mIsVisible )
4042 // store difference between source y point and the y position of the current character
4043 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4045 if( currentYdifference < closestYdifference )
4047 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4048 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4049 closestYdifference = currentYdifference;
4050 matchedCharacters.clear();
4051 numberOfMatchedCharacters = 0; // reset count
4054 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4055 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4057 // ignore new line character.
4058 if( !info.mIsNewParagraphChar )
4060 matchedCharacters.push_back( info );
4061 numberOfMatchedCharacters++;
4065 } // End of loop checking each character's y position in the character layout table
4067 // Check if last character is a newline, if it is
4068 // then need pretend there is an imaginary line afterwards,
4069 // and check if user is touching below previous line.
4070 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4072 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4074 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4078 // 2 Iterate through matching list of y positions and find closest matching X position.
4080 bool matched( false );
4082 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4083 std::size_t visualIndex = 0u;
4084 const std::size_t matchedCharactersSize = matchedCharacters.size();
4085 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4087 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4089 if( info.mIsVisible )
4091 // stop when on left side of character's center.
4092 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4093 if( sourceScrollOffset.x < characterMidPointPosition )
4095 if(info.mIsRightToLeftCharacter)
4097 rightToLeftChar = true;
4099 glyphIntersection = info.mPosition.x;
4104 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4108 if( visualIndex == matchedCharactersSize )
4110 rightToLeftChar = lastRightToLeftChar;
4113 closestIndex = lineOffset + visualIndex;
4115 mClosestCursorPositionEOL = false; // reset
4116 if( ( visualIndex == matchedCharactersSize ) && !matched )
4118 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4121 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4122 if( rightToLeftChar && lastRightToLeftChar )
4124 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4129 // closestIndex is the visual index, need to convert it to the logical index
4130 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4132 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4134 // Checks for situations where user is touching between LTR and RTL
4135 // characters. To identify if the user means the end of a LTR string
4136 // or the beginning of an RTL string, and vice versa.
4137 if( closestIndex > 0 )
4139 if( rightToLeftChar && !lastRightToLeftChar )
4144 // A: In this touch range, the user is indicating that they wish to place
4145 // the cursor at the end of the LTR text.
4146 // B: In this touch range, the user is indicating that they wish to place
4147 // the cursor at the end of the RTL text.
4149 // Result of touching A area:
4150 // [.....LTR]|[RTL......]+
4152 // |: primary cursor (for typing LTR chars)
4153 // +: secondary cursor (for typing RTL chars)
4155 // Result of touching B area:
4156 // [.....LTR]+[RTL......]|
4158 // |: primary cursor (for typing RTL chars)
4159 // +: secondary cursor (for typing LTR chars)
4161 if( sourceScrollOffset.x < glyphIntersection )
4166 else if( !rightToLeftChar && lastRightToLeftChar )
4168 if( sourceScrollOffset.x < glyphIntersection )
4175 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4176 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4177 // one further ahead
4178 if( rightToLeftChar && !lastRightToLeftChar )
4183 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4185 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4187 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4196 float TextInput::GetLineJustificationPosition() const
4198 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4199 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4200 float alignmentOffset = 0.f;
4202 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4203 if( alignment & Toolkit::Alignment::HorizontalLeft )
4205 alignmentOffset = 0.f;
4207 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4209 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4211 else if( alignment & Toolkit::Alignment::HorizontalRight )
4213 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4216 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4217 float justificationOffset = 0.f;
4219 switch( justification )
4221 case Toolkit::TextView::Left:
4223 justificationOffset = 0.f;
4226 case Toolkit::TextView::Center:
4228 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4231 case Toolkit::TextView::Right:
4233 justificationOffset = mTextLayoutInfo.mTextSize.width;
4236 case Toolkit::TextView::Justified:
4238 justificationOffset = 0.f;
4243 DALI_ASSERT_ALWAYS( false );
4247 return alignmentOffset + justificationOffset;
4250 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4252 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4253 A newline character is not inserted in this case */
4255 Vector3 cursorPosition;
4257 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4261 if( characterPosition > 0u )
4263 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4265 // If previous character on a different line then use current characters position
4266 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4268 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4269 if ( mClosestCursorPositionEOL )
4271 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4275 cursorPosition = Vector3( currentCharInfo.mPosition );
4284 // If the character is left to right, the position is the character's position plus its width.
4285 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4287 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4288 cursorPosition.y = currentCharInfo.mPosition.y;
4291 return cursorPosition;
4294 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4296 bool direction = false;
4297 Vector3 alternatePosition;
4298 bool alternatePositionValid = false;
4300 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4303 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4305 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4306 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4307 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4309 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4311 alternatePositionValid = false;
4312 directionRTL = false;
4314 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4316 if( characterPosition == 0u )
4318 // When the cursor position is at the beginning, it should be at the start of the current character.
4319 // If the current character is LTR, then the start is on the right side of the glyph.
4320 // If the current character is RTL, then the start is on the left side of the glyph.
4322 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4324 characterPosition = FindVisibleCharacter( Right, 0u );
4327 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4328 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4330 cursorPosition.x = info.mPosition.x + rtlOffset;
4331 cursorPosition.y = info.mPosition.y;
4332 directionRTL = info.mIsRightToLeftCharacter;
4334 else if( characterPosition > 0u )
4336 // Get the direction of the paragraph.
4337 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4338 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4340 // When cursor is not at beginning, consider possibility of
4341 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4343 // Cursor position should be the end of the last character.
4344 // If the last character is LTR, then the end is on the right side of the glyph.
4345 // If the last character is RTL, then the end is on the left side of the glyph.
4347 --characterPosition;
4349 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4351 characterPosition = FindVisibleCharacter( Left, characterPosition );
4354 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4355 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4357 // VCC TODO : check for a new paragraph character.
4359 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4360 const Vector3& size = GetControlSize();
4362 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4364 --characterPosition;
4366 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4369 if( !info.mIsNewParagraphChar )
4371 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4375 // VCC TODO : check for a new paragraph character.
4377 // When cursor points to first character on new line, position cursor at the start of this glyph.
4378 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4380 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4381 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4383 cursorPosition.x = infoNext.mPosition.x + start;
4384 cursorPosition.y = infoNext.mPosition.y;
4388 // If cursor points to the end of text, then can only position
4389 // cursor where the new line starts based on the line-justification position.
4390 cursorPosition.x = GetLineJustificationPosition();
4392 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4394 // If this is after the last character, then we can assume that the new cursor
4395 // should be exactly one row below the current row.
4397 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4398 cursorPosition.y = info.mPosition.y + rowRect.height;
4402 // If this is not after last character, then we can use this row's height.
4403 // should be exactly one row below the current row.
4405 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4406 cursorPosition.y = info.mPosition.y + rowRect.height;
4411 directionRTL = info.mIsRightToLeftCharacter;
4413 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4415 // 1. When the cursor is neither at the beginning or the end,
4416 // we can show multiple cursors under situations when the cursor is
4417 // between RTL and LTR text...
4418 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4420 std::size_t characterAltPosition = characterPosition + 1u;
4422 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4424 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4426 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4427 // Text: [...LTR...]|[...RTL...]
4429 // Alternate cursor pos: ^
4430 // In which case we need to display an alternate cursor for the RTL text.
4432 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4433 alternatePosition.y = infoAlt.mPosition.y;
4434 alternatePositionValid = true;
4436 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4438 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4439 // Text: |[...RTL...] [...LTR....]
4441 // Alternate cursor pos: ^
4442 // In which case we need to display an alternate cursor for the RTL text.
4444 alternatePosition.x = infoAlt.mPosition.x;
4445 alternatePosition.y = infoAlt.mPosition.y;
4446 alternatePositionValid = true;
4451 // 2. When the cursor is at the end of the text,
4452 // and we have multi-directional text,
4453 // we can also consider showing mulitple cursors.
4454 // The rule here is:
4455 // If first and last characters on row are different
4456 // Directions, then two cursors need to be displayed.
4458 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4460 // The last character's direction is differernt than the first one of current paragraph.
4463 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4465 if(info.mIsRightToLeftCharacter)
4467 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4468 // Text: [...LTR...]|[...RTL...]
4470 // Alternate cursor pos: ^
4471 // In which case we need to display an alternate cursor for the RTL text, this cursor
4472 // should be at the end of the given line.
4474 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4475 alternatePosition.y = infoStart.mPosition.y;
4476 alternatePositionValid = true;
4478 else if(!info.mIsRightToLeftCharacter) // starting RTL
4480 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4481 // Text: |[...RTL...] [...LTR....]
4483 // Alternate cursor pos: ^
4484 // In which case we need to display an alternate cursor for the RTL text.
4486 alternatePosition.x = infoStart.mPosition.x;
4487 alternatePosition.y = infoStart.mPosition.y;
4488 alternatePositionValid = true;
4493 } // characterPosition > 0
4497 // If the character table is void, place the cursor accordingly the text alignment.
4498 const Vector3& size = GetControlSize();
4500 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4501 float alignmentOffset = 0.f;
4503 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4504 if( alignment & Toolkit::Alignment::HorizontalLeft )
4506 alignmentOffset = 0.f;
4508 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4510 alignmentOffset = 0.5f * ( size.width );
4512 else if( alignment & Toolkit::Alignment::HorizontalRight )
4514 alignmentOffset = size.width;
4517 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4518 cursorPosition.x = alignmentOffset;
4520 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4521 if( alignment & Toolkit::Alignment::VerticalTop )
4523 cursorPosition.y = mLineHeight;
4525 else if( alignment & Toolkit::Alignment::VerticalCenter )
4527 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4529 else if( alignment & Toolkit::Alignment::VerticalBottom )
4531 cursorPosition.y = size.height;
4535 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4536 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4538 if( alternatePositionValid )
4540 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4541 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4544 return cursorPosition;
4547 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4549 // scan string from current position to beginning of current line to note direction of line
4550 while( logicalPosition )
4553 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4560 return logicalPosition;
4563 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4565 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4567 while( logicalPosition )
4570 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4577 return logicalPosition;
4580 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4584 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4587 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4589 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4590 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4592 min = Vector2::ZERO;
4593 max = Vector2(0.0f, mLineHeight);
4597 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4599 // Initializes the min and max position.
4600 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4601 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4605 // 1) Find the line where the character is laid-out.
4606 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4607 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4610 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4612 // Index within the whole text to the last character of the current line.
4613 std::size_t lastCharacterOfLine = 0u;
4615 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4616 if( lineNextIt != lineEndIt )
4618 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4622 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4625 // Check if the given chracter position is within the line.
4626 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4628 // 2) Get the row rect of all laid-out characters on the line.
4630 // Need to scan all characters of the line because they are in the logical position.
4631 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4632 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4636 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4638 min.x = std::min( min.x, characterInfo.mPosition.x );
4639 min.y = std::min( min.y, characterInfo.mPosition.y );
4640 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4641 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4648 return Size( max.x - min.x, max.y - min.y );
4651 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4653 Actor popUpPanel = mPopupPanel.GetRootActor();
4655 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4661 Dali::Actor parent( touchedActor.GetParent() );
4665 return WasTouchedCheck( parent );
4672 void TextInput::StartMonitoringStageForTouch()
4674 Stage stage = Stage::GetCurrent();
4675 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4678 void TextInput::EndMonitoringStageForTouch()
4680 Stage stage = Stage::GetCurrent();
4681 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4684 void TextInput::OnStageTouched(const TouchEvent& event)
4686 if( event.GetPointCount() > 0 )
4688 if ( TouchPoint::Down == event.GetPoint(0).state )
4690 const Actor touchedActor(event.GetPoint(0).hitActor);
4692 bool popUpShown( false );
4694 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4699 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4701 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4703 EndMonitoringStageForTouch();
4704 HidePopup( true, false );
4707 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4709 EndMonitoringStageForTouch();
4710 ShowGrabHandleAndSetVisibility( false );
4716 void TextInput::SelectText(std::size_t start, std::size_t end)
4718 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4719 IsGrabHandleEnabled()?"true":"false",
4720 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4721 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4722 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4724 StartMonitoringStageForTouch();
4726 if ( mEditModeActive ) // Only allow text selection when in edit mode
4728 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4729 mSelectingText = true;
4731 std::size_t selectionStartPosition = std::min( start, end );
4733 // Hide grab handle when selecting.
4734 ShowGrabHandleAndSetVisibility( false );
4736 if( start != end ) // something to select
4738 SetCursorVisibility( false );
4739 StopCursorBlinkTimer();
4741 CreateSelectionHandles(start, end);
4744 const TextStyle oldInputStyle( mInputStyle );
4745 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4747 if( oldInputStyle != mInputStyle )
4749 // Updates the line height accordingly with the input style.
4752 EmitStyleChangedSignal();
4758 mSelectingText = false;
4762 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4764 MarkupProcessor::StyledTextArray currentSelectedText;
4766 if ( IsTextSelected() )
4768 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4769 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4771 for(; it != end; ++it)
4773 MarkupProcessor::StyledText& styledText( *it );
4774 currentSelectedText.push_back( styledText );
4777 return currentSelectedText;
4780 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4782 const std::size_t beginIndex = std::min( begin, end );
4783 const std::size_t endIndex = std::max( begin, end );
4786 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4788 // Create a styled text array used to replace the text into the text-view.
4789 MarkupProcessor::StyledTextArray text;
4790 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4792 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4793 GetTextLayoutInfo();
4795 if( IsScrollEnabled() )
4797 // Need to set the scroll position as the text's size may have changed.
4798 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4801 ShowGrabHandleAndSetVisibility( false );
4807 // Set Handle positioning as the new style may have repositioned the characters.
4808 SetSelectionHandlePosition(HandleOne);
4809 SetSelectionHandlePosition(HandleTwo);
4812 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4814 // Just hide the grab handle when keyboard is hidden.
4815 if (!keyboardShown )
4817 ShowGrabHandleAndSetVisibility( false );
4819 // If the keyboard is not now being shown, then hide the popup panel
4820 mPopupPanel.Hide( true );
4824 // Removes highlight and resumes edit mode state
4825 void TextInput::RemoveHighlight( bool hidePopup )
4827 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4829 if ( mHighlightMeshActor )
4831 if ( mSelectionHandleOne )
4833 mActiveLayer.Remove( mSelectionHandleOne );
4834 mSelectionHandleOne.Reset();
4835 mSelectionHandleOneOffset.x = 0.0f;
4837 if ( mSelectionHandleTwo )
4839 mActiveLayer.Remove( mSelectionHandleTwo );
4840 mSelectionHandleTwo.Reset();
4841 mSelectionHandleTwoOffset.x = 0.0f;
4844 mNewHighlightInfo.mQuadList.clear();
4846 Self().Remove( mHighlightMeshActor );
4848 SetCursorVisibility( true );
4849 StartCursorBlinkTimer();
4851 mHighlightMeshActor.Reset();
4852 // NOTE: We cannot dereference mHighlightMesh, due
4853 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4861 mSelectionHandleOnePosition = 0;
4862 mSelectionHandleTwoPosition = 0;
4865 void TextInput::CreateHighlight()
4867 if ( !mHighlightMeshActor )
4869 mMeshData = MeshData( );
4870 mMeshData.SetHasNormals( true );
4872 mCustomMaterial = Material::New("CustomMaterial");
4873 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4875 mMeshData.SetMaterial( mCustomMaterial );
4877 mHighlightMesh = Mesh::New( mMeshData );
4879 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4880 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4881 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4882 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4883 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4884 mHighlightMeshActor.SetAffectedByLighting(false);
4886 Self().Add(mHighlightMeshActor);
4891 bool TextInput::CopySelectedTextToClipboard()
4893 mCurrentCopySelecton.clear();
4895 mCurrentCopySelecton = GetSelectedText();
4897 std::string stringToStore;
4899 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4900 * a marked up string.
4902 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4903 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4905 bool success = mClipboard.SetItem( stringToStore );
4909 void TextInput::PasteText( const Text& text )
4911 // Update Flag, indicates whether to update the text-input contents or not.
4912 // Any key stroke that results in a visual change of the text-input should
4913 // set this flag to true.
4914 bool update = false;
4915 if( mHighlightMeshActor )
4917 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4918 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4920 ImfManager imfManager = ImfManager::Get();
4923 imfManager.SetCursorPosition( mCursorPosition );
4924 imfManager.NotifyCursorPosition();
4926 DeleteHighlightedText( true );
4930 bool textExceedsMaximunNumberOfCharacters = false;
4931 bool textExceedsBoundary = false;
4933 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4935 mCursorPosition += insertedStringLength;
4936 ImfManager imfManager = ImfManager::Get();
4939 imfManager.SetCursorPosition ( mCursorPosition );
4940 imfManager.NotifyCursorPosition();
4943 update = update || ( insertedStringLength > 0 );
4950 if( insertedStringLength < text.GetLength() )
4952 EmitMaxInputCharactersReachedSignal();
4955 if( textExceedsBoundary )
4957 EmitInputTextExceedsBoundariesSignal();
4961 void TextInput::SetTextDirection()
4963 // Put the cursor to the right if we are empty and an RTL language is being used.
4964 if ( mStyledText.empty() )
4966 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4968 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4969 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4971 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4972 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4974 int alignment( mDisplayedTextView.GetTextAlignment() &
4975 ( Toolkit::Alignment::VerticalTop |
4976 Toolkit::Alignment::VerticalCenter |
4977 Toolkit::Alignment::VerticalBottom |
4978 Toolkit::Alignment::HorizontalCenter ) );
4979 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4981 // If our alignment is in the center, then do not change.
4982 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4984 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4987 // If our justification is in the center, then do not change.
4988 if ( justification != Toolkit::TextView::Center )
4990 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4993 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4994 mDisplayedTextView.SetLineJustification( justification );
4998 void TextInput::UpdateLineHeight()
5000 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
5001 mLineHeight = font.GetLineHeight();
5003 // 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.
5005 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5007 if( !mExceedEnabled || shrink )
5009 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5013 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5015 // VCC check if we need do this in the visual order ...
5016 std::size_t position = 0u;
5018 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5024 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5026 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5028 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5034 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5035 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5037 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5043 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5048 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5055 void TextInput::SetSortModifier( float depthOffset )
5057 if(mDisplayedTextView)
5059 mDisplayedTextView.SetSortModifier(depthOffset);
5063 void TextInput::SetSnapshotModeEnabled( bool enable )
5065 if(mDisplayedTextView)
5067 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5071 bool TextInput::IsSnapshotModeEnabled() const
5073 bool snapshotEnabled = false;
5075 if(mDisplayedTextView)
5077 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5080 return snapshotEnabled;
5083 void TextInput::SetMarkupProcessingEnabled( bool enable )
5085 mMarkUpEnabled = enable;
5088 bool TextInput::IsMarkupProcessingEnabled() const
5090 return mMarkUpEnabled;
5093 void TextInput::SetScrollEnabled( bool enable )
5095 if( mDisplayedTextView )
5097 mDisplayedTextView.SetScrollEnabled( enable );
5102 // Don't set cursor's and handle's visibility to false if they are outside the
5103 // boundaries of the text-input.
5104 mIsCursorInScrollArea = true;
5105 mIsGrabHandleInScrollArea = true;
5106 if( mSelectionHandleOne && mSelectionHandleTwo )
5108 mSelectionHandleOne.SetVisible( true );
5109 mSelectionHandleTwo.SetVisible( true );
5111 if( mHighlightMeshActor )
5113 mHighlightMeshActor.SetVisible( true );
5119 bool TextInput::IsScrollEnabled() const
5121 bool scrollEnabled = false;
5123 if( mDisplayedTextView )
5125 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5128 return scrollEnabled;
5131 void TextInput::SetScrollPosition( const Vector2& position )
5133 if( mDisplayedTextView )
5135 mDisplayedTextView.SetScrollPosition( position );
5139 Vector2 TextInput::GetScrollPosition() const
5141 Vector2 scrollPosition;
5143 if( mDisplayedTextView )
5145 scrollPosition = mDisplayedTextView.GetScrollPosition();
5148 return scrollPosition;
5151 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5153 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5154 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5155 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5157 // Add style to the new input text.
5158 MarkupProcessor::StyledTextArray textToInsert;
5159 for( std::size_t i = 0; i < insertedStringLength; ++i )
5161 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5162 textToInsert.push_back( newStyledCharacter );
5165 //Insert text to the TextView.
5166 const bool emptyTextView = mStyledText.empty();
5167 if( emptyTextView && mPlaceHolderSet )
5169 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5170 mDisplayedTextView.SetText( textToInsert );
5174 if( 0 == numberOfCharactersToReplace )
5176 mDisplayedTextView.InsertTextAt( position, textToInsert );
5180 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5183 mPlaceHolderSet = false;
5185 if( textToInsert.empty() )
5187 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5188 GetTextLayoutInfo();
5192 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5193 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5196 textExceedsBoundary = false;
5198 if( !mExceedEnabled )
5200 const Vector3& size = GetControlSize();
5202 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5204 // If new text does not fit within TextView
5205 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5206 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5207 GetTextLayoutInfo();
5208 textExceedsBoundary = true;
5209 insertedStringLength = 0;
5212 if( textExceedsBoundary )
5214 // Add the part of the text which fits on the text-input.
5216 // Split the text which doesn't fit in two halves.
5217 MarkupProcessor::StyledTextArray firstHalf;
5218 MarkupProcessor::StyledTextArray secondHalf;
5219 SplitText( textToInsert, firstHalf, secondHalf );
5221 // Clear text. This text will be filled with the text inserted.
5222 textToInsert.clear();
5224 // Where to insert the text.
5225 std::size_t positionToInsert = position;
5227 bool end = text.GetLength() <= 1;
5230 // Insert text and check ...
5231 const std::size_t textLength = firstHalf.size();
5232 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5233 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5235 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5237 // Inserted text doesn't fit.
5239 // Remove inserted text
5240 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5241 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5243 // The iteration finishes when only one character doesn't fit.
5244 end = textLength <= 1;
5248 // Prepare next two halves for next iteration.
5249 MarkupProcessor::StyledTextArray copyText = firstHalf;
5250 SplitText( copyText, firstHalf, secondHalf );
5257 // store text to be inserted in mStyledText.
5258 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5260 // Increase the inserted characters counter.
5261 insertedStringLength += textLength;
5263 // Prepare next two halves for next iteration.
5264 MarkupProcessor::StyledTextArray copyText = secondHalf;
5265 SplitText( copyText, firstHalf, secondHalf );
5267 // Update where next text has to be inserted
5268 positionToInsert += textLength;
5274 if( textToInsert.empty() && emptyTextView )
5276 // No character has been added and the text-view was empty.
5277 // Show the placeholder text.
5278 ShowPlaceholderText( mStyledPlaceHolderText );
5282 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5283 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5284 mPlaceHolderSet = false;
5287 return insertedStringLength;
5290 void TextInput::GetTextLayoutInfo()
5292 if( mStyledText.empty() )
5294 // The text-input has no text, clear the text-view's layout info.
5295 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5299 if( mDisplayedTextView )
5301 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5305 // There is no text-view.
5306 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5311 void TextInput::SetOffsetFromText( const Vector4& offset )
5313 mPopupOffsetFromText = offset;
5316 const Vector4& TextInput::GetOffsetFromText() const
5318 return mPopupOffsetFromText;
5321 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5323 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5327 TextInput& textInputImpl( GetImpl( textInput ) );
5329 switch ( propertyIndex )
5331 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5333 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5336 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5338 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5341 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5343 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5346 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5348 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5351 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5353 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5356 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5358 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5361 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5363 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5366 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5368 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5371 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5373 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5376 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5378 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5381 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5383 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5386 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5388 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5391 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5393 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5396 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5398 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5401 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5403 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5406 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5408 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5414 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5416 Property::Value value;
5418 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5422 TextInput& textInputImpl( GetImpl( textInput ) );
5424 switch ( propertyIndex )
5426 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5428 value = textInputImpl.GetMaterialDiffuseColor();
5431 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5433 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5436 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5438 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5441 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5443 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5446 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5448 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5451 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5453 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5456 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5458 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5461 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5463 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5466 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5468 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5471 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5473 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5476 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5478 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5481 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5483 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5486 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5488 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5491 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5493 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5496 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5498 value = textInputImpl.GetOffsetFromText();
5501 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5503 value = textInputImpl.mCursor.GetCurrentColor();
5510 void TextInput::EmitStyleChangedSignal()
5512 // emit signal if input style changes.
5513 Toolkit::TextInput handle( GetOwner() );
5514 mStyleChangedSignal.Emit( handle, mInputStyle );
5517 void TextInput::EmitTextModified()
5519 // emit signal when text changes.
5520 Toolkit::TextInput handle( GetOwner() );
5521 mTextModifiedSignal.Emit( handle );
5525 void TextInput::EmitMaxInputCharactersReachedSignal()
5527 // emit signal if max characters is reached during text input.
5528 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5530 Toolkit::TextInput handle( GetOwner() );
5531 mMaxInputCharactersReachedSignal.Emit( handle );
5534 void TextInput::EmitInputTextExceedsBoundariesSignal()
5536 // Emit a signal when the input text exceeds the boundaries of the text input.
5538 Toolkit::TextInput handle( GetOwner() );
5539 mInputTextExceedBoundariesSignal.Emit( handle );
5542 } // namespace Internal
5544 } // namespace Toolkit