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 const char* const SIGNAL_START_INPUT = "start-input";
204 const char* const SIGNAL_END_INPUT = "end-input";
205 const char* const SIGNAL_STYLE_CHANGED = "style-changed";
206 const char* const SIGNAL_MAX_INPUT_CHARACTERS_REACHED = "max-input-characters-reached";
207 const char* const SIGNAL_TOOLBAR_DISPLAYED = "toolbar-displayed";
208 const char* const SIGNAL_TEXT_EXCEED_BOUNDARIES = "text-exceed-boundaries";
212 return Toolkit::TextInput::New();
215 TypeRegistration typeRegistration( typeid( Toolkit::TextInput ), typeid( Toolkit::Control ), Create );
217 SignalConnectorType signalConnector1( typeRegistration, SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
218 SignalConnectorType signalConnector2( typeRegistration, SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
219 SignalConnectorType signalConnector3( typeRegistration, SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
220 SignalConnectorType signalConnector4( typeRegistration, SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
221 SignalConnectorType signalConnector5( typeRegistration, SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
222 SignalConnectorType signalConnector6( typeRegistration, SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
226 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
227 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
228 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
229 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
230 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
231 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
232 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
233 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
234 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
235 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
236 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
237 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
238 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
239 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
246 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
248 QuadCoordinates quad(x1, y1, x2, y2);
249 mQuadList.push_back( quad );
252 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
254 for(std::size_t i = 0;i < mQuadList.size(); i++)
256 QuadCoordinates& quad = mQuadList[i];
258 quad.min.Clamp(min, max);
259 quad.max.Clamp(min, max);
263 // [TextInput] ////////////////////////////////////////////////////////////////
265 Dali::Toolkit::TextInput TextInput::New()
267 // Create the implementation
268 TextInputPtr textInput(new TextInput());
269 // Pass ownership to CustomActor via derived handle
270 Dali::Toolkit::TextInput handle(*textInput);
271 handle.SetName( "TextInput");
273 textInput->Initialize();
277 TextInput::TextInput()
278 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
283 mDisplayedTextView(),
284 mStyledPlaceHolderText(),
285 mMaxStringLength( DEFAULT_MAX_SIZE ),
286 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
287 mCursorPosition( 0 ),
288 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
289 mIsSelectionHandleOneFlipped( false ),
290 mIsSelectionHandleTwoFlipped( false ),
291 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
292 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
293 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
294 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
295 mSelectionHandleOnePosition( 0 ),
296 mSelectionHandleTwoPosition( 0 ),
298 mPreEditStartPosition( 0 ),
299 mPreEditLength ( 0 ),
300 mNumberOfSurroundingCharactersDeleted( 0 ),
301 mTouchStartTime( 0 ),
303 mCurrentCopySelecton(),
306 mScrollDisplacement(),
307 mCurrentHandlePosition(),
308 mCurrentSelectionId(),
309 mCurrentSelectionHandlePosition(),
310 mRequestedSelection( 0, 0 ),
311 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
312 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
314 mMaterialColor( LIGHTBLUE ),
315 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
316 mOverrideAutomaticAlignment( false ),
317 mCursorRTLEnabled( false ),
318 mClosestCursorPositionEOL ( false ),
319 mCursorBlinkStatus( true ),
320 mCursorVisibility( false ),
321 mGrabHandleVisibility( false ),
322 mIsCursorInScrollArea( true ),
323 mIsGrabHandleInScrollArea( true ),
324 mEditModeActive( false ),
325 mEditOnTouch( true ),
326 mTextSelection( true ),
327 mExceedEnabled( true ),
328 mGrabHandleEnabled( true ),
329 mIsSelectionHandleFlipEnabled( true ),
330 mPreEditFlag( false ),
331 mIgnoreCommitFlag( false ),
332 mIgnoreFirstCommitFlag( false ),
333 mSelectingText( false ),
334 mPreserveCursorPosition( false ),
335 mSelectTextOnCommit( false ),
336 mUnderlinedPriorToPreEdit ( false ),
337 mCommitByKeyInput( false ),
338 mPlaceHolderSet( false ),
339 mMarkUpEnabled( false )
341 // Updates the line height accordingly with the input style.
345 TextInput::~TextInput()
347 StopCursorBlinkTimer();
352 std::string TextInput::GetText() const
356 // Return text-view's text only if the text-input's text is not empty
357 // in order to not to return the placeholder text.
358 if( !mStyledText.empty() )
360 text = mDisplayedTextView.GetText();
366 std::string TextInput::GetMarkupText() const
368 std::string markupString;
369 MarkupProcessor::GetMarkupString( mStyledText, markupString );
374 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
376 mDisplayedTextView.SetText( stylePlaceHolderText );
377 mPlaceHolderSet = true;
378 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
381 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
383 // Get the placeholder styled text array from the markup string.
384 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
385 if( mStyledText.empty() )
387 ShowPlaceholderText( mStyledPlaceHolderText );
391 std::string TextInput::GetPlaceholderText()
393 // Traverses the styled placeholder array getting only the text.
394 // Note that for some languages a 'character' could be represented by more than one 'char'
396 std::string placeholderText;
397 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
399 placeholderText.append( (*it).mText.GetText() );
402 return placeholderText ;
405 void TextInput::SetInitialText(const std::string& initialText)
407 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
409 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
411 mPreEditFlag = false;
412 mIgnoreCommitFlag = true;
415 SetText( initialText );
416 PreEditReset( false ); // Reset keyboard as text changed
419 void TextInput::SetText(const std::string& initialText)
421 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
423 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
425 if( mStyledText.empty() )
427 ShowPlaceholderText( mStyledPlaceHolderText );
431 mDisplayedTextView.SetText( mStyledText );
432 mPlaceHolderSet = false;
437 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
439 ImfManager imfManager = ImfManager::Get();
442 imfManager.SetCursorPosition( mCursorPosition );
443 imfManager.SetSurroundingText( initialText );
444 imfManager.NotifyCursorPosition();
447 if( IsScrollEnabled() )
449 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
452 ShowGrabHandleAndSetVisibility( false );
461 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
463 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
465 mDisplayedTextView.SetText( styleText );
466 mPlaceHolderSet = false;
468 // If text alignment hasn't been manually set by application developer, then we
469 // automatically determine the alignment based on the content of the text i.e. what
470 // language the text begins with.
471 // TODO: This should determine different alignments for each line (broken by '\n') of text.
472 if(!mOverrideAutomaticAlignment)
474 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
475 bool leftToRight(true);
477 if( !styleText.empty() )
479 bool breakOut(false);
481 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
483 const Text& text = textIter->mText;
485 for( std::size_t i = 0; i < text.GetLength(); ++i )
487 Character character( text[i] );
488 if( character.GetCharacterDirection() != Character::Neutral )
490 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
498 // Based on this direction, either left or right align text if not manually set by application developer.
499 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
500 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
501 Toolkit::Alignment::VerticalTop ) );
502 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
508 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
510 mMaxStringLength = maxChars;
513 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
515 DALI_ASSERT_DEBUG( maxLines > 0 )
519 mNumberOflinesLimit = maxLines;
523 std::size_t TextInput::GetNumberOfLinesLimit() const
525 return mNumberOflinesLimit;
528 std::size_t TextInput::GetNumberOfCharacters() const
530 return mStyledText.size();
534 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
536 mMaterialColor = color;
537 if ( mCustomMaterial )
539 mCustomMaterial.SetDiffuseColor( mMaterialColor );
540 mMeshData.SetMaterial( mCustomMaterial );
544 const Vector4& TextInput::GetMaterialDiffuseColor() const
546 return mMaterialColor;
551 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
553 return mInputStartedSignal;
556 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
558 return mInputFinishedSignal;
561 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
563 return mCutAndPasteToolBarDisplayed;
566 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
568 return mStyleChangedSignal;
571 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
573 return mTextModifiedSignal;
576 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
578 return mMaxInputCharactersReachedSignal;
581 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
583 return mInputTextExceedBoundariesSignal;
586 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
588 Dali::BaseHandle handle( object );
590 bool connected( true );
591 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( handle );
593 if( 0 == strcmp( signalName.c_str(), SIGNAL_START_INPUT ) )
595 textInput.InputStartedSignal().Connect( tracker, functor );
597 else if( 0 == strcmp( signalName.c_str(), SIGNAL_END_INPUT ) )
599 textInput.InputFinishedSignal().Connect( tracker, functor );
601 else if( 0 == strcmp( signalName.c_str(), SIGNAL_STYLE_CHANGED ) )
603 textInput.StyleChangedSignal().Connect( tracker, functor );
605 else if( 0 == strcmp( signalName.c_str(), SIGNAL_MAX_INPUT_CHARACTERS_REACHED ) )
607 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
609 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TOOLBAR_DISPLAYED ) )
611 textInput.CutAndPasteToolBarDisplayedSignal().Connect( tracker, functor );
613 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TEXT_EXCEED_BOUNDARIES ) )
615 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
619 // signalName does not match any signal
626 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
630 // update line height before calculate the actual position.
635 if( setCursorOnTouchPoint )
637 // Sets the cursor position for the given touch point.
638 ReturnClosestIndex( touchPoint, mCursorPosition );
640 // Creates the grab handle.
641 if( IsGrabHandleEnabled() )
643 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
647 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
648 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
649 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
650 ShowGrabHandleAndSetVisibility( true );
652 // Scrolls the text-view if needed.
653 if( IsScrollEnabled() )
655 ScrollTextViewToMakeCursorVisible( cursorPosition );
661 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
673 bool TextInput::IsEditable() const
675 return mEditModeActive;
678 void TextInput::SetEditOnTouch( bool editOnTouch )
680 mEditOnTouch = editOnTouch;
683 bool TextInput::IsEditOnTouch() const
688 void TextInput::SetTextSelectable( bool textSelectable )
690 mTextSelection = textSelectable;
693 bool TextInput::IsTextSelectable() const
695 return mTextSelection;
698 bool TextInput::IsTextSelected() const
700 return mHighlightMeshActor;
703 void TextInput::DeSelectText()
710 void TextInput::SetGrabHandleImage(Dali::Image image )
714 CreateGrabHandle(image);
718 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
720 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
724 mCursor.SetImage( image );
725 mCursor.SetNinePatchBorder( border );
729 Vector3 TextInput::GetSelectionHandleSize()
731 return DEFAULT_SELECTION_HANDLE_SIZE;
734 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
736 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
740 mCursorRTL.SetImage( image);
741 mCursorRTL.SetNinePatchBorder( border );
745 void TextInput::EnableGrabHandle(bool toggle)
747 // enables grab handle with will in turn de-activate magnifier
748 mGrabHandleEnabled = toggle;
751 bool TextInput::IsGrabHandleEnabled()
753 // if false then magnifier will be shown instead.
754 return mGrabHandleEnabled;
757 void TextInput::EnableSelectionHandleFlip( bool toggle )
759 // Deprecated function. To be removed.
760 mIsSelectionHandleFlipEnabled = toggle;
763 bool TextInput::IsSelectionHandleFlipEnabled()
765 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
769 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
771 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
772 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
773 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
775 mSelectionHandleFlipMargin = margin;
778 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
780 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
781 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
783 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
784 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
786 const Vector4 boundary( originX,
788 originX + boundingRectangle.width,
789 originY + boundingRectangle.height );
791 mBoundingRectangleWorldCoordinates = boundary;
794 const Rect<float> TextInput::GetBoundingRectangle() const
796 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
798 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
799 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
801 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
806 const Vector4& TextInput::GetSelectionHandleFlipMargin()
808 return mSelectionHandleFlipMargin;
811 void TextInput::SetTextColor( const Vector4& color )
813 mDisplayedTextView.SetColor( color );
816 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
818 if( style != mInputStyle )
821 bool emitSignal = false;
823 // mask: modify style according to mask, if different emit signal.
824 const TextStyle oldInputStyle( mInputStyle );
826 // Copy the new style.
827 mInputStyle.Copy( style, mask );
829 // if style has changed, emit signal.
830 if( oldInputStyle != mInputStyle )
835 // Updates the line height accordingly with the input style.
838 // Changing font point size will require the cursor to be re-sized
843 EmitStyleChangedSignal();
848 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
850 if ( IsTextSelected() )
852 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
853 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
855 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
857 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
860 // Keeps the old style to be compared with the new one.
861 const TextStyle oldInputStyle( mInputStyle );
863 // Copy only those parameters from the style which are set in the mask.
864 mInputStyle.Copy( style, mask );
866 if( mInputStyle != oldInputStyle )
868 // Updates the line height accordingly with the input style.
871 EmitStyleChangedSignal();
876 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
878 if( !mStyledText.empty() )
880 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
884 TextStyle TextInput::GetStyleAtCursor() const
888 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
890 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
891 style = mStyledText.at( mCursorPosition-1 ).mStyle;
897 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
899 Dali::Font defaultFont = Dali::Font::New();
900 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
907 TextStyle TextInput::GetStyleAt( std::size_t position ) const
909 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
911 if( position >= mStyledText.size() )
913 position = mStyledText.size() - 1;
916 return mStyledText.at( position ).mStyle;
919 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
921 mDisplayedTextView.SetTextAlignment( align );
922 mOverrideAutomaticAlignment = true;
925 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
927 mDisplayedTextView.SetLineJustification( justification );
928 mOverrideAutomaticAlignment = true;
931 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
933 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
936 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
938 return mDisplayedTextView.GetFadeBoundary();
941 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
943 return mDisplayedTextView.GetTextAlignment();
946 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
948 mDisplayedTextView.SetMultilinePolicy( policy );
951 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
953 return mDisplayedTextView.GetMultilinePolicy();
956 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
958 mDisplayedTextView.SetWidthExceedPolicy( policy );
961 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
963 return mDisplayedTextView.GetWidthExceedPolicy();
966 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
968 mDisplayedTextView.SetHeightExceedPolicy( policy );
971 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
973 return mDisplayedTextView.GetHeightExceedPolicy();
976 void TextInput::SetExceedEnabled( bool enable )
978 mExceedEnabled = enable;
981 bool TextInput::GetExceedEnabled() const
983 return mExceedEnabled;
986 void TextInput::SetBackground(Dali::Image image )
988 // TODO Should add this function and add public api to match.
991 bool TextInput::OnTouchEvent(const TouchEvent& event)
996 bool TextInput::OnKeyEvent(const KeyEvent& event)
998 switch( event.state )
1000 case KeyEvent::Down:
1002 return OnKeyDownEvent(event);
1008 return OnKeyUpEvent(event);
1020 void TextInput::OnKeyInputFocusGained()
1022 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1024 mEditModeActive = true;
1026 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1028 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1030 // Updates the line height accordingly with the input style.
1033 // Connect the signals to use in text input.
1034 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1035 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1037 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1040 GetTextLayoutInfo();
1043 SetCursorVisibility( true );
1044 StartCursorBlinkTimer();
1046 Toolkit::TextInput handle( GetOwner() );
1047 mInputStartedSignal.Emit( handle );
1049 ImfManager imfManager = ImfManager::Get();
1053 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1055 // Notify that the text editing start.
1056 imfManager.Activate();
1058 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1059 imfManager.SetRestoreAfterFocusLost( true );
1061 imfManager.SetCursorPosition( mCursorPosition );
1062 imfManager.NotifyCursorPosition();
1065 mClipboard = Clipboard::Get(); // Store handle to clipboard
1067 // Now in edit mode we can accept string to paste from clipboard
1068 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1071 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1075 void TextInput::OnKeyInputFocusLost()
1077 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1081 // If key input focus is lost, it removes the
1082 // underline from the last pre-edit text.
1083 RemovePreEditStyle();
1084 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1085 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1089 ImfManager imfManager = ImfManager::Get();
1092 // The text editing is finished. Therefore the imf manager don't have restore activation.
1093 imfManager.SetRestoreAfterFocusLost( false );
1095 // Notify that the text editing finish.
1096 imfManager.Deactivate();
1098 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1100 // Disconnect signal used the text input.
1101 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1103 Toolkit::TextInput handle( GetOwner() );
1104 mInputFinishedSignal.Emit( handle );
1105 mEditModeActive = false;
1106 mPreEditFlag = false;
1108 SetCursorVisibility( false );
1109 StopCursorBlinkTimer();
1111 ShowGrabHandleAndSetVisibility( false );
1114 // No longer in edit mode so do not want to receive string from clipboard
1115 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1118 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1121 Clipboard clipboard = Clipboard::Get();
1124 clipboard.HideClipboard();
1128 void TextInput::OnControlStageConnection()
1130 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1132 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1134 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1138 void TextInput::CreateActiveLayer()
1140 Actor self = Self();
1141 mActiveLayer = Layer::New();
1142 mActiveLayer.SetName ( "ActiveLayerActor" );
1144 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1145 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1146 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1148 self.Add( mActiveLayer );
1149 mActiveLayer.RaiseToTop();
1152 void TextInput::OnInitialize()
1154 CreateTextViewActor();
1158 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1159 // different positions depending on language)
1160 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1161 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1163 Actor self = Self();
1164 self.Add( mCursor );
1165 self.Add( mCursorRTL );
1167 mCursorVisibility = false;
1169 CreateActiveLayer(); // todo move this so layer only created when needed.
1171 // Assign names to image actors
1172 mCursor.SetName("mainCursor");
1173 mCursorRTL.SetName("rtlCursor");
1176 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1178 mDisplayedTextView.SetSize( targetSize );
1179 GetTextLayoutInfo();
1180 mActiveLayer.SetSize(targetSize);
1183 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1185 Relayout( mDisplayedTextView, size, container );
1186 Relayout( mPopupPanel.GetRootActor(), size, container );
1188 GetTextLayoutInfo();
1193 Vector3 TextInput::GetNaturalSize()
1195 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1197 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1199 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1200 naturalSize.height = mLineHeight;
1206 float TextInput::GetHeightForWidth( float width )
1208 float height = mDisplayedTextView.GetHeightForWidth( width );
1210 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1212 // If the height is zero, it means there is no text. Let's return the cursor height.
1213 height = mLineHeight;
1219 /*end of Virtual methods from parent*/
1221 // Private Internal methods
1223 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1225 switch (gesture.state)
1227 case Gesture::Started:
1228 // fall through so code not duplicated
1229 case Gesture::Continuing:
1231 if (actor == mGrabArea)
1233 SetCursorVisibility( true );
1234 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1235 MoveGrabHandle( gesture.displacement );
1236 HidePopup(); // Do not show popup whilst handle is moving
1238 else if (actor == mHandleOneGrabArea)
1240 // the displacement in PanGesture is affected by the actor's rotation.
1241 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1242 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1244 MoveSelectionHandle( HandleOne, gesture.displacement );
1246 mState = StateDraggingHandle;
1249 else if (actor == mHandleTwoGrabArea)
1251 // the displacement in PanGesture is affected by the actor's rotation.
1252 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1253 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1255 MoveSelectionHandle( HandleTwo, gesture.displacement );
1257 mState = StateDraggingHandle;
1263 case Gesture::Finished:
1265 // Revert back to non-pressed selection handle images
1266 if (actor == mGrabArea)
1268 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1269 SetCursorVisibility( true );
1270 SetUpPopupSelection();
1273 if (actor == mHandleOneGrabArea)
1275 // the displacement in PanGesture is affected by the actor's rotation.
1276 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1277 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1279 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1281 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1283 ShowPopupCutCopyPaste();
1285 if (actor == mHandleTwoGrabArea)
1287 // the displacement in PanGesture is affected by the actor's rotation.
1288 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1289 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1291 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1293 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1295 ShowPopupCutCopyPaste();
1304 // Stop the flashing animation so easy to see when moved.
1305 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1307 if (touch.GetPoint(0).state == TouchPoint::Down)
1309 SetCursorVisibility( true );
1310 StopCursorBlinkTimer();
1312 else if (touch.GetPoint(0).state == TouchPoint::Up)
1314 SetCursorVisibility( true );
1315 StartCursorBlinkTimer();
1320 // selection handle one
1321 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1323 if (touch.GetPoint(0).state == TouchPoint::Down)
1325 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1327 else if (touch.GetPoint(0).state == TouchPoint::Up)
1329 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1334 // selection handle two
1335 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1337 if (touch.GetPoint(0).state == TouchPoint::Down)
1339 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1341 else if (touch.GetPoint(0).state == TouchPoint::Up)
1343 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1348 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1350 // If text exists then select nearest word.
1351 if ( !mStyledText.empty())
1355 ShowGrabHandleAndSetVisibility( false );
1360 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1361 // converts the pre-edit word being displayed to a committed word.
1362 if ( !mUnderlinedPriorToPreEdit )
1365 style.SetUnderline( false );
1366 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1368 mPreEditFlag = false;
1369 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1370 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1371 PreEditReset( false );
1373 mCursorPosition = 0;
1375 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1376 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1378 std::size_t start = 0;
1379 std::size_t end = 0;
1380 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1382 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1384 ImfManager imfManager = ImfManager::Get();
1387 imfManager.SetCursorPosition ( mCursorPosition );
1388 imfManager.NotifyCursorPosition();
1391 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1393 SelectText( start, end );
1394 ShowPopupCutCopyPaste();
1398 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1399 HidePopup( false ); // Hide popup with setting to do auto show.
1400 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1404 else if ( mClipboard && mClipboard.NumberOfItems() )
1406 ShowPopupCutCopyPaste();
1409 // If no text and clipboard empty then do nothing
1412 // TODO: Change the function name to be more general.
1413 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1415 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1416 , (mEditOnTouch)?"true":"false"
1417 , (mEditModeActive)?"true":"false");
1419 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1424 if( mGrabArea == actor )
1426 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1428 SetUpPopupSelection();
1438 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1440 // Initially don't create the grab handle.
1441 bool createGrabHandle = false;
1443 if ( !mEditModeActive )
1445 // update line height before calculate the actual position.
1448 // Only start edit mode if TextInput configured to edit on touch
1451 // Set the initial cursor position in the tap point.
1452 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1458 // Show the keyboard if it was hidden.
1459 if (!VirtualKeyboard::IsVisible())
1461 VirtualKeyboard::Show();
1464 // Reset keyboard as tap event has occurred.
1465 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1466 PreEditReset( true );
1468 GetTextLayoutInfo();
1470 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1472 // 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.
1474 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1476 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1478 // Notify keyboard so it can 're-capture' word for predictive text.
1479 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1480 ImfManager imfManager = ImfManager::Get();
1483 imfManager.SetCursorPosition ( mCursorPosition );
1484 imfManager.NotifyCursorPosition();
1486 const TextStyle oldInputStyle( mInputStyle );
1488 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1492 // Create the grab handle.
1493 // Grab handle is created later.
1494 createGrabHandle = true;
1496 if( oldInputStyle != mInputStyle )
1498 // Updates the line height accordingly with the input style.
1501 EmitStyleChangedSignal();
1506 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1507 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1508 // otherwise the Grab handle will be shown when selecting.
1509 if ( createGrabHandle && IsGrabHandleEnabled() )
1511 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1512 bool altPositionValid; // Alternate cursor validity flag.
1513 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1514 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1516 if( altPositionValid )
1518 // Check which of the positions is the closest.
1519 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1521 cursorPosition = altPosition;
1527 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1528 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1529 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1530 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1535 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1537 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1539 // Ignore longpress if in selection mode already
1540 if( mHighlightMeshActor )
1545 if(longPress.state == Dali::Gesture::Started)
1547 // Start edit mode on long press
1548 if ( !mEditModeActive )
1553 // If text exists then select nearest word.
1554 if ( !mStyledText.empty())
1558 ShowGrabHandleAndSetVisibility( false );
1563 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1564 // converts the pre-edit word being displayed to a committed word.
1565 if ( !mUnderlinedPriorToPreEdit )
1568 style.SetUnderline( false );
1569 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1571 mPreEditFlag = false;
1572 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1573 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1574 PreEditReset( false );
1576 mCursorPosition = 0;
1578 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1579 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1581 std::size_t start = 0;
1582 std::size_t end = 0;
1583 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1585 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1587 ImfManager imfManager = ImfManager::Get();
1590 imfManager.SetCursorPosition ( mCursorPosition );
1591 imfManager.NotifyCursorPosition();
1594 SelectText( start, end );
1597 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1598 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1600 ShowPopupCutCopyPaste();
1605 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1607 const Text clipboardText( notifier.GetContent() );
1608 PasteText( clipboardText );
1610 SetCursorVisibility( true );
1611 StartCursorBlinkTimer();
1613 ShowGrabHandleAndSetVisibility( false );
1619 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1621 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1623 const std::string& name = button.GetName();
1625 if(name == TextInputPopup::OPTION_SELECT_WORD)
1627 std::size_t start = 0;
1628 std::size_t end = 0;
1629 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1631 SelectText( start, end );
1633 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1635 SetCursorVisibility(false);
1636 StopCursorBlinkTimer();
1638 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1639 std::size_t start = 0;
1641 SelectText( start, end );
1643 else if(name == TextInputPopup::OPTION_CUT)
1645 bool ret = CopySelectedTextToClipboard();
1649 DeleteHighlightedText( true );
1653 SetCursorVisibility( true );
1654 StartCursorBlinkTimer();
1658 else if(name == TextInputPopup::OPTION_COPY)
1660 CopySelectedTextToClipboard();
1664 SetCursorVisibility( true );
1665 StartCursorBlinkTimer();
1669 else if(name == TextInputPopup::OPTION_PASTE)
1671 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1673 PasteText(retrievedString);
1675 SetCursorVisibility( true );
1676 StartCursorBlinkTimer();
1678 ShowGrabHandleAndSetVisibility( false );
1682 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1684 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1685 // Hence pass the false parameter for signalFinished.
1686 HidePopup( true, false );
1687 mClipboard.ShowClipboard();
1693 bool TextInput::OnCursorBlinkTimerTick()
1696 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1697 if ( mCursorRTLEnabled )
1699 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1701 mCursorBlinkStatus = !mCursorBlinkStatus;
1706 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1708 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1710 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1711 if(mHighlightMeshActor && mState == StateEdit)
1713 ShowPopupCutCopyPaste();
1717 //FIXME this routine needs to be re-written as it contains too many branches.
1718 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1720 std::string keyName = event.keyPressedName;
1721 std::string keyString = event.keyPressed;
1723 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1725 // Do not consume "Tab" and "Escape" keys.
1726 if(keyName == "Tab" || keyName == "Escape")
1728 // Escape key to end the edit mode
1734 HidePopup(); // If Pop-up shown then hides it as editing text.
1736 // Update Flag, indicates whether to update the text-input contents or not.
1737 // Any key stroke that results in a visual change of the text-input should
1738 // set this flag to true.
1741 // Whether to scroll text to cursor position.
1742 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1743 bool scroll = false;
1745 if (keyName == "Return")
1747 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1749 bool preEditFlagPreviouslySet( mPreEditFlag );
1751 // replaces highlighted text with new line
1752 DeleteHighlightedText( false );
1754 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1756 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1757 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1760 mCommitByKeyInput = true;
1763 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1764 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1766 mPreEditFlag = true;
1767 mIgnoreCommitFlag = false;
1777 else if ( keyName == "space" )
1779 if ( mHighlightMeshActor )
1781 // Some text is selected so erase it before adding space.
1782 DeleteHighlightedText( true );
1785 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1787 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1788 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1791 mCommitByKeyInput = true;
1796 else if (keyName == "BackSpace")
1798 if ( mHighlightMeshActor )
1800 // Some text is selected so erase it
1801 DeleteHighlightedText( true );
1806 if ( mCursorPosition > 0 )
1808 DeleteCharacter( mCursorPosition );
1814 else if (keyName == "Right")
1819 else if (keyName == "Left")
1821 AdvanceCursor(true);
1824 else // event is a character
1826 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1827 if ( !keyString.empty() )
1829 // replaces highlighted text with new character
1830 DeleteHighlightedText( false );
1832 // Received key String
1833 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1839 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1840 // as this is a costly operation.
1846 if(update || scroll)
1848 if( IsScrollEnabled() )
1850 // Calculates the new cursor position (in actor coordinates)
1851 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1853 ScrollTextViewToMakeCursorVisible( cursorPosition );
1860 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1862 std::string keyName = event.keyPressedName;
1863 std::string keyString = event.keyPressed;
1865 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1867 // The selected text become deselected when the key code is DALI_KEY_BACK.
1868 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1877 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1878 const Vector3& cursorPositionTwo,
1879 bool altPositionValidOne,
1880 bool altPositionValidTwo,
1881 const Vector3& altPositionOne,
1882 const Vector3& altPositionTwo )
1884 // TODO VCC Valid for one line.
1885 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1886 if( cursorPositionOne != cursorPositionTwo )
1888 if( cursorPositionOne.x < cursorPositionTwo.x )
1890 mSelectionHandleOneActualPosition = cursorPositionOne;
1891 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1895 mSelectionHandleOneActualPosition = cursorPositionTwo;
1896 mSelectionHandleTwoActualPosition = cursorPositionOne;
1901 mSelectionHandleOneActualPosition = cursorPositionOne;
1902 if( altPositionValidOne )
1904 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1906 mSelectionHandleOneActualPosition = altPositionOne;
1909 if( altPositionValidTwo )
1911 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1913 mSelectionHandleOneActualPosition = altPositionTwo;
1917 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1918 if( altPositionValidTwo )
1920 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1922 mSelectionHandleTwoActualPosition = altPositionTwo;
1925 if( altPositionValidOne )
1927 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1929 mSelectionHandleTwoActualPosition = altPositionOne;
1935 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1937 // Updates the stored scroll position.
1938 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1940 const Vector3& controlSize = GetControlSize();
1941 Size cursorSize( CURSOR_THICKNESS, 0.f );
1943 // Updates the cursor and grab handle position and visibility.
1944 if( mGrabHandle || mCursor )
1946 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1948 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1949 bool altPositionValid; // Alternate cursor validity flag.
1950 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1951 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1953 if( altPositionValid )
1955 // Check which of the positions is the closest.
1956 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1958 cursorPosition = altPosition;
1962 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1964 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1968 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1969 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1974 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1975 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1979 // Updates the selection handles and highlighted text position and visibility.
1980 if( mSelectionHandleOne && mSelectionHandleTwo )
1982 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1983 bool altPositionValidOne; // Alternate cursor validity flag.
1984 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1985 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1987 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1988 bool altPositionValidTwo; // Alternate cursor validity flag.
1989 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1990 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1992 // VCC TODO: This method is a hack for one line.
1993 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1995 altPositionValidOne,
1996 altPositionValidTwo,
2000 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
2001 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
2002 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
2003 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
2005 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
2006 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
2007 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
2008 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
2010 if( mHighlightMeshActor )
2012 mHighlightMeshActor.SetVisible( true );
2018 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2020 // Scroll the text to make the cursor visible.
2021 const Size cursorSize( CURSOR_THICKNESS,
2022 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2024 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2026 const Vector3& controlSize = GetControlSize();
2028 // Calculates the new scroll position.
2029 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2030 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2032 scrollOffset.x += cursorPosition.x;
2035 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2037 scrollOffset.y += cursorPosition.y;
2040 // Sets the new scroll position.
2041 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2042 SetScrollPosition( scrollOffset );
2045 void TextInput::StartScrollTimer()
2049 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2050 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2053 if( !mScrollTimer.IsRunning() )
2055 mScrollTimer.Start();
2059 void TextInput::StopScrollTimer()
2063 mScrollTimer.Stop();
2067 bool TextInput::OnScrollTimerTick()
2069 // TODO: need to set the new style accordingly the new handle position.
2071 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2073 // nothing to do if all handles are invisible or doesn't exist.
2079 // Choose between the grab handle or the selection handles.
2080 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2081 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2082 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2084 std::size_t newCursorPosition = 0;
2085 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2087 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2088 // the new selection handle's position needs to be different of the other one.
2089 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2090 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2091 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2093 if( differentSelectionHandles )
2095 handlePosition = newCursorPosition;
2097 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2099 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2101 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2102 scrollPosition += scrollDelta;
2103 SetScrollPosition( scrollPosition );
2105 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2110 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2113 actualHandlePosition.x += mScrollDisplacement.x;
2114 actualHandlePosition.y += mScrollDisplacement.y;
2119 // Public Internal Methods (public for testing purpose)
2121 void TextInput::SetUpTouchEvents()
2123 if ( !mTapDetector )
2125 mTapDetector = TapGestureDetector::New();
2126 // Attach the actors and connect the signal
2127 mTapDetector.Attach(Self());
2129 // As contains children which may register for tap the default control detector is not used.
2130 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2133 if ( !mDoubleTapDetector )
2135 mDoubleTapDetector = TapGestureDetector::New();
2136 mDoubleTapDetector.SetTapsRequired( 2 );
2137 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2139 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2140 // so that we do not, unnecessarily, have a double tap request all the time
2143 if ( !mPanGestureDetector )
2145 mPanGestureDetector = PanGestureDetector::New();
2146 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2149 if ( !mLongPressDetector )
2151 mLongPressDetector = LongPressGestureDetector::New();
2152 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2153 mLongPressDetector.Attach(Self());
2157 void TextInput::CreateTextViewActor()
2159 mDisplayedTextView = Toolkit::TextView::New();
2160 mDisplayedTextView.SetName( "DisplayedTextView ");
2161 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2162 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2163 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2164 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2165 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2166 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2167 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2168 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2169 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2170 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2172 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2174 Self().Add( mDisplayedTextView );
2177 // Start a timer to initiate, used by the cursor to blink.
2178 void TextInput::StartCursorBlinkTimer()
2180 if ( !mCursorBlinkTimer )
2182 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2183 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2186 if ( !mCursorBlinkTimer.IsRunning() )
2188 mCursorBlinkTimer.Start();
2192 // Start a timer to initiate, used by the cursor to blink.
2193 void TextInput::StopCursorBlinkTimer()
2195 if ( mCursorBlinkTimer )
2197 mCursorBlinkTimer.Stop();
2201 void TextInput::StartEditMode()
2203 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2205 if(!mEditModeActive)
2210 if ( mDoubleTapDetector )
2212 mDoubleTapDetector.Attach( Self() );
2216 void TextInput::EndEditMode()
2218 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2220 ClearKeyInputFocus();
2222 if ( mDoubleTapDetector )
2224 mDoubleTapDetector.Detach( Self() );
2228 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2230 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2232 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2234 style.SetUnderline( true );
2235 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2239 void TextInput::RemovePreEditStyle()
2241 if ( !mUnderlinedPriorToPreEdit )
2244 style.SetUnderline( false );
2245 SetActiveStyle( style, TextStyle::UNDERLINE );
2249 // IMF related methods
2252 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2254 bool update( false );
2255 bool preeditResetRequired ( false );
2257 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2259 HidePopup(); // If Pop-up shown then hides it as editing text.
2262 switch ( imfEvent.eventName )
2264 case ImfManager::PREEDIT:
2266 mIgnoreFirstCommitFlag = false;
2268 // 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
2269 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2271 // replaces highlighted text with new character
2272 DeleteHighlightedText( false );
2275 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2277 if( IsScrollEnabled() )
2279 // Calculates the new cursor position (in actor coordinates)
2280 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2281 ScrollTextViewToMakeCursorVisible( cursorPosition );
2288 case ImfManager::COMMIT:
2290 if( mIgnoreFirstCommitFlag )
2292 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2293 mIgnoreFirstCommitFlag = false;
2297 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2299 // 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
2300 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2302 // replaces highlighted text with new character
2303 DeleteHighlightedText( false );
2306 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2307 // not needed, one such scenario is when the pre-edit word is too long to fit.
2308 if ( !mIgnoreCommitFlag )
2310 update = CommitReceived( imfEvent.predictiveString );
2314 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2320 if( IsScrollEnabled() )
2322 // Calculates the new cursor position (in actor coordinates)
2323 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2325 ScrollTextViewToMakeCursorVisible( cursorPosition );
2330 case ImfManager::DELETESURROUNDING:
2332 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2333 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2335 mPreEditFlag = false;
2337 std::size_t toDelete = 0;
2338 std::size_t numberOfCharacters = 0;
2340 if( mHighlightMeshActor )
2342 // delete highlighted text.
2343 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2344 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2348 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2350 toDelete = mCursorPosition + imfEvent.cursorOffset;
2352 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2354 numberOfCharacters = mStyledText.size() - toDelete;
2358 numberOfCharacters = imfEvent.numberOfChars;
2361 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2362 DeleteRange( toDelete, numberOfCharacters );
2364 mCursorPosition = toDelete;
2365 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2369 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2372 case ImfManager::GETSURROUNDING:
2374 // 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
2375 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2376 if (! ( mHighlightMeshActor || mSelectingText ) )
2378 std::string text( GetText() );
2379 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2381 imfManager.SetCursorPosition( mCursorPosition );
2382 imfManager.SetSurroundingText( text );
2385 if( 0 != mNumberOfSurroundingCharactersDeleted )
2387 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2388 mNumberOfSurroundingCharactersDeleted = 0;
2390 if( mStyledText.empty() )
2392 ShowPlaceholderText( mStyledPlaceHolderText );
2397 case ImfManager::VOID:
2399 DALI_ASSERT_DEBUG( false );
2403 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2405 return callbackData;
2408 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2410 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2412 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2413 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2415 bool preeditResetRequest ( false );
2417 if( mPreEditFlag ) // Already in pre-edit state.
2419 if( mStyledText.size() >= mMaxStringLength )
2421 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2422 // Cannot fit these characters into field, clear pre-edit.
2423 if ( !mUnderlinedPriorToPreEdit )
2426 style.SetUnderline( false );
2427 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2429 mIgnoreCommitFlag = true;
2430 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2431 mPreEditFlag = false;
2432 EmitMaxInputCharactersReachedSignal();
2436 // delete existing pre-edit string
2437 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2439 // Store new pre-edit string
2440 mPreEditString.SetText( keyString );
2442 if ( keyString.empty() )
2444 mPreEditFlag = false;
2445 mCursorPosition = mPreEditStartPosition;
2447 if( mStyledText.empty() )
2449 ShowPlaceholderText( mStyledPlaceHolderText );
2453 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2456 GetTextLayoutInfo();
2461 // Insert new pre-edit string. InsertAt updates the size and position table.
2462 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2463 // 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.
2464 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2465 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2466 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2469 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2473 else // mPreEditFlag not set
2475 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2477 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2478 // new pre-edit so move into pre-edit state by setting flag
2479 mPreEditFlag = true;
2480 mPreEditString.SetText( keyString ); // store new pre-edit string
2481 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2482 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2483 // 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.
2484 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2485 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2486 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2487 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2493 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2497 return preeditResetRequest;
2500 bool TextInput::CommitReceived(const std::string& keyString )
2502 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2503 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2505 bool update( false );
2507 RemovePreEditStyle();
2509 const std::size_t styledTextSize( mStyledText.size() );
2510 if( styledTextSize >= mMaxStringLength )
2512 // Cannot fit these characters into field, clear pre-edit.
2515 mIgnoreCommitFlag = true;
2516 mPreEditFlag = false;
2518 EmitMaxInputCharactersReachedSignal();
2524 // delete existing pre-edit string
2525 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2526 mPreEditFlag = false;
2528 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2529 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2531 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2533 // No need to update cursor position as Cursor location given by touch.
2534 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2535 mPreserveCursorPosition = false;
2539 // Cursor not set by touch so needs to be re-positioned to input more text
2540 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2542 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2543 if ( mCommitByKeyInput )
2545 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2546 mCommitByKeyInput = false;
2552 if ( mSelectTextOnCommit )
2554 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2559 else // mPreEditFlag not set
2561 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2563 if( mStyledText.empty() && mPlaceHolderSet )
2565 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2566 mDisplayedTextView.SetText( "" );
2567 mNumberOfSurroundingCharactersDeleted = 0;
2568 mPlaceHolderSet = false;
2570 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2572 mNumberOfSurroundingCharactersDeleted = 0;
2577 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2582 mSelectTextOnCommit = false;
2584 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2585 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2590 // End of IMF related methods
2592 std::size_t TextInput::DeletePreEdit()
2594 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2596 DALI_ASSERT_DEBUG( mPreEditFlag );
2598 const std::size_t preEditStringLength = mPreEditString.GetLength();
2599 const std::size_t styledTextSize = mStyledText.size();
2601 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2603 // Prevents erase items outside mStyledText bounds.
2604 if( mPreEditStartPosition > styledTextSize )
2606 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2607 mPreEditStartPosition = styledTextSize;
2610 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2612 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2613 endPosition = styledTextSize;
2616 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2618 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2619 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2621 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2623 return preEditStringLength;
2626 void TextInput::PreEditReset( bool preserveCursorPosition )
2628 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2629 preserveCursorPosition, mCursorPosition);
2631 // 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.
2632 mPreserveCursorPosition = preserveCursorPosition;
2634 // Reset incase we are in a pre-edit state.
2635 ImfManager imfManager = ImfManager::Get();
2638 imfManager.Reset(); // Will trigger a commit message
2642 void TextInput::CursorUpdate()
2646 ImfManager imfManager = ImfManager::Get();
2649 std::string text( GetText() );
2650 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2651 imfManager.SetCursorPosition ( mCursorPosition );
2652 imfManager.NotifyCursorPosition();
2656 /* Delete highlighted characters redisplay*/
2657 void TextInput::DeleteHighlightedText( bool inheritStyle )
2659 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2661 if( mHighlightMeshActor )
2663 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2665 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2666 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2668 // Get the styled text of the characters to be deleted as it may be needed if
2669 // the "exceed the text-input's boundaries" option is disabled.
2670 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2672 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2674 mStyledText.erase( start, end ); // erase range of characters
2676 // Remove text from TextView and update place holder text if required
2678 // Set the placeholder text only if the styled text is empty.
2679 if( mStyledText.empty() )
2681 ShowPlaceholderText( mStyledPlaceHolderText );
2685 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2687 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2689 // It may happen than after removing a white space or a new line character,
2690 // two words merge, this new word could be big enough to not fit in its
2691 // current line, so moved to the next one, and make some part of the text to
2692 // exceed the text-input's boundary.
2693 if( !mExceedEnabled )
2695 // Get the new text layout after removing some characters.
2696 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2698 // Get text-input's size.
2699 const Vector3& size = GetControlSize();
2701 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2702 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2704 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2706 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2707 styledCharactersToDelete.begin(),
2708 styledCharactersToDelete.end() );
2712 GetTextLayoutInfo();
2720 const TextStyle oldInputStyle( mInputStyle );
2722 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2724 if( oldInputStyle != mInputStyle )
2726 // Updates the line height accordingly with the input style.
2729 EmitStyleChangedSignal();
2735 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2737 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2738 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2740 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2743 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2745 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2746 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2748 mStyledText.erase(itStart, itEnd);
2750 // update the selection handles if they are visible.
2751 if( mHighlightMeshActor )
2753 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2754 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2756 if( minHandle >= start + ncharacters )
2758 minHandle -= ncharacters;
2760 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2765 if( maxHandle >= start + ncharacters )
2767 maxHandle -= ncharacters;
2769 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2775 // 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.
2778 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2780 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2781 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2782 // Mean we do not re-draw the text more than we have too.
2785 /* Delete character at current cursor position and redisplay*/
2786 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2788 // Ensure positionToDelete is not out of bounds.
2789 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2790 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2791 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2793 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2796 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2798 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2800 // Get the styled text of the character to be deleted as it may be needed if
2801 // the "exceed the text-input's boundaries" option is disabled.
2802 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2804 mStyledText.erase(it); // erase the character left of positionToDelete
2806 if( mStyledText.empty() )
2808 ShowPlaceholderText( mStyledPlaceHolderText );
2812 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2814 const Character characterToDelete = styledCharacterToDelete.mText[0];
2816 // It may happen than after removing a white space or a new line character,
2817 // two words merge, this new word could be big enough to not fit in its
2818 // current line, so moved to the next one, and make some part of the text to
2819 // exceed the text-input's boundary.
2820 if( !mExceedEnabled )
2822 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2824 // Get the new text layout after removing one character.
2825 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2827 // Get text-input's size.
2828 const Vector3& size = GetControlSize();
2830 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2831 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2833 MarkupProcessor::StyledTextArray array;
2834 array.push_back( styledCharacterToDelete );
2835 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2837 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2842 GetTextLayoutInfo();
2844 ShowGrabHandleAndSetVisibility( false );
2846 mCursorPosition = positionToDelete -1;
2848 const TextStyle oldInputStyle( mInputStyle );
2850 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2852 if( oldInputStyle != mInputStyle )
2854 // Updates the line height accordingly with the input style.
2857 EmitStyleChangedSignal();
2862 /*Insert new character into the string and (optionally) redisplay text-input*/
2863 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2865 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2867 // Ensure insertionPosition is not out of bounds.
2868 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2870 bool textExceedsMaximunNumberOfCharacters = false;
2871 bool textExceedsBoundary = false;
2872 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2874 ShowGrabHandleAndSetVisibility( false );
2876 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2880 mIgnoreCommitFlag = true;
2881 mPreEditFlag = false;
2882 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2883 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2886 if( textExceedsMaximunNumberOfCharacters )
2888 EmitMaxInputCharactersReachedSignal();
2891 if( textExceedsBoundary )
2893 EmitInputTextExceedsBoundariesSignal();
2894 PreEditReset( false );
2898 return insertedStringLength;
2901 ImageActor TextInput::CreateCursor( const Vector4& color)
2904 cursor = CreateSolidColorActor(color);
2905 cursor.SetName( "Cursor" );
2907 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2908 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2909 cursor.SetVisible(false);
2914 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2916 // As cursor is not moving due to grab handle, handle should be hidden.
2917 ShowGrabHandleAndSetVisibility( false );
2919 bool cursorPositionChanged = false;
2922 if ( mCursorPosition >= places )
2924 mCursorPosition = mCursorPosition - places;
2925 cursorPositionChanged = true;
2930 if ((mCursorPosition + places) <= mStyledText.size())
2932 mCursorPosition = mCursorPosition + places;
2933 cursorPositionChanged = true;
2937 if( cursorPositionChanged )
2939 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2941 const TextStyle oldInputStyle( mInputStyle );
2942 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2946 if( oldInputStyle != mInputStyle )
2948 // Updates the line height accordingly with the input style.
2951 EmitStyleChangedSignal();
2954 ImfManager imfManager = ImfManager::Get();
2957 imfManager.SetCursorPosition ( mCursorPosition );
2958 imfManager.NotifyCursorPosition();
2963 void TextInput::DrawCursor()
2965 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2967 // Get height of cursor and set its size
2968 Size size( CURSOR_THICKNESS, 0.0f );
2969 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2971 size.height = rowRect.height;
2975 // Measure Font so know how big text will be if no initial text to measure.
2976 size.height = mLineHeight;
2979 mCursor.SetSize(size);
2981 // If the character is italic then the cursor also tilts.
2982 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2984 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2986 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2988 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2989 bool altPositionValid; // Alternate cursor validity flag.
2990 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2991 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2993 SetAltCursorEnabled( altPositionValid );
2995 if( !altPositionValid )
2997 mCursor.SetPosition( position + UI_OFFSET );
3001 size.height *= 0.5f;
3002 mCursor.SetSize(size);
3003 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
3005 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
3006 size.height = rowRect.height * 0.5f;
3007 mCursorRTL.SetSize(size);
3008 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
3011 if( IsScrollEnabled() )
3013 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3014 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3019 void TextInput::SetAltCursorEnabled( bool enabled )
3021 mCursorRTLEnabled = enabled;
3022 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3025 void TextInput::SetCursorVisibility( bool visible )
3027 mCursorVisibility = visible;
3028 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3029 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3032 void TextInput::CreateGrabHandle( Dali::Image image )
3038 mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3042 mGrabHandleImage = image;
3045 mGrabHandle = ImageActor::New(mGrabHandleImage);
3046 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3047 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3049 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3051 ShowGrabHandleAndSetVisibility( false );
3053 CreateGrabArea( mGrabHandle );
3055 mActiveLayer.Add(mGrabHandle);
3059 void TextInput::CreateGrabArea( Actor& parent )
3061 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3062 mGrabArea.SetName( "GrabArea" );
3063 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3064 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3065 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
3066 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3067 mTapDetector.Attach( mGrabArea );
3068 mPanGestureDetector.Attach( mGrabArea );
3069 mLongPressDetector.Attach( mGrabArea );
3071 parent.Add(mGrabArea);
3074 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3076 Vector3 actualHandlePosition;
3080 mActualGrabHandlePosition.x += displacement.x;
3081 mActualGrabHandlePosition.y += displacement.y;
3083 // Grab handle should jump to the nearest character and take cursor with it
3084 std::size_t newCursorPosition = 0;
3085 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3087 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3088 bool altPositionValid; // Alternate cursor validity flag.
3089 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3090 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3092 if( altPositionValid )
3094 // Check which of the positions is the closest.
3095 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3097 actualHandlePosition = altPosition;
3101 bool handleVisible = true;
3103 if( IsScrollEnabled() )
3105 const Vector3 controlSize = GetControlSize();
3106 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3107 // Scrolls the text if the handle is not in a visible position
3108 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3115 mCurrentHandlePosition = actualHandlePosition;
3116 mScrollDisplacement = Vector2::ZERO;
3120 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3122 mScrollDisplacement.x = -SCROLL_SPEED;
3124 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3126 mScrollDisplacement.x = SCROLL_SPEED;
3128 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3130 mScrollDisplacement.y = -SCROLL_SPEED;
3132 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3134 mScrollDisplacement.y = SCROLL_SPEED;
3140 if( handleVisible && // Only redraw cursor and do updates if position changed
3141 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3143 mCursorPosition = newCursorPosition;
3145 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3147 const TextStyle oldInputStyle( mInputStyle );
3149 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3151 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3153 if( oldInputStyle != mInputStyle )
3155 // Updates the line height accordingly with the input style.
3158 EmitStyleChangedSignal();
3163 return actualHandlePosition;
3166 void TextInput::ShowGrabHandle( bool visible )
3168 if ( IsGrabHandleEnabled() )
3172 mGrabHandle.SetVisible( mGrabHandleVisibility );
3174 StartMonitoringStageForTouch();
3178 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3180 mGrabHandleVisibility = visible;
3181 ShowGrabHandle( visible );
3184 // Callbacks connected to be Property notifications for Boundary checking.
3186 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3188 mIsSelectionHandleOneFlipped = true;
3189 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3190 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3193 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3195 mIsSelectionHandleOneFlipped = false;
3196 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3197 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3200 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3202 mIsSelectionHandleTwoFlipped = true;
3203 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3204 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3207 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3209 mIsSelectionHandleTwoFlipped = false;
3210 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3211 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3214 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3215 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3217 mSelectionHandleOne.SetOpacity(0.0f);
3220 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3222 mSelectionHandleOne.SetOpacity(1.0f);
3225 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3227 mSelectionHandleTwo.SetOpacity(0.0f);
3230 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3232 mSelectionHandleTwo.SetOpacity(1.0f);
3235 // End of Callbacks connected to be Property notifications for Boundary checking.
3237 void TextInput::SetUpHandlePropertyNotifications()
3239 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3241 Vector3 handlesize = GetSelectionHandleSize();
3243 // Exceeding horizontal boundary
3244 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionX, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3245 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3247 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionX, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3248 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3250 // Within horizontal boundary
3251 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionX, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3252 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3254 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionX, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3255 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3257 // Exceeding vertical boundary
3258 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionY,
3259 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3260 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3261 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3263 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionY,
3264 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3265 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3266 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3268 // Within vertical boundary
3269 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionY,
3270 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3271 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3272 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3274 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionY,
3275 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3276 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3277 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3280 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3282 mSelectionHandleOnePosition = start;
3283 mSelectionHandleTwoPosition = end;
3285 if ( !mSelectionHandleOne )
3287 // create normal and pressed images
3288 mSelectionHandleOneImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3289 mSelectionHandleOneImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3291 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3292 mSelectionHandleOne.SetName("SelectionHandleOne");
3293 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3294 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3295 mIsSelectionHandleOneFlipped = false;
3296 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3298 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3299 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3301 mHandleOneGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3302 mHandleOneGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3303 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3305 mTapDetector.Attach( mHandleOneGrabArea );
3306 mPanGestureDetector.Attach( mHandleOneGrabArea );
3308 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3310 mSelectionHandleOne.Add( mHandleOneGrabArea );
3311 mActiveLayer.Add( mSelectionHandleOne );
3314 if ( !mSelectionHandleTwo )
3316 // create normal and pressed images
3317 mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3318 mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3320 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3321 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3322 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3323 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3324 mIsSelectionHandleTwoFlipped = false;
3325 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3327 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3328 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3329 mHandleTwoGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3330 mHandleTwoGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3331 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3333 mTapDetector.Attach( mHandleTwoGrabArea );
3334 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3336 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3338 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3340 mActiveLayer.Add( mSelectionHandleTwo );
3343 SetUpHandlePropertyNotifications();
3345 // update table as text may have changed.
3346 GetTextLayoutInfo();
3348 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3349 bool altPositionValidOne; // Alternate cursor validity flag.
3350 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3351 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3353 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3354 bool altPositionValidTwo; // Alternate cursor validity flag.
3355 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3356 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3358 // VCC TODO: This method is a hack for one line.
3359 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3361 altPositionValidOne,
3362 altPositionValidTwo,
3366 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3367 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3369 // Calculates and set the visibility if the scroll mode is enabled.
3370 bool isSelectionHandleOneVisible = true;
3371 bool isSelectionHandleTwoVisible = true;
3372 if( IsScrollEnabled() )
3374 const Vector3& controlSize( GetControlSize() );
3375 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3376 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3377 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3378 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3381 CreateHighlight(); // function will only create highlight if not already created.
3384 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3386 Vector3 actualHandlePosition;
3388 if ( mSelectionHandleOne && mSelectionHandleTwo )
3390 const Vector3& controlSize = GetControlSize();
3392 Size cursorSize( CURSOR_THICKNESS, 0.f );
3394 // Get a reference of the wanted selection handle (handle one or two).
3395 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3397 // Get a reference for the current position of the handle and a copy of its pair
3398 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3399 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3401 // Get a handle of the selection handle actor
3402 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3404 // Selection handles should jump to the nearest character
3405 std::size_t newHandlePosition = 0;
3406 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3408 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3409 bool altPositionValid; // Alternate cursor validity flag.
3410 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3411 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3412 if( altPositionValid )
3414 // Check which of the positions is the closest.
3415 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3417 actualHandlePosition = altPosition;
3421 bool handleVisible = true;
3423 if( IsScrollEnabled() )
3425 mCurrentSelectionId = handleId;
3427 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3428 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3429 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3436 mCurrentSelectionHandlePosition = actualHandlePosition;
3437 mScrollDisplacement = Vector2::ZERO;
3441 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3443 mScrollDisplacement.x = -SCROLL_SPEED;
3445 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3447 mScrollDisplacement.x = SCROLL_SPEED;
3449 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3451 mScrollDisplacement.y = -SCROLL_SPEED;
3453 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3455 mScrollDisplacement.y = SCROLL_SPEED;
3461 if ( handleVisible && // Ensure the handle is visible.
3462 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3463 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3465 currentSelectionHandlePosition = newHandlePosition;
3467 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3468 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3472 if ( handleId == HandleOne )
3474 const TextStyle oldInputStyle( mInputStyle );
3476 // Set Active Style to that of first character in selection
3477 if( mSelectionHandleOnePosition < mStyledText.size() )
3479 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3482 if( oldInputStyle != mInputStyle )
3484 // Updates the line height accordingly with the input style.
3487 EmitStyleChangedSignal();
3493 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3496 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3498 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3499 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3501 if ( selectionHandleActor )
3503 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3504 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3505 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3507 if( IsScrollEnabled() )
3509 const Size cursorSize( CURSOR_THICKNESS,
3510 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3511 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3513 GetControlSize() ) );
3518 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3520 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3522 // VCC Set true/false in logical order. TODO : It needs to be checked.
3524 if( startSelection > endSelection )
3526 std::swap( startSelection, endSelection );
3528 std::size_t index = 0u;
3529 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3531 if( ( index < startSelection ) || ( endSelection <= index ) )
3542 // Calculate the dimensions of the quads they will make the highlight mesh
3543 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3545 // At the moment there is no public API to modify the block alignment option.
3547 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3549 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3551 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3552 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3554 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3555 std::vector<bool> selectedVisualText;
3556 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3557 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3558 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3560 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3561 float rowLeft = 0.0f;
3562 float rowRight = 0.0f;
3563 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3564 float maxRowLeft = std::numeric_limits<float>::max();
3565 float maxRowRight = 0.0f;
3567 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3569 // Scan through entire text.
3572 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3574 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3575 bool charSelected = false;
3576 if( selectedIt != selectedEndIt )
3578 charSelected = *selectedIt++;
3581 if( selectionState == SelectionNone )
3585 selectionState = SelectionStarted;
3586 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3587 rowRight = rowLeft + charInfo.mSize.width;
3590 else if( selectionState == SelectionStarted )
3592 // break selection on:
3593 // 1. new line causing selection break. (\n or wordwrap)
3594 // 2. character not selected.
3595 if( !charSelected ||
3596 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3598 // finished selection.
3599 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3600 // that it resides on. That way this enumeration is not necessary.
3602 if(lastIt->mIsNewParagraphChar)
3604 // 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.
3605 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3607 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3608 maxRowLeft = std::min(maxRowLeft, min.x);
3609 maxRowRight = std::max(maxRowRight, max.x);
3610 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3611 float rowTop = rowBottom - rowSize.height;
3613 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3616 rowRight = std::numeric_limits<float>::max();
3618 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3620 selectionState = SelectionNone;
3622 // Still selected? start a new selection
3625 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3627 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3628 selectionState = SelectionStarted;
3633 // build up highlight(s) with this selection data.
3634 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3635 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3642 // If reached end, and still on selection, then close selection.
3645 if(selectionState == SelectionStarted)
3647 // finished selection.
3649 if(lastIt->mIsNewParagraphChar)
3651 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3653 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3654 maxRowLeft = std::min(maxRowLeft, min.x);
3655 maxRowRight = std::max(maxRowRight, max.x);
3656 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3657 float rowTop = rowBottom - rowSize.height;
3658 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3662 // Get the top left and bottom right corners.
3663 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3664 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3665 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3667 // Clamp quads so they appear to clip to borders of the whole text.
3668 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3670 // For block-align align Further Clamp quads to max left and right extents
3671 // BlockAlign: Will adjust highlight to block:
3673 // H[ello] (top row right = max of all rows right)
3674 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3675 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3676 // [text] (bottom row left = min of all rows left)
3677 // (common in SMS messaging selection)
3679 // As opposed to the default which is tight text highlighting.
3684 // (common in regular text editors/web browser selection)
3685 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3687 // Finally clamp quads again so they don't exceed the boundry of the control.
3688 const Vector3& controlSize = GetControlSize();
3689 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3692 return mNewHighlightInfo;
3695 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3696 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3697 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3699 // At the moment there is no public API to modify the block alignment option.
3701 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3703 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3705 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3706 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3708 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3709 std::vector<bool> selectedVisualText;
3710 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3711 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3712 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3714 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3715 float rowLeft = 0.0f;
3716 float rowRight = 0.0f;
3718 // VCC TODO this is valid for one line.
3720 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3722 // Scan through entire text.
3725 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3727 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3728 bool charSelected = false;
3729 if( selectedIt != selectedEndIt )
3731 charSelected = *selectedIt++;
3736 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3737 rowRight = rowLeft + charInfo.mSize.width;
3739 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3740 float rowTop = rowBottom - rowSize.height;
3741 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3747 // Finally clamp quads again so they don't exceed the boundry of the control.
3748 const Vector3& controlSize = GetControlSize();
3749 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3752 return mNewHighlightInfo;
3755 void TextInput::UpdateHighlight()
3757 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3759 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3761 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3762 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3763 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3764 // [BOTTOM] [ MIDDLE ]
3767 // Each quad is created as 2 triangles.
3768 // Middle is just 1 quad regardless of its size.
3782 if ( mHighlightMeshActor )
3784 // vertex and triangle buffers should always be present if MeshActor is alive.
3785 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3786 MeshData::VertexContainer vertices;
3787 Dali::MeshData::FaceIndices faceIndices;
3789 if( !newHighlightInfo.mQuadList.empty() )
3791 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3792 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3794 // vertex position defaults to (0 0 0)
3795 MeshData::Vertex vertex;
3796 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3799 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3801 // Add each quad geometry (a sub-selection) to the mesh data.
3811 QuadCoordinates& quad = *iter;
3813 vertex.x = quad.min.x;
3814 vertex.y = quad.min.y;
3815 vertices.push_back( vertex );
3818 vertex.x = quad.max.x;
3819 vertex.y = quad.min.y;
3820 vertices.push_back( vertex );
3822 // bottom-left (v+2)
3823 vertex.x = quad.min.x;
3824 vertex.y = quad.max.y;
3825 vertices.push_back( vertex );
3827 // bottom-right (v+3)
3828 vertex.x = quad.max.x;
3829 vertex.y = quad.max.y;
3830 vertices.push_back( vertex );
3832 // triangle A (3, 1, 0)
3833 faceIndices.push_back( v + 3 );
3834 faceIndices.push_back( v + 1 );
3835 faceIndices.push_back( v );
3837 // triangle B (0, 2, 3)
3838 faceIndices.push_back( v );
3839 faceIndices.push_back( v + 2 );
3840 faceIndices.push_back( v + 3 );
3842 mMeshData.SetFaceIndices( faceIndices );
3845 BoneContainer bones(0); // passed empty as bones not required
3846 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3847 mHighlightMesh.UpdateMeshData(mMeshData);
3852 void TextInput::ClearPopup()
3854 mPopupPanel.Clear();
3857 void TextInput::AddPopupOptions()
3859 mPopupPanel.AddPopupOptions();
3862 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3864 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3866 Vector3 clampedPosition ( position );
3867 Vector3 tailOffsetPosition ( position );
3869 float xOffSet( 0.0f );
3871 Actor self = Self();
3872 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3874 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3875 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3877 // Clamp to left or right or of boundary
3878 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3880 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3882 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3884 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3887 clampedPosition.x = position.x + xOffSet;
3888 tailOffsetPosition.x = -xOffSet;
3890 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3891 bool flipTail( false );
3893 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3895 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3899 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3900 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3903 void TextInput::HidePopup(bool animate, bool signalFinished )
3905 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3907 mPopupPanel.Hide( animate );
3909 if( animate && signalFinished )
3911 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3916 void TextInput::ShowPopup( bool animate )
3919 Vector2 alternativePopupPosition;
3921 if(mHighlightMeshActor && mState == StateEdit)
3924 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3926 // When text is selected, show popup above top handle (and text), or below bottom handle.
3927 // topHandle: referring to the top most point of the handle or the top line of selection.
3928 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3930 topHandle = mSelectionHandleOneActualPosition;
3931 bottomHandle = mSelectionHandleTwoActualPosition;
3932 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3936 topHandle = mSelectionHandleTwoActualPosition;
3937 bottomHandle = mSelectionHandleOneActualPosition;
3938 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3940 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3941 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3943 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3945 position.x = xPosition;
3947 // Alternative position if no upper space
3948 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3949 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3953 // When no text is selected, show popup at world position of grab handle or cursor
3954 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3955 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3956 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3957 // if can't be positioned above, then position below row.
3958 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3961 // If grab handle enabled then position pop-up below the grab handle.
3962 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3966 SetPopupPosition( position, alternativePopupPosition );
3969 mPopupPanel.Show( Self(), animate );
3970 StartMonitoringStageForTouch();
3972 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3975 void TextInput::ShowPopupCutCopyPaste()
3979 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3980 // Check the selected text is whole text or not.
3981 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3983 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3986 if ( !mStyledText.empty() && IsTextSelected() )
3988 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3989 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3992 if( mClipboard && mClipboard.NumberOfItems() )
3994 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3995 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4000 mPopupPanel.Hide(false);
4004 void TextInput::SetUpPopupSelection( bool showCutButton )
4007 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
4008 // If no text exists then don't offer to select
4009 if ( !mStyledText.empty() )
4011 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
4012 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
4013 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
4015 // if clipboard has valid contents then offer paste option
4016 if( mClipboard && mClipboard.NumberOfItems() )
4018 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4019 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4024 mPopupPanel.Hide(false);
4027 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4032 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4033 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4034 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4035 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4037 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4039 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4041 float closestYdifference = std::numeric_limits<float>::max();
4042 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4043 std::size_t numberOfMatchedCharacters = 0;
4045 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4046 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4048 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4050 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4051 float baselinePosition = info.mPosition.y - info.mDescender;
4053 if( info.mIsVisible )
4055 // store difference between source y point and the y position of the current character
4056 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4058 if( currentYdifference < closestYdifference )
4060 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4061 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4062 closestYdifference = currentYdifference;
4063 matchedCharacters.clear();
4064 numberOfMatchedCharacters = 0; // reset count
4067 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4068 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4070 // ignore new line character.
4071 if( !info.mIsNewParagraphChar )
4073 matchedCharacters.push_back( info );
4074 numberOfMatchedCharacters++;
4078 } // End of loop checking each character's y position in the character layout table
4080 // Check if last character is a newline, if it is
4081 // then need pretend there is an imaginary line afterwards,
4082 // and check if user is touching below previous line.
4083 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4085 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4087 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4091 // 2 Iterate through matching list of y positions and find closest matching X position.
4093 bool matched( false );
4095 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4096 std::size_t visualIndex = 0u;
4097 const std::size_t matchedCharactersSize = matchedCharacters.size();
4098 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4100 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4102 if( info.mIsVisible )
4104 // stop when on left side of character's center.
4105 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4106 if( sourceScrollOffset.x < characterMidPointPosition )
4108 if(info.mIsRightToLeftCharacter)
4110 rightToLeftChar = true;
4112 glyphIntersection = info.mPosition.x;
4117 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4121 if( visualIndex == matchedCharactersSize )
4123 rightToLeftChar = lastRightToLeftChar;
4126 closestIndex = lineOffset + visualIndex;
4128 mClosestCursorPositionEOL = false; // reset
4129 if( ( visualIndex == matchedCharactersSize ) && !matched )
4131 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4134 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4135 if( rightToLeftChar && lastRightToLeftChar )
4137 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4142 // closestIndex is the visual index, need to convert it to the logical index
4143 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4145 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4147 // Checks for situations where user is touching between LTR and RTL
4148 // characters. To identify if the user means the end of a LTR string
4149 // or the beginning of an RTL string, and vice versa.
4150 if( closestIndex > 0 )
4152 if( rightToLeftChar && !lastRightToLeftChar )
4157 // A: In this touch range, the user is indicating that they wish to place
4158 // the cursor at the end of the LTR text.
4159 // B: In this touch range, the user is indicating that they wish to place
4160 // the cursor at the end of the RTL text.
4162 // Result of touching A area:
4163 // [.....LTR]|[RTL......]+
4165 // |: primary cursor (for typing LTR chars)
4166 // +: secondary cursor (for typing RTL chars)
4168 // Result of touching B area:
4169 // [.....LTR]+[RTL......]|
4171 // |: primary cursor (for typing RTL chars)
4172 // +: secondary cursor (for typing LTR chars)
4174 if( sourceScrollOffset.x < glyphIntersection )
4179 else if( !rightToLeftChar && lastRightToLeftChar )
4181 if( sourceScrollOffset.x < glyphIntersection )
4188 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4189 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4190 // one further ahead
4191 if( rightToLeftChar && !lastRightToLeftChar )
4196 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4198 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4200 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4209 float TextInput::GetLineJustificationPosition() const
4211 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4212 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4213 float alignmentOffset = 0.f;
4215 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4216 if( alignment & Toolkit::Alignment::HorizontalLeft )
4218 alignmentOffset = 0.f;
4220 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4222 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4224 else if( alignment & Toolkit::Alignment::HorizontalRight )
4226 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4229 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4230 float justificationOffset = 0.f;
4232 switch( justification )
4234 case Toolkit::TextView::Left:
4236 justificationOffset = 0.f;
4239 case Toolkit::TextView::Center:
4241 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4244 case Toolkit::TextView::Right:
4246 justificationOffset = mTextLayoutInfo.mTextSize.width;
4249 case Toolkit::TextView::Justified:
4251 justificationOffset = 0.f;
4256 DALI_ASSERT_ALWAYS( false );
4260 return alignmentOffset + justificationOffset;
4263 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4265 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4266 A newline character is not inserted in this case */
4268 Vector3 cursorPosition;
4270 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4274 if( characterPosition > 0u )
4276 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4278 // If previous character on a different line then use current characters position
4279 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4281 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4282 if ( mClosestCursorPositionEOL )
4284 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4288 cursorPosition = Vector3( currentCharInfo.mPosition );
4297 // If the character is left to right, the position is the character's position plus its width.
4298 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4300 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4301 cursorPosition.y = currentCharInfo.mPosition.y;
4304 return cursorPosition;
4307 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4309 bool direction = false;
4310 Vector3 alternatePosition;
4311 bool alternatePositionValid = false;
4313 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4316 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4318 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4319 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4320 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4322 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4324 alternatePositionValid = false;
4325 directionRTL = false;
4327 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4329 if( characterPosition == 0u )
4331 // When the cursor position is at the beginning, it should be at the start of the current character.
4332 // If the current character is LTR, then the start is on the right side of the glyph.
4333 // If the current character is RTL, then the start is on the left side of the glyph.
4335 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4337 characterPosition = FindVisibleCharacter( Right, 0u );
4340 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4341 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4343 cursorPosition.x = info.mPosition.x + rtlOffset;
4344 cursorPosition.y = info.mPosition.y;
4345 directionRTL = info.mIsRightToLeftCharacter;
4347 else if( characterPosition > 0u )
4349 // Get the direction of the paragraph.
4350 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4351 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4353 // When cursor is not at beginning, consider possibility of
4354 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4356 // Cursor position should be the end of the last character.
4357 // If the last character is LTR, then the end is on the right side of the glyph.
4358 // If the last character is RTL, then the end is on the left side of the glyph.
4360 --characterPosition;
4362 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4364 characterPosition = FindVisibleCharacter( Left, characterPosition );
4367 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4368 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4370 // VCC TODO : check for a new paragraph character.
4372 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4373 const Vector3& size = GetControlSize();
4375 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4377 --characterPosition;
4379 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4382 if( !info.mIsNewParagraphChar )
4384 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4388 // VCC TODO : check for a new paragraph character.
4390 // When cursor points to first character on new line, position cursor at the start of this glyph.
4391 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4393 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4394 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4396 cursorPosition.x = infoNext.mPosition.x + start;
4397 cursorPosition.y = infoNext.mPosition.y;
4401 // If cursor points to the end of text, then can only position
4402 // cursor where the new line starts based on the line-justification position.
4403 cursorPosition.x = GetLineJustificationPosition();
4405 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4407 // If this is after the last character, then we can assume that the new cursor
4408 // should be exactly one row below the current row.
4410 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4411 cursorPosition.y = info.mPosition.y + rowRect.height;
4415 // If this is not after last character, then we can use this row's height.
4416 // should be exactly one row below the current row.
4418 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4419 cursorPosition.y = info.mPosition.y + rowRect.height;
4424 directionRTL = info.mIsRightToLeftCharacter;
4426 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4428 // 1. When the cursor is neither at the beginning or the end,
4429 // we can show multiple cursors under situations when the cursor is
4430 // between RTL and LTR text...
4431 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4433 std::size_t characterAltPosition = characterPosition + 1u;
4435 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4437 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4439 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4440 // Text: [...LTR...]|[...RTL...]
4442 // Alternate cursor pos: ^
4443 // In which case we need to display an alternate cursor for the RTL text.
4445 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4446 alternatePosition.y = infoAlt.mPosition.y;
4447 alternatePositionValid = true;
4449 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4451 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4452 // Text: |[...RTL...] [...LTR....]
4454 // Alternate cursor pos: ^
4455 // In which case we need to display an alternate cursor for the RTL text.
4457 alternatePosition.x = infoAlt.mPosition.x;
4458 alternatePosition.y = infoAlt.mPosition.y;
4459 alternatePositionValid = true;
4464 // 2. When the cursor is at the end of the text,
4465 // and we have multi-directional text,
4466 // we can also consider showing mulitple cursors.
4467 // The rule here is:
4468 // If first and last characters on row are different
4469 // Directions, then two cursors need to be displayed.
4471 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4473 // The last character's direction is differernt than the first one of current paragraph.
4476 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4478 if(info.mIsRightToLeftCharacter)
4480 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4481 // Text: [...LTR...]|[...RTL...]
4483 // Alternate cursor pos: ^
4484 // In which case we need to display an alternate cursor for the RTL text, this cursor
4485 // should be at the end of the given line.
4487 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4488 alternatePosition.y = infoStart.mPosition.y;
4489 alternatePositionValid = true;
4491 else if(!info.mIsRightToLeftCharacter) // starting RTL
4493 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4494 // Text: |[...RTL...] [...LTR....]
4496 // Alternate cursor pos: ^
4497 // In which case we need to display an alternate cursor for the RTL text.
4499 alternatePosition.x = infoStart.mPosition.x;
4500 alternatePosition.y = infoStart.mPosition.y;
4501 alternatePositionValid = true;
4506 } // characterPosition > 0
4510 // If the character table is void, place the cursor accordingly the text alignment.
4511 const Vector3& size = GetControlSize();
4513 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4514 float alignmentOffset = 0.f;
4516 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4517 if( alignment & Toolkit::Alignment::HorizontalLeft )
4519 alignmentOffset = 0.f;
4521 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4523 alignmentOffset = 0.5f * ( size.width );
4525 else if( alignment & Toolkit::Alignment::HorizontalRight )
4527 alignmentOffset = size.width;
4530 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4531 cursorPosition.x = alignmentOffset;
4533 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4534 if( alignment & Toolkit::Alignment::VerticalTop )
4536 cursorPosition.y = mLineHeight;
4538 else if( alignment & Toolkit::Alignment::VerticalCenter )
4540 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4542 else if( alignment & Toolkit::Alignment::VerticalBottom )
4544 cursorPosition.y = size.height;
4548 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4549 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4551 if( alternatePositionValid )
4553 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4554 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4557 return cursorPosition;
4560 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4562 // scan string from current position to beginning of current line to note direction of line
4563 while( logicalPosition )
4566 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4573 return logicalPosition;
4576 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4578 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4580 while( logicalPosition )
4583 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4590 return logicalPosition;
4593 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4597 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4600 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4602 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4603 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4605 min = Vector2::ZERO;
4606 max = Vector2(0.0f, mLineHeight);
4610 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4612 // Initializes the min and max position.
4613 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4614 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4618 // 1) Find the line where the character is laid-out.
4619 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4620 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4623 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4625 // Index within the whole text to the last character of the current line.
4626 std::size_t lastCharacterOfLine = 0u;
4628 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4629 if( lineNextIt != lineEndIt )
4631 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4635 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4638 // Check if the given chracter position is within the line.
4639 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4641 // 2) Get the row rect of all laid-out characters on the line.
4643 // Need to scan all characters of the line because they are in the logical position.
4644 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4645 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4649 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4651 min.x = std::min( min.x, characterInfo.mPosition.x );
4652 min.y = std::min( min.y, characterInfo.mPosition.y );
4653 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4654 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4661 return Size( max.x - min.x, max.y - min.y );
4664 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4666 Actor popUpPanel = mPopupPanel.GetRootActor();
4668 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4674 Dali::Actor parent( touchedActor.GetParent() );
4678 return WasTouchedCheck( parent );
4685 void TextInput::StartMonitoringStageForTouch()
4687 Stage stage = Stage::GetCurrent();
4688 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4691 void TextInput::EndMonitoringStageForTouch()
4693 Stage stage = Stage::GetCurrent();
4694 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4697 void TextInput::OnStageTouched(const TouchEvent& event)
4699 if( event.GetPointCount() > 0 )
4701 if ( TouchPoint::Down == event.GetPoint(0).state )
4703 const Actor touchedActor(event.GetPoint(0).hitActor);
4705 bool popUpShown( false );
4707 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4712 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4714 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4716 EndMonitoringStageForTouch();
4717 HidePopup( true, false );
4720 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4722 EndMonitoringStageForTouch();
4723 ShowGrabHandleAndSetVisibility( false );
4729 void TextInput::SelectText(std::size_t start, std::size_t end)
4731 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4732 IsGrabHandleEnabled()?"true":"false",
4733 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4734 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4735 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4737 StartMonitoringStageForTouch();
4739 if ( mEditModeActive ) // Only allow text selection when in edit mode
4741 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4742 mSelectingText = true;
4744 std::size_t selectionStartPosition = std::min( start, end );
4746 // Hide grab handle when selecting.
4747 ShowGrabHandleAndSetVisibility( false );
4749 if( start != end ) // something to select
4751 SetCursorVisibility( false );
4752 StopCursorBlinkTimer();
4754 CreateSelectionHandles(start, end);
4757 const TextStyle oldInputStyle( mInputStyle );
4758 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4760 if( oldInputStyle != mInputStyle )
4762 // Updates the line height accordingly with the input style.
4765 EmitStyleChangedSignal();
4771 mSelectingText = false;
4775 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4777 MarkupProcessor::StyledTextArray currentSelectedText;
4779 if ( IsTextSelected() )
4781 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4782 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4784 for(; it != end; ++it)
4786 MarkupProcessor::StyledText& styledText( *it );
4787 currentSelectedText.push_back( styledText );
4790 return currentSelectedText;
4793 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4795 const std::size_t beginIndex = std::min( begin, end );
4796 const std::size_t endIndex = std::max( begin, end );
4799 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4801 // Create a styled text array used to replace the text into the text-view.
4802 MarkupProcessor::StyledTextArray text;
4803 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4805 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4806 GetTextLayoutInfo();
4808 if( IsScrollEnabled() )
4810 // Need to set the scroll position as the text's size may have changed.
4811 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4814 ShowGrabHandleAndSetVisibility( false );
4820 // Set Handle positioning as the new style may have repositioned the characters.
4821 SetSelectionHandlePosition(HandleOne);
4822 SetSelectionHandlePosition(HandleTwo);
4825 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4827 // Just hide the grab handle when keyboard is hidden.
4828 if (!keyboardShown )
4830 ShowGrabHandleAndSetVisibility( false );
4832 // If the keyboard is not now being shown, then hide the popup panel
4833 mPopupPanel.Hide( true );
4837 // Removes highlight and resumes edit mode state
4838 void TextInput::RemoveHighlight( bool hidePopup )
4840 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4842 if ( mHighlightMeshActor )
4844 if ( mSelectionHandleOne )
4846 mActiveLayer.Remove( mSelectionHandleOne );
4847 mSelectionHandleOne.Reset();
4848 mSelectionHandleOneOffset.x = 0.0f;
4850 if ( mSelectionHandleTwo )
4852 mActiveLayer.Remove( mSelectionHandleTwo );
4853 mSelectionHandleTwo.Reset();
4854 mSelectionHandleTwoOffset.x = 0.0f;
4857 mNewHighlightInfo.mQuadList.clear();
4859 Self().Remove( mHighlightMeshActor );
4861 SetCursorVisibility( true );
4862 StartCursorBlinkTimer();
4864 mHighlightMeshActor.Reset();
4865 // NOTE: We cannot dereference mHighlightMesh, due
4866 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4874 mSelectionHandleOnePosition = 0;
4875 mSelectionHandleTwoPosition = 0;
4878 void TextInput::CreateHighlight()
4880 if ( !mHighlightMeshActor )
4882 mMeshData = MeshData( );
4883 mMeshData.SetHasNormals( true );
4885 mCustomMaterial = Material::New("CustomMaterial");
4886 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4888 mMeshData.SetMaterial( mCustomMaterial );
4890 mHighlightMesh = Mesh::New( mMeshData );
4892 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4893 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4894 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4895 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4896 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4898 Self().Add(mHighlightMeshActor);
4903 bool TextInput::CopySelectedTextToClipboard()
4905 mCurrentCopySelecton.clear();
4907 mCurrentCopySelecton = GetSelectedText();
4909 std::string stringToStore;
4911 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4912 * a marked up string.
4914 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4915 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4917 bool success = mClipboard.SetItem( stringToStore );
4921 void TextInput::PasteText( const Text& text )
4923 // Update Flag, indicates whether to update the text-input contents or not.
4924 // Any key stroke that results in a visual change of the text-input should
4925 // set this flag to true.
4926 bool update = false;
4927 if( mHighlightMeshActor )
4929 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4930 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4932 ImfManager imfManager = ImfManager::Get();
4935 imfManager.SetCursorPosition( mCursorPosition );
4936 imfManager.NotifyCursorPosition();
4938 DeleteHighlightedText( true );
4942 bool textExceedsMaximunNumberOfCharacters = false;
4943 bool textExceedsBoundary = false;
4945 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4947 mCursorPosition += insertedStringLength;
4948 ImfManager imfManager = ImfManager::Get();
4951 imfManager.SetCursorPosition ( mCursorPosition );
4952 imfManager.NotifyCursorPosition();
4955 update = update || ( insertedStringLength > 0 );
4962 if( insertedStringLength < text.GetLength() )
4964 EmitMaxInputCharactersReachedSignal();
4967 if( textExceedsBoundary )
4969 EmitInputTextExceedsBoundariesSignal();
4973 void TextInput::SetTextDirection()
4975 // Put the cursor to the right if we are empty and an RTL language is being used.
4976 if ( mStyledText.empty() )
4978 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4980 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4981 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4983 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4984 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4986 int alignment( mDisplayedTextView.GetTextAlignment() &
4987 ( Toolkit::Alignment::VerticalTop |
4988 Toolkit::Alignment::VerticalCenter |
4989 Toolkit::Alignment::VerticalBottom |
4990 Toolkit::Alignment::HorizontalCenter ) );
4991 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4993 // If our alignment is in the center, then do not change.
4994 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4996 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4999 // If our justification is in the center, then do not change.
5000 if ( justification != Toolkit::TextView::Center )
5002 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
5005 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
5006 mDisplayedTextView.SetLineJustification( justification );
5010 void TextInput::UpdateLineHeight()
5012 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
5013 mLineHeight = font.GetLineHeight();
5015 // 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.
5017 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5019 if( !mExceedEnabled || shrink )
5021 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5025 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5027 // VCC check if we need do this in the visual order ...
5028 std::size_t position = 0u;
5030 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5036 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5038 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5040 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5046 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5047 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5049 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5055 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5060 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5067 void TextInput::SetSortModifier( float depthOffset )
5069 if(mDisplayedTextView)
5071 mDisplayedTextView.SetSortModifier(depthOffset);
5075 void TextInput::SetSnapshotModeEnabled( bool enable )
5077 if(mDisplayedTextView)
5079 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5083 bool TextInput::IsSnapshotModeEnabled() const
5085 bool snapshotEnabled = false;
5087 if(mDisplayedTextView)
5089 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5092 return snapshotEnabled;
5095 void TextInput::SetMarkupProcessingEnabled( bool enable )
5097 mMarkUpEnabled = enable;
5100 bool TextInput::IsMarkupProcessingEnabled() const
5102 return mMarkUpEnabled;
5105 void TextInput::SetScrollEnabled( bool enable )
5107 if( mDisplayedTextView )
5109 mDisplayedTextView.SetScrollEnabled( enable );
5114 // Don't set cursor's and handle's visibility to false if they are outside the
5115 // boundaries of the text-input.
5116 mIsCursorInScrollArea = true;
5117 mIsGrabHandleInScrollArea = true;
5118 if( mSelectionHandleOne && mSelectionHandleTwo )
5120 mSelectionHandleOne.SetVisible( true );
5121 mSelectionHandleTwo.SetVisible( true );
5123 if( mHighlightMeshActor )
5125 mHighlightMeshActor.SetVisible( true );
5131 bool TextInput::IsScrollEnabled() const
5133 bool scrollEnabled = false;
5135 if( mDisplayedTextView )
5137 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5140 return scrollEnabled;
5143 void TextInput::SetScrollPosition( const Vector2& position )
5145 if( mDisplayedTextView )
5147 mDisplayedTextView.SetScrollPosition( position );
5151 Vector2 TextInput::GetScrollPosition() const
5153 Vector2 scrollPosition;
5155 if( mDisplayedTextView )
5157 scrollPosition = mDisplayedTextView.GetScrollPosition();
5160 return scrollPosition;
5163 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5165 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5166 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5167 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5169 // Add style to the new input text.
5170 MarkupProcessor::StyledTextArray textToInsert;
5171 for( std::size_t i = 0; i < insertedStringLength; ++i )
5173 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5174 textToInsert.push_back( newStyledCharacter );
5177 //Insert text to the TextView.
5178 const bool emptyTextView = mStyledText.empty();
5179 if( emptyTextView && mPlaceHolderSet )
5181 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5182 mDisplayedTextView.SetText( textToInsert );
5186 if( 0 == numberOfCharactersToReplace )
5188 mDisplayedTextView.InsertTextAt( position, textToInsert );
5192 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5195 mPlaceHolderSet = false;
5197 if( textToInsert.empty() )
5199 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5200 GetTextLayoutInfo();
5204 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5205 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5208 textExceedsBoundary = false;
5210 if( !mExceedEnabled )
5212 const Vector3& size = GetControlSize();
5214 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5216 // If new text does not fit within TextView
5217 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5218 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5219 GetTextLayoutInfo();
5220 textExceedsBoundary = true;
5221 insertedStringLength = 0;
5224 if( textExceedsBoundary )
5226 // Add the part of the text which fits on the text-input.
5228 // Split the text which doesn't fit in two halves.
5229 MarkupProcessor::StyledTextArray firstHalf;
5230 MarkupProcessor::StyledTextArray secondHalf;
5231 SplitText( textToInsert, firstHalf, secondHalf );
5233 // Clear text. This text will be filled with the text inserted.
5234 textToInsert.clear();
5236 // Where to insert the text.
5237 std::size_t positionToInsert = position;
5239 bool end = text.GetLength() <= 1;
5242 // Insert text and check ...
5243 const std::size_t textLength = firstHalf.size();
5244 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5245 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5247 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5249 // Inserted text doesn't fit.
5251 // Remove inserted text
5252 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5253 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5255 // The iteration finishes when only one character doesn't fit.
5256 end = textLength <= 1;
5260 // Prepare next two halves for next iteration.
5261 MarkupProcessor::StyledTextArray copyText = firstHalf;
5262 SplitText( copyText, firstHalf, secondHalf );
5269 // store text to be inserted in mStyledText.
5270 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5272 // Increase the inserted characters counter.
5273 insertedStringLength += textLength;
5275 // Prepare next two halves for next iteration.
5276 MarkupProcessor::StyledTextArray copyText = secondHalf;
5277 SplitText( copyText, firstHalf, secondHalf );
5279 // Update where next text has to be inserted
5280 positionToInsert += textLength;
5286 if( textToInsert.empty() && emptyTextView )
5288 // No character has been added and the text-view was empty.
5289 // Show the placeholder text.
5290 ShowPlaceholderText( mStyledPlaceHolderText );
5294 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5295 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5296 mPlaceHolderSet = false;
5299 return insertedStringLength;
5302 void TextInput::GetTextLayoutInfo()
5304 if( mStyledText.empty() )
5306 // The text-input has no text, clear the text-view's layout info.
5307 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5311 if( mDisplayedTextView )
5313 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5317 // There is no text-view.
5318 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5323 void TextInput::SetOffsetFromText( const Vector4& offset )
5325 mPopupOffsetFromText = offset;
5328 const Vector4& TextInput::GetOffsetFromText() const
5330 return mPopupOffsetFromText;
5333 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5335 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5339 TextInput& textInputImpl( GetImpl( textInput ) );
5341 switch ( propertyIndex )
5343 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5345 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5348 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5350 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5353 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5355 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5358 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5360 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5363 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5365 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5368 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5370 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5373 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5375 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5378 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5380 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5383 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5385 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5388 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5390 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5393 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5395 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5398 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5400 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5403 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5405 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5408 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5410 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5413 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5415 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5418 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5420 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5426 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5428 Property::Value value;
5430 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5434 TextInput& textInputImpl( GetImpl( textInput ) );
5436 switch ( propertyIndex )
5438 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5440 value = textInputImpl.GetMaterialDiffuseColor();
5443 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5445 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5448 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5450 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5453 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5455 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5458 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5460 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5463 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5465 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5468 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5470 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5473 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5475 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5478 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5480 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5483 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5485 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5488 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5490 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5493 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5495 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5498 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5500 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5503 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5505 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5508 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5510 value = textInputImpl.GetOffsetFromText();
5513 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5515 value = textInputImpl.mCursor.GetCurrentColor();
5522 void TextInput::EmitStyleChangedSignal()
5524 // emit signal if input style changes.
5525 Toolkit::TextInput handle( GetOwner() );
5526 mStyleChangedSignal.Emit( handle, mInputStyle );
5529 void TextInput::EmitTextModified()
5531 // emit signal when text changes.
5532 Toolkit::TextInput handle( GetOwner() );
5533 mTextModifiedSignal.Emit( handle );
5537 void TextInput::EmitMaxInputCharactersReachedSignal()
5539 // emit signal if max characters is reached during text input.
5540 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5542 Toolkit::TextInput handle( GetOwner() );
5543 mMaxInputCharactersReachedSignal.Emit( handle );
5546 void TextInput::EmitInputTextExceedsBoundariesSignal()
5548 // Emit a signal when the input text exceeds the boundaries of the text input.
5550 Toolkit::TextInput handle( GetOwner() );
5551 mInputTextExceedBoundariesSignal.Emit( handle );
5554 } // namespace Internal
5556 } // namespace Toolkit