2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/dali.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
25 #include <dali/integration-api/debug.h>
32 #define GET_LOCALE_TEXT(string) dgettext("sys_string", string)
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
45 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
46 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
47 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
48 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
49 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
50 const Vector4 LIGHTBLUE( 10.0f/255.0f, 140.0f/255.0f, 210.0f/255.0f, 1.0f ); // Used for Selection highlight
52 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
55 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
56 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
57 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
59 const char* DEFAULT_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" );
60 const char* DEFAULT_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" );
61 const char* DEFAULT_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" );
62 const char* DEFAULT_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" );
63 const char* DEFAULT_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" );
64 const char* DEFAULT_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" );
66 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
68 const std::string OPTION_SELECT_WORD("select_word"); ///< "Select Word" popup option.
69 const std::string OPTION_SELECT_ALL("select_all"); ///< "Select All" popup option.
70 const std::string OPTION_CUT("cut"); ///< "Cut" popup option.
71 const std::string OPTION_COPY("copy"); ///< "Copy" popup option.
72 const std::string OPTION_PASTE("paste"); ///< "Paste" popup option.
73 const std::string OPTION_CLIPBOARD("clipboard"); ///< "Clipboard" popup option.
75 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
76 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
77 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.0f ); ///< 1. Highlight rendered (z-offset).
78 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.1f ); ///< 2. Text rendered (z-offset).
79 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
81 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
82 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
83 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
84 const float TOP_HANDLE_TOP_OFFSET(-1.5f); ///< Offset between top handle and cutCopyPaste pop-up
85 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); ///< Offset between bottom handle and cutCopyPaste pop-up
86 const float CURSOR_THICKNESS(6.0f);
87 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
89 const std::string NEWLINE( "\n" );
91 const TextStyle DEFAULT_TEXT_STYLE;
93 const unsigned int SCROLL_TICK_INTERVAL = 50u;
94 const float SCROLL_THRESHOLD = 10.f;
95 const float SCROLL_SPEED = 15.f;
98 * Whether the given style is the default style or not.
99 * @param[in] style The given style.
100 * @return \e true if the given style is the default. Otherwise it returns \e false.
102 bool IsDefaultStyle( const TextStyle& style )
104 return DEFAULT_TEXT_STYLE == style;
108 * Whether the given styled text is using the default style or not.
109 * @param[in] textArray The given text.
110 * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
112 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
114 for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
116 const TextStyle& style( (*it).mStyle );
118 if( !IsDefaultStyle( style ) )
128 * Selection state enumeration (FSM)
132 SelectionNone, ///< Currently not encountered selected section.
133 SelectionStarted, ///< Encountered selected section
134 SelectionFinished ///< Finished selected section
137 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
139 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
143 if( ( *it ).mIsVisible )
145 return --cursorPosition;
154 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
156 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
158 if( ( *it ).mIsVisible )
160 return cursorPosition;
166 return cursorPosition;
170 * Whether the given position plus the cursor size offset is inside the given boundary.
172 * @param[in] position The given position.
173 * @param[in] cursorSize The cursor size.
174 * @param[in] controlSize The given boundary.
176 * @return whether the given position is inside the given boundary.
178 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
180 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
181 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
182 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
183 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
187 * Splits a text in two halves.
189 * If the text's number of characters is odd, firstHalf has one more character.
191 * @param[in] text The text to be split.
192 * @param[out] firstHalf The first half of the text.
193 * @param[out] secondHalf The second half of the text.
195 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
196 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
197 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
202 const std::size_t textLength = text.size();
203 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
205 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
206 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
209 } // end of namespace
225 return Toolkit::TextInput::New();
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
239 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
241 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
243 QuadCoordinates quad(x1, y1, x2, y2);
244 mQuadList.push_back( quad );
247 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
249 for(std::size_t i = 0;i < mQuadList.size(); i++)
251 QuadCoordinates& quad = mQuadList[i];
253 quad.min.Clamp(min, max);
254 quad.max.Clamp(min, max);
258 // [TextInput] ////////////////////////////////////////////////////////////////
260 Dali::Toolkit::TextInput TextInput::New()
262 // Create the implementation
263 TextInputPtr textInput(new TextInput());
264 // Pass ownership to CustomActor via derived handle
265 Dali::Toolkit::TextInput handle(*textInput);
267 textInput->Initialize();
272 TextInput::TextInput()
273 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
278 mDisplayedTextView(),
279 mStyledPlaceHolderText(),
280 mMaxStringLength( DEFAULT_MAX_SIZE ),
281 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
282 mCursorPosition( 0 ),
283 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
284 mIsSelectionHandleOneFlipped( false ),
285 mIsSelectionHandleTwoFlipped( false ),
286 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
287 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
288 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
289 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
290 mSelectionHandleOnePosition( 0 ),
291 mSelectionHandleTwoPosition( 0 ),
293 mPreEditStartPosition( 0 ),
294 mPreEditLength ( 0 ),
295 mNumberOfSurroundingCharactersDeleted( 0 ),
296 mTouchStartTime( 0 ),
298 mCurrentCopySelecton(),
300 mScrollDisplacement(),
301 mCurrentHandlePosition(),
302 mCurrentSelectionId(),
303 mCurrentSelectionHandlePosition(),
304 mRequestedSelection( 0, 0 ),
305 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
306 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
308 mOverrideAutomaticAlignment( false ),
309 mCursorRTLEnabled( false ),
310 mClosestCursorPositionEOL ( false ),
311 mCursorBlinkStatus( true ),
312 mCursorVisibility( false ),
313 mGrabHandleVisibility( false ),
314 mIsCursorInScrollArea( true ),
315 mIsGrabHandleInScrollArea( true ),
316 mEditModeActive( false ),
317 mEditOnTouch( true ),
318 mTextSelection( true ),
319 mExceedEnabled( true ),
320 mGrabHandleEnabled( true ),
321 mIsSelectionHandleFlipEnabled( true ),
322 mPreEditFlag( false ),
323 mIgnoreCommitFlag( false ),
324 mIgnoreFirstCommitFlag( false ),
325 mSelectingText( false ),
326 mPreserveCursorPosition( false ),
327 mSelectTextOnCommit( false ),
328 mUnderlinedPriorToPreEdit ( false ),
329 mCommitByKeyInput( false ),
330 mPlaceHolderSet( false ),
331 mMarkUpEnabled( false )
333 // Updates the line height accordingly with the input style.
337 TextInput::~TextInput()
339 StopCursorBlinkTimer();
344 std::string TextInput::GetText() const
348 // Return text-view's text only if the text-input's text is not empty
349 // in order to not to return the placeholder text.
350 if( !mStyledText.empty() )
352 text = mDisplayedTextView.GetText();
358 std::string TextInput::GetMarkupText() const
360 std::string markupString;
361 MarkupProcessor::GetMarkupString( mStyledText, markupString );
366 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
368 // Get the placeholder styled text array from the markup string.
369 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
371 if( mStyledText.empty() )
373 // Set the placeholder text only if the styled text is empty.
374 mDisplayedTextView.SetText( mStyledPlaceHolderText );
375 mPlaceHolderSet = true;
379 std::string TextInput::GetPlaceholderText()
381 // Traverses the styled placeholder array getting only the text.
382 // Note that for some languages a 'character' could be represented by more than one 'char'
384 std::string placeholderText;
385 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
387 placeholderText.append( (*it).mText.GetText() );
390 return placeholderText ;
393 void TextInput::SetInitialText(const std::string& initialText)
395 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
397 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
399 mPreEditFlag = false;
400 mIgnoreCommitFlag = true;
403 SetText( initialText );
404 PreEditReset( false ); // Reset keyboard as text changed
407 void TextInput::SetText(const std::string& initialText)
409 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
411 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
413 if( mStyledText.empty() )
415 // If the initial text is empty, set the placeholder text.
416 mDisplayedTextView.SetText( mStyledPlaceHolderText );
417 mPlaceHolderSet = true;
421 mDisplayedTextView.SetText( mStyledText );
422 mPlaceHolderSet = false;
427 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
429 ImfManager imfManager = ImfManager::Get();
432 imfManager.SetCursorPosition( mCursorPosition );
433 imfManager.SetSurroundingText( initialText );
434 imfManager.NotifyCursorPosition();
437 if( IsScrollEnabled() )
439 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
442 ShowGrabHandleAndSetVisibility( false );
451 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
453 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
455 mDisplayedTextView.SetText( styleText );
456 mPlaceHolderSet = false;
458 // If text alignment hasn't been manually set by application developer, then we
459 // automatically determine the alignment based on the content of the text i.e. what
460 // language the text begins with.
461 // TODO: This should determine different alignments for each line (broken by '\n') of text.
462 if(!mOverrideAutomaticAlignment)
464 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
465 bool leftToRight(true);
467 if( !styleText.empty() )
469 bool breakOut(false);
471 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
473 const Text& text = textIter->mText;
475 for( std::size_t i = 0; i < text.GetLength(); ++i )
477 Character character( text[i] );
478 if( character.GetCharacterDirection() != Character::Neutral )
480 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
488 // Based on this direction, either left or right align text if not manually set by application developer.
489 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
490 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
491 Toolkit::Alignment::VerticalTop ) );
492 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
498 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
500 mMaxStringLength = maxChars;
503 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
505 DALI_ASSERT_DEBUG( maxLines > 0 )
509 mNumberOflinesLimit = maxLines;
513 std::size_t TextInput::GetNumberOfLinesLimit() const
515 return mNumberOflinesLimit;
518 std::size_t TextInput::GetNumberOfCharacters() const
520 return mStyledText.size();
523 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
525 return mInputStartedSignalV2;
528 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
530 return mInputFinishedSignalV2;
533 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
535 return mCutAndPasteToolBarDisplayedV2;
538 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
540 return mStyleChangedSignalV2;
543 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
545 return mTextModifiedSignal;
548 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
550 return mMaxInputCharactersReachedSignalV2;
553 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
555 return mInputTextExceedBoundariesSignalV2;
558 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
560 Dali::BaseHandle handle( object );
562 bool connected( true );
563 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
565 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
567 textInput.InputStartedSignal().Connect( tracker, functor );
569 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
571 textInput.InputFinishedSignal().Connect( tracker, functor );
573 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
575 textInput.StyleChangedSignal().Connect( tracker, functor );
577 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
579 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
581 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
583 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
587 // signalName does not match any signal
594 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
598 // update line height before calculate the actual position.
603 if( setCursorOnTouchPoint )
605 // Sets the cursor position for the given touch point.
606 ReturnClosestIndex( touchPoint, mCursorPosition );
608 // Creates the grab handle.
609 if( IsGrabHandleEnabled() )
611 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
615 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
616 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
617 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
618 ShowGrabHandleAndSetVisibility( true );
620 // Scrolls the text-view if needed.
621 if( IsScrollEnabled() )
623 ScrollTextViewToMakeCursorVisible( cursorPosition );
629 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
641 bool TextInput::IsEditable() const
643 return mEditModeActive;
646 void TextInput::SetEditOnTouch( bool editOnTouch )
648 mEditOnTouch = editOnTouch;
651 bool TextInput::IsEditOnTouch() const
656 void TextInput::SetTextSelectable( bool textSelectable )
658 mTextSelection = textSelectable;
661 bool TextInput::IsTextSelectable() const
663 return mTextSelection;
666 bool TextInput::IsTextSelected() const
668 return mHighlightMeshActor;
671 void TextInput::DeSelectText()
678 void TextInput::SetGrabHandleImage(Dali::Image image )
682 CreateGrabHandle(image);
686 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
688 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
692 mCursor.SetImage( image );
693 mCursor.SetNinePatchBorder( border );
697 Vector3 TextInput::GetSelectionHandleSize()
699 return DEFAULT_SELECTION_HANDLE_SIZE;
702 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
704 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
708 mCursorRTL.SetImage( image);
709 mCursorRTL.SetNinePatchBorder( border );
713 void TextInput::EnableGrabHandle(bool toggle)
715 // enables grab handle with will in turn de-activate magnifier
716 mGrabHandleEnabled = toggle;
719 bool TextInput::IsGrabHandleEnabled()
721 // if false then magnifier will be shown instead.
722 return mGrabHandleEnabled;
725 void TextInput::EnableSelectionHandleFlip( bool toggle )
727 // Deprecated function. To be removed.
728 mIsSelectionHandleFlipEnabled = toggle;
731 bool TextInput::IsSelectionHandleFlipEnabled()
733 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
737 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
739 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
740 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
741 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
743 mSelectionHandleFlipMargin = margin;
746 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
748 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
749 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
751 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
752 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
754 const Vector4 boundary( originX,
756 originX + boundingRectangle.width,
757 originY + boundingRectangle.height );
759 mBoundingRectangleWorldCoordinates = boundary;
762 const Rect<float> TextInput::GetBoundingRectangle() const
764 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
766 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
767 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
769 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
774 const Vector4& TextInput::GetSelectionHandleFlipMargin()
776 return mSelectionHandleFlipMargin;
779 void TextInput::SetTextColor( const Vector4& color )
781 mDisplayedTextView.SetColor( color );
784 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
786 if( style != mInputStyle )
789 bool emitSignal = false;
791 // mask: modify style according to mask, if different emit signal.
792 const TextStyle oldInputStyle( mInputStyle );
794 // Copy the new style.
795 mInputStyle.Copy( style, mask );
797 // if style has changed, emit signal.
798 if( oldInputStyle != mInputStyle )
803 // Updates the line height accordingly with the input style.
806 // Changing font point size will require the cursor to be re-sized
811 EmitStyleChangedSignal();
816 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
818 if ( IsTextSelected() )
820 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
821 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
823 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
825 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
828 // Keeps the old style to be compared with the new one.
829 const TextStyle oldInputStyle( mInputStyle );
831 // Copy only those parameters from the style which are set in the mask.
832 mInputStyle.Copy( style, mask );
834 if( mInputStyle != oldInputStyle )
836 // Updates the line height accordingly with the input style.
839 EmitStyleChangedSignal();
844 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
846 if( !mStyledText.empty() )
848 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
852 TextStyle TextInput::GetStyleAtCursor() const
856 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
858 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
859 style = mStyledText.at( mCursorPosition-1 ).mStyle;
865 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
867 Dali::Font defaultFont = Dali::Font::New();
868 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
875 TextStyle TextInput::GetStyleAt( std::size_t position ) const
877 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
879 if( position >= mStyledText.size() )
881 position = mStyledText.size() - 1;
884 return mStyledText.at( position ).mStyle;
887 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
889 mDisplayedTextView.SetTextAlignment( align );
890 mOverrideAutomaticAlignment = true;
893 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
895 mDisplayedTextView.SetLineJustification( justification );
896 mOverrideAutomaticAlignment = true;
899 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
901 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
904 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
906 return mDisplayedTextView.GetFadeBoundary();
909 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
911 return mDisplayedTextView.GetTextAlignment();
914 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
916 mDisplayedTextView.SetMultilinePolicy( policy );
919 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
921 return mDisplayedTextView.GetMultilinePolicy();
924 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
926 mDisplayedTextView.SetWidthExceedPolicy( policy );
929 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
931 return mDisplayedTextView.GetWidthExceedPolicy();
934 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
936 mDisplayedTextView.SetHeightExceedPolicy( policy );
939 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
941 return mDisplayedTextView.GetHeightExceedPolicy();
944 void TextInput::SetExceedEnabled( bool enable )
946 mExceedEnabled = enable;
949 bool TextInput::GetExceedEnabled() const
951 return mExceedEnabled;
954 void TextInput::SetBackground(Dali::Image image )
956 // TODO Should add this function and add public api to match.
959 bool TextInput::OnTouchEvent(const TouchEvent& event)
964 bool TextInput::OnKeyEvent(const KeyEvent& event)
966 switch( event.state )
970 return OnKeyDownEvent(event);
976 return OnKeyUpEvent(event);
988 void TextInput::OnKeyInputFocusGained()
990 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
992 mEditModeActive = true;
994 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
996 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
998 // Updates the line height accordingly with the input style.
1001 // Connect the signals to use in text input.
1002 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1003 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1005 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1008 GetTextLayoutInfo();
1011 SetCursorVisibility( true );
1012 StartCursorBlinkTimer();
1014 Toolkit::TextInput handle( GetOwner() );
1015 mInputStartedSignalV2.Emit( handle );
1017 ImfManager imfManager = ImfManager::Get();
1021 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1023 // Notify that the text editing start.
1024 imfManager.Activate();
1026 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1027 imfManager.SetRestoreAferFocusLost( true );
1029 imfManager.SetCursorPosition( mCursorPosition );
1030 imfManager.NotifyCursorPosition();
1033 mClipboard = Clipboard::Get(); // Store handle to clipboard
1035 // Now in edit mode we can accept string to paste from clipboard
1036 if( Adaptor::IsAvailable() )
1038 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1041 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1046 void TextInput::OnKeyInputFocusLost()
1048 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1052 // If key input focus is lost, it removes the
1053 // underline from the last pre-edit text.
1054 RemovePreEditStyle();
1055 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1056 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1060 ImfManager imfManager = ImfManager::Get();
1063 // The text editing is finished. Therefore the imf manager don't have restore activation.
1064 imfManager.SetRestoreAferFocusLost( false );
1066 // Notify that the text editing finish.
1067 imfManager.Deactivate();
1069 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1071 // Disconnect signal used the text input.
1072 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1074 Toolkit::TextInput handle( GetOwner() );
1075 mInputFinishedSignalV2.Emit( handle );
1076 mEditModeActive = false;
1077 mPreEditFlag = false;
1079 SetCursorVisibility( false );
1080 StopCursorBlinkTimer();
1082 ShowGrabHandleAndSetVisibility( false );
1085 // No longer in edit mode so do not want to receive string from clipboard
1086 if( Adaptor::IsAvailable() )
1088 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1091 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1093 Clipboard clipboard = Clipboard::Get();
1097 clipboard.HideClipboard();
1102 void TextInput::OnControlStageConnection()
1104 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1106 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1108 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1112 void TextInput::CreateActiveLayer()
1114 Actor self = Self();
1115 mActiveLayer = Layer::New();
1117 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1118 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1119 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1121 self.Add( mActiveLayer );
1122 mActiveLayer.RaiseToTop();
1125 void TextInput::OnInitialize()
1127 CreateTextViewActor();
1131 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1132 // different positions depending on language)
1133 Image mCursorImage = Image::New( DEFAULT_CURSOR );
1134 mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1135 mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1137 Actor self = Self();
1138 self.Add( mCursor );
1139 self.Add( mCursorRTL );
1141 mCursorVisibility = false;
1143 CreateActiveLayer(); // todo move this so layer only created when needed.
1145 // Assign names to image actors
1146 mCursor.SetName("mainCursor");
1147 mCursorRTL.SetName("rtlCursor");
1150 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1152 mDisplayedTextView.SetSize( targetSize );
1153 GetTextLayoutInfo();
1154 mActiveLayer.SetSize(targetSize);
1157 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1159 Relayout( mDisplayedTextView, size, container );
1160 GetTextLayoutInfo();
1165 Vector3 TextInput::GetNaturalSize()
1167 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1169 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1171 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1172 naturalSize.height = mLineHeight;
1178 float TextInput::GetHeightForWidth( float width )
1180 float height = mDisplayedTextView.GetHeightForWidth( width );
1182 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1184 // If the height is zero, it means there is no text. Let's return the cursor height.
1185 height = mLineHeight;
1191 /*end of Virtual methods from parent*/
1193 // Private Internal methods
1195 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1197 switch (gesture.state)
1199 case Gesture::Started:
1200 // fall through so code not duplicated
1201 case Gesture::Continuing:
1203 if (actor == mGrabArea)
1205 SetCursorVisibility( true );
1206 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1207 MoveGrabHandle( gesture.displacement );
1208 HidePopup(); // Do not show popup whilst handle is moving
1210 else if (actor == mHandleOneGrabArea)
1212 // the displacement in PanGesture is affected by the actor's rotation.
1213 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1214 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1216 MoveSelectionHandle( HandleOne, gesture.displacement );
1218 mState = StateDraggingHandle;
1221 else if (actor == mHandleTwoGrabArea)
1223 // the displacement in PanGesture is affected by the actor's rotation.
1224 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1225 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1227 MoveSelectionHandle( HandleTwo, gesture.displacement );
1229 mState = StateDraggingHandle;
1235 case Gesture::Finished:
1237 // Revert back to non-pressed selection handle images
1238 if (actor == mGrabArea)
1240 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1241 SetCursorVisibility( true );
1242 SetUpPopUpSelection();
1245 if (actor == mHandleOneGrabArea)
1247 // the displacement in PanGesture is affected by the actor's rotation.
1248 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1249 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1251 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1253 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1255 ShowPopupCutCopyPaste();
1257 if (actor == mHandleTwoGrabArea)
1259 // the displacement in PanGesture is affected by the actor's rotation.
1260 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1261 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1263 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1265 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1267 ShowPopupCutCopyPaste();
1276 // Stop the flashing animation so easy to see when moved.
1277 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1279 if (touch.GetPoint(0).state == TouchPoint::Down)
1281 SetCursorVisibility( true );
1282 StopCursorBlinkTimer();
1284 else if (touch.GetPoint(0).state == TouchPoint::Up)
1286 SetCursorVisibility( true );
1287 StartCursorBlinkTimer();
1292 // selection handle one
1293 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1295 if (touch.GetPoint(0).state == TouchPoint::Down)
1297 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1299 else if (touch.GetPoint(0).state == TouchPoint::Up)
1301 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1306 // selection handle two
1307 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1309 if (touch.GetPoint(0).state == TouchPoint::Down)
1311 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1313 else if (touch.GetPoint(0).state == TouchPoint::Up)
1315 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1320 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1322 // If text exists then select nearest word.
1323 if ( !mStyledText.empty())
1327 ShowGrabHandleAndSetVisibility( false );
1332 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1333 // converts the pre-edit word being displayed to a committed word.
1334 if ( !mUnderlinedPriorToPreEdit )
1337 style.SetUnderline( false );
1338 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1340 mPreEditFlag = false;
1341 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1342 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1343 PreEditReset( false );
1345 mCursorPosition = 0;
1347 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1348 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1350 ImfManager imfManager = ImfManager::Get();
1353 imfManager.SetCursorPosition ( mCursorPosition );
1354 imfManager.NotifyCursorPosition();
1357 std::size_t start = 0;
1358 std::size_t end = 0;
1359 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1361 SelectText( start, end );
1363 // if no text but clipboard has content then show paste option
1364 if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1366 ShowPopupCutCopyPaste();
1369 // If no text and clipboard empty then do nothing
1372 // TODO: Change the function name to be more general.
1373 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1375 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1376 , (mEditOnTouch)?"true":"false"
1377 , (mEditModeActive)?"true":"false");
1379 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1384 if( mGrabArea == actor )
1386 if( mPopUpPanel.GetState() == TextInputPopup::StateHidden || mPopUpPanel.GetState() == TextInputPopup::StateHiding )
1388 SetUpPopUpSelection();
1398 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1400 // Initially don't create the grab handle.
1401 bool createGrabHandle = false;
1403 if ( !mEditModeActive )
1405 // update line height before calculate the actual position.
1408 // Only start edit mode if TextInput configured to edit on touch
1411 // Set the initial cursor position in the tap point.
1412 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1414 // Create the grab handle.
1415 // TODO Make this a re-usable function.
1416 if ( IsGrabHandleEnabled() )
1418 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1422 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1423 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1424 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1425 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1429 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1430 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1431 // otherwise the Grab handle will be shown when selecting.
1438 // Show the keyboard if it was hidden.
1439 if (!VirtualKeyboard::IsVisible())
1441 VirtualKeyboard::Show();
1444 // Reset keyboard as tap event has occurred.
1445 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1446 PreEditReset( true );
1448 GetTextLayoutInfo();
1450 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1452 // 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.
1454 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1456 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1458 // Notify keyboard so it can 're-capture' word for predictive text.
1459 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1460 ImfManager imfManager = ImfManager::Get();
1463 imfManager.SetCursorPosition ( mCursorPosition );
1464 imfManager.NotifyCursorPosition();
1466 const TextStyle oldInputStyle( mInputStyle );
1468 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1472 // Create the grab handle.
1473 // Grab handle is created later.
1474 createGrabHandle = true;
1476 if( oldInputStyle != mInputStyle )
1478 // Updates the line height accordingly with the input style.
1481 EmitStyleChangedSignal();
1486 if ( createGrabHandle && IsGrabHandleEnabled() )
1488 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1492 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1493 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1494 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1495 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1500 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1502 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1504 if(longPress.state == Dali::Gesture::Started)
1506 // Start edit mode on long press
1507 if ( !mEditModeActive )
1512 // If text exists then select nearest word.
1513 if ( !mStyledText.empty())
1517 ShowGrabHandleAndSetVisibility( false );
1522 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1523 // converts the pre-edit word being displayed to a committed word.
1524 if ( !mUnderlinedPriorToPreEdit )
1527 style.SetUnderline( false );
1528 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1530 mPreEditFlag = false;
1531 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1532 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1533 PreEditReset( false );
1535 mCursorPosition = 0;
1537 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1538 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1540 ImfManager imfManager = ImfManager::Get();
1543 imfManager.SetCursorPosition ( mCursorPosition );
1544 imfManager.NotifyCursorPosition();
1546 std::size_t start = 0;
1547 std::size_t end = 0;
1548 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1550 SelectText( start, end );
1553 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1554 if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1556 ShowPopupCutCopyPaste();
1561 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1563 const Text clipboardText( notifier.GetContent() );
1564 PasteText( clipboardText );
1566 SetCursorVisibility( true );
1567 StartCursorBlinkTimer();
1569 ShowGrabHandleAndSetVisibility( false );
1575 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1577 mPopUpPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1579 const std::string& name = button.GetName();
1581 if(name == OPTION_SELECT_WORD)
1583 std::size_t start = 0;
1584 std::size_t end = 0;
1585 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1587 SelectText( start, end );
1589 else if(name == OPTION_SELECT_ALL)
1591 SetCursorVisibility(false);
1592 StopCursorBlinkTimer();
1594 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1595 std::size_t start = 0;
1597 SelectText( start, end );
1599 else if(name == OPTION_CUT)
1601 bool ret = CopySelectedTextToClipboard();
1605 DeleteHighlightedText( true );
1609 SetCursorVisibility( true );
1610 StartCursorBlinkTimer();
1614 else if(name == OPTION_COPY)
1616 CopySelectedTextToClipboard();
1620 SetCursorVisibility( true );
1621 StartCursorBlinkTimer();
1625 else if(name == OPTION_PASTE)
1627 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1629 PasteText(retrievedString);
1631 SetCursorVisibility( true );
1632 StartCursorBlinkTimer();
1634 ShowGrabHandleAndSetVisibility( false );
1638 else if(name == OPTION_CLIPBOARD)
1640 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1641 // Hence pass the false parameter for signalFinished.
1642 HidePopup( true, false );
1643 mClipboard.ShowClipboard();
1649 bool TextInput::OnCursorBlinkTimerTick()
1652 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1653 if ( mCursorRTLEnabled )
1655 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1657 mCursorBlinkStatus = !mCursorBlinkStatus;
1662 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1664 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1666 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1667 if(mHighlightMeshActor && mState == StateEdit)
1669 ShowPopupCutCopyPaste();
1673 //FIXME this routine needs to be re-written as it contains too many branches.
1674 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1676 std::string keyName = event.keyPressedName;
1677 std::string keyString = event.keyPressed;
1679 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1681 // Do not consume "Tab" and "Escape" keys.
1682 if(keyName == "Tab" || keyName == "Escape")
1684 // Escape key to end the edit mode
1690 HidePopup(); // If Pop-up shown then hides it as editing text.
1692 // Update Flag, indicates whether to update the text-input contents or not.
1693 // Any key stroke that results in a visual change of the text-input should
1694 // set this flag to true.
1697 // Whether to scroll text to cursor position.
1698 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1699 bool scroll = false;
1701 if (keyName == "Return")
1703 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1705 bool preEditFlagPreviouslySet( mPreEditFlag );
1707 if (mHighlightMeshActor)
1709 // replaces highlighted text with new line
1710 DeleteHighlightedText( false );
1712 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1714 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1715 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1718 mCommitByKeyInput = true;
1721 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1722 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1724 mPreEditFlag = true;
1725 mIgnoreCommitFlag = false;
1735 else if ( keyName == "space" )
1737 if ( mHighlightMeshActor )
1739 // Some text is selected so erase it before adding space.
1740 DeleteHighlightedText( true );
1744 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1746 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1747 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1750 mCommitByKeyInput = true;
1755 else if (keyName == "BackSpace")
1757 if ( mHighlightMeshActor )
1759 // Some text is selected so erase it
1760 DeleteHighlightedText( true );
1765 if ( mCursorPosition > 0 )
1767 DeleteCharacter( mCursorPosition );
1773 else if (keyName == "Right")
1778 else if (keyName == "Left")
1780 AdvanceCursor(true);
1783 else // event is a character
1785 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1786 if ( !keyString.empty() )
1788 if ( mHighlightMeshActor )
1790 // replaces highlighted text with new character
1791 DeleteHighlightedText( false );
1795 // Received key String
1796 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1802 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1803 // as this is a costly operation.
1809 if(update || scroll)
1811 if( IsScrollEnabled() )
1813 // Calculates the new cursor position (in actor coordinates)
1814 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1816 ScrollTextViewToMakeCursorVisible( cursorPosition );
1823 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1825 std::string keyName = event.keyPressedName;
1826 std::string keyString = event.keyPressed;
1828 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1830 // The selected text become deselected when the key code is DALI_KEY_BACK.
1831 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1840 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1842 // Updates the stored scroll position.
1843 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1845 const Vector3& controlSize = GetControlSize();
1846 Size cursorSize( CURSOR_THICKNESS, 0.f );
1848 // Updates the cursor and grab handle position and visibility.
1849 if( mGrabHandle || mCursor )
1851 cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1852 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1854 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1856 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1860 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1861 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1866 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1867 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1871 // Updates the selection handles and highlighted text position and visibility.
1872 if( mSelectionHandleOne && mSelectionHandleTwo )
1874 const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1875 const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1876 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1877 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1878 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1879 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1881 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1882 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1884 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1885 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1886 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1887 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1889 if( mHighlightMeshActor )
1891 mHighlightMeshActor.SetVisible( true );
1897 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1899 // Scroll the text to make the cursor visible.
1900 const Size cursorSize( CURSOR_THICKNESS,
1901 GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1903 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1905 const Vector3& controlSize = GetControlSize();
1907 // Calculates the new scroll position.
1908 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1909 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1911 scrollOffset.x += cursorPosition.x;
1914 if( cursorPosition.y - cursorSize.height < 0.f )
1916 scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1918 else if( cursorPosition.y > controlSize.height )
1920 scrollOffset.y += cursorPosition.y;
1923 // Sets the new scroll position.
1924 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1925 SetScrollPosition( scrollOffset );
1928 void TextInput::StartScrollTimer()
1932 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1933 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1936 if( !mScrollTimer.IsRunning() )
1938 mScrollTimer.Start();
1942 void TextInput::StopScrollTimer()
1946 mScrollTimer.Stop();
1950 bool TextInput::OnScrollTimerTick()
1952 // TODO: need to set the new style accordingly the new handle position.
1954 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1956 // nothing to do if all handles are invisible or doesn't exist.
1962 // Choose between the grab handle or the selection handles.
1963 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1964 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1965 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1967 std::size_t newCursorPosition = 0;
1968 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1970 // Whether the handle's position is different of the previous one and in the case of the selection handle,
1971 // the new selection handle's position needs to be different of the other one.
1972 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1973 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1974 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1976 if( differentSelectionHandles )
1978 handlePosition = newCursorPosition;
1980 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1982 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1984 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1985 scrollPosition += scrollDelta;
1986 SetScrollPosition( scrollPosition );
1988 if( mDisplayedTextView.IsScrollPositionTrimmed() )
1993 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
1996 actualHandlePosition.x += mScrollDisplacement.x;
1997 actualHandlePosition.y += mScrollDisplacement.y;
2002 // Public Internal Methods (public for testing purpose)
2004 void TextInput::SetUpTouchEvents()
2006 if ( !mTapDetector )
2008 mTapDetector = TapGestureDetector::New();
2009 // Attach the actors and connect the signal
2010 mTapDetector.Attach(Self());
2012 // As contains children which may register for tap the default control detector is not used.
2013 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2016 if ( !mDoubleTapDetector )
2018 mDoubleTapDetector = TapGestureDetector::New();
2019 mDoubleTapDetector.SetTapsRequired( 2 );
2020 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2022 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2023 // so that we do not, unnecessarily, have a double tap request all the time
2026 if ( !mPanGestureDetector )
2028 mPanGestureDetector = PanGestureDetector::New();
2029 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2032 if ( !mLongPressDetector )
2034 mLongPressDetector = LongPressGestureDetector::New();
2035 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2036 mLongPressDetector.Attach(Self());
2040 void TextInput::CreateTextViewActor()
2042 mDisplayedTextView = Toolkit::TextView::New();
2043 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2044 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2045 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2046 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2047 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2048 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2049 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2050 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2051 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2052 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2054 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2056 Self().Add( mDisplayedTextView );
2059 // Start a timer to initiate, used by the cursor to blink.
2060 void TextInput::StartCursorBlinkTimer()
2062 if ( !mCursorBlinkTimer )
2064 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2065 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2068 if ( !mCursorBlinkTimer.IsRunning() )
2070 mCursorBlinkTimer.Start();
2074 // Start a timer to initiate, used by the cursor to blink.
2075 void TextInput::StopCursorBlinkTimer()
2077 if ( mCursorBlinkTimer )
2079 mCursorBlinkTimer.Stop();
2083 void TextInput::StartEditMode()
2085 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2087 if(!mEditModeActive)
2092 if ( mDoubleTapDetector )
2094 mDoubleTapDetector.Attach( Self() );
2098 void TextInput::EndEditMode()
2100 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2102 ClearKeyInputFocus();
2104 if ( mDoubleTapDetector )
2106 mDoubleTapDetector.Detach( Self() );
2110 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2112 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2114 mUnderlinedPriorToPreEdit = mInputStyle.GetUnderline();
2116 style.SetUnderline( true );
2117 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2121 void TextInput::RemovePreEditStyle()
2123 if ( !mUnderlinedPriorToPreEdit )
2126 style.SetUnderline( false );
2127 SetActiveStyle( style, TextStyle::UNDERLINE );
2131 // IMF related methods
2134 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2136 bool update( false );
2137 bool preeditResetRequired ( false );
2139 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2141 HidePopup(); // If Pop-up shown then hides it as editing text.
2144 switch ( imfEvent.eventName )
2146 case ImfManager::PREEDIT:
2148 mIgnoreFirstCommitFlag = false;
2150 // 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
2151 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2153 // replaces highlighted text with new character
2154 DeleteHighlightedText( false );
2157 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2159 if( IsScrollEnabled() )
2161 // Calculates the new cursor position (in actor coordinates)
2162 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2163 ScrollTextViewToMakeCursorVisible( cursorPosition );
2170 case ImfManager::COMMIT:
2172 if( mIgnoreFirstCommitFlag )
2174 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2175 mIgnoreFirstCommitFlag = false;
2179 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2181 // 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
2182 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2184 // replaces highlighted text with new character
2185 DeleteHighlightedText( false );
2188 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2189 // not needed, one such scenario is when the pre-edit word is too long to fit.
2190 if ( !mIgnoreCommitFlag )
2192 update = CommitReceived( imfEvent.predictiveString );
2196 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2202 if( IsScrollEnabled() )
2204 // Calculates the new cursor position (in actor coordinates)
2205 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2207 ScrollTextViewToMakeCursorVisible( cursorPosition );
2212 case ImfManager::DELETESURROUNDING:
2214 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2215 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2217 mPreEditFlag = false;
2219 std::size_t toDelete = 0;
2220 std::size_t numberOfCharacters = 0;
2222 if( mHighlightMeshActor )
2224 // delete highlighted text.
2225 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2226 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2230 if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2232 toDelete = mCursorPosition + imfEvent.cursorOffset;
2234 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2236 numberOfCharacters = mStyledText.size() - toDelete;
2240 numberOfCharacters = imfEvent.numberOfChars;
2243 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2244 DeleteRange( toDelete, numberOfCharacters );
2246 mCursorPosition = toDelete;
2247 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2251 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2254 case ImfManager::GETSURROUNDING:
2256 // 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
2257 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2258 if (! ( mHighlightMeshActor || mSelectingText ) )
2260 std::string text( GetText() );
2261 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2263 imfManager.SetCursorPosition( mCursorPosition );
2264 imfManager.SetSurroundingText( text );
2267 if( 0 != mNumberOfSurroundingCharactersDeleted )
2269 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2270 mNumberOfSurroundingCharactersDeleted = 0;
2272 if( mStyledText.empty() )
2274 // Styled text is empty, so set the placeholder text.
2275 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2276 mPlaceHolderSet = true;
2281 case ImfManager::VOID:
2283 DALI_ASSERT_DEBUG( false );
2287 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2289 return callbackData;
2292 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2294 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2296 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2297 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2299 bool preeditResetRequest ( false );
2301 if( mPreEditFlag ) // Already in pre-edit state.
2303 if( mStyledText.size() >= mMaxStringLength )
2305 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2306 // Cannot fit these characters into field, clear pre-edit.
2307 if ( !mUnderlinedPriorToPreEdit )
2310 style.SetUnderline( false );
2311 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2313 mIgnoreCommitFlag = true;
2314 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2315 mPreEditFlag = false;
2316 EmitMaxInputCharactersReachedSignal();
2320 // delete existing pre-edit string
2321 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2323 // Store new pre-edit string
2324 mPreEditString.SetText( keyString );
2326 if ( keyString.empty() )
2328 mPreEditFlag = false;
2329 mCursorPosition = mPreEditStartPosition;
2331 if( mStyledText.empty() )
2333 // Styled text is empty, so set the placeholder text.
2334 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2335 mPlaceHolderSet = true;
2339 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2341 GetTextLayoutInfo();
2346 // Insert new pre-edit string. InsertAt updates the size and position table.
2347 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2348 // 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.
2349 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2350 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2351 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2354 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2358 else // mPreEditFlag not set
2360 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2362 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2363 // new pre-edit so move into pre-edit state by setting flag
2364 mPreEditFlag = true;
2365 mPreEditString.SetText( keyString ); // store new pre-edit string
2366 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2367 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2368 // 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.
2369 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2370 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2371 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2372 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2378 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2382 return preeditResetRequest;
2385 bool TextInput::CommitReceived(const std::string& keyString )
2387 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2388 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2390 bool update( false );
2392 RemovePreEditStyle();
2394 const std::size_t styledTextSize( mStyledText.size() );
2395 if( styledTextSize >= mMaxStringLength )
2397 // Cannot fit these characters into field, clear pre-edit.
2400 mIgnoreCommitFlag = true;
2401 mPreEditFlag = false;
2403 EmitMaxInputCharactersReachedSignal();
2409 // delete existing pre-edit string
2410 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2411 mPreEditFlag = false;
2413 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2414 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2416 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2418 // No need to update cursor position as Cursor location given by touch.
2419 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2420 mPreserveCursorPosition = false;
2424 // Cursor not set by touch so needs to be re-positioned to input more text
2425 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2427 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2428 if ( mCommitByKeyInput )
2430 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2431 mCommitByKeyInput = false;
2437 if ( mSelectTextOnCommit )
2439 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2444 else // mPreEditFlag not set
2446 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2448 if( mStyledText.empty() && mPlaceHolderSet )
2450 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2451 mDisplayedTextView.SetText( "" );
2452 mNumberOfSurroundingCharactersDeleted = 0;
2453 mPlaceHolderSet = false;
2455 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2457 mNumberOfSurroundingCharactersDeleted = 0;
2462 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2467 mSelectTextOnCommit = false;
2469 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2470 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2475 // End of IMF related methods
2477 std::size_t TextInput::DeletePreEdit()
2479 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2481 DALI_ASSERT_DEBUG( mPreEditFlag );
2483 const std::size_t preEditStringLength = mPreEditString.GetLength();
2484 const std::size_t styledTextSize = mStyledText.size();
2486 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2488 // Prevents erase items outside mStyledText bounds.
2489 if( mPreEditStartPosition > styledTextSize )
2491 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2492 mPreEditStartPosition = styledTextSize;
2495 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2497 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2498 endPosition = styledTextSize;
2501 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2503 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2504 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2506 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2508 return preEditStringLength;
2511 void TextInput::PreEditReset( bool preserveCursorPosition )
2513 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2514 preserveCursorPosition, mCursorPosition);
2516 // 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.
2517 mPreserveCursorPosition = preserveCursorPosition;
2519 // Reset incase we are in a pre-edit state.
2520 ImfManager imfManager = ImfManager::Get();
2523 imfManager.Reset(); // Will trigger a commit message
2527 void TextInput::CursorUpdate()
2531 ImfManager imfManager = ImfManager::Get();
2534 std::string text( GetText() );
2535 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2536 imfManager.SetCursorPosition ( mCursorPosition );
2537 imfManager.NotifyCursorPosition();
2541 /* Delete highlighted characters redisplay*/
2542 void TextInput::DeleteHighlightedText( bool inheritStyle )
2544 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2546 if(mHighlightMeshActor)
2548 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2550 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2551 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2553 // Get the styled text of the characters to be deleted as it may be needed if
2554 // the "exceed the text-input's boundaries" option is disabled.
2555 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2557 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2559 mStyledText.erase( start, end ); // erase range of characters
2561 // Remove text from TextView.
2563 if( mStyledText.empty() )
2565 // Styled text is empty, so set the placeholder text.
2566 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2567 mPlaceHolderSet = true;
2571 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2573 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2575 // It may happen than after removing a white space or a new line character,
2576 // two words merge, this new word could be big enough to not fit in its
2577 // current line, so moved to the next one, and make some part of the text to
2578 // exceed the text-input's boundary.
2579 if( !mExceedEnabled )
2581 // Get the new text layout after removing some characters.
2582 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2584 // Get text-input's size.
2585 const Vector3& size = GetControlSize();
2587 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2588 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2590 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2592 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2593 styledCharactersToDelete.begin(),
2594 styledCharactersToDelete.end() );
2598 GetTextLayoutInfo();
2604 const TextStyle oldInputStyle( mInputStyle );
2606 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2608 if( oldInputStyle != mInputStyle )
2610 // Updates the line height accordingly with the input style.
2613 EmitStyleChangedSignal();
2619 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2621 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2622 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2624 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2627 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2629 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2630 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2632 mStyledText.erase(itStart, itEnd);
2634 // update the selection handles if they are visible.
2635 if( mHighlightMeshActor )
2637 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2638 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2640 if( minHandle >= start + ncharacters )
2642 minHandle -= ncharacters;
2644 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2649 if( maxHandle >= start + ncharacters )
2651 maxHandle -= ncharacters;
2653 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2659 // 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.
2662 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2664 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2665 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2666 // Mean we do not re-draw the text more than we have too.
2669 /* Delete character at current cursor position and redisplay*/
2670 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2672 // Ensure positionToDelete is not out of bounds.
2673 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2674 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2675 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2677 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2680 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2682 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2684 // Get the styled text of the character to be deleted as it may be needed if
2685 // the "exceed the text-input's boundaries" option is disabled.
2686 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2688 mStyledText.erase(it); // erase the character left of positionToDelete
2690 if( mStyledText.empty() )
2692 // Styled text is empty, so set the placeholder text.
2693 mDisplayedTextView.SetText( mStyledPlaceHolderText );
2694 mPlaceHolderSet = true;
2698 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2700 const Character characterToDelete = styledCharacterToDelete.mText[0];
2702 // It may happen than after removing a white space or a new line character,
2703 // two words merge, this new word could be big enough to not fit in its
2704 // current line, so moved to the next one, and make some part of the text to
2705 // exceed the text-input's boundary.
2706 if( !mExceedEnabled )
2708 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2710 // Get the new text layout after removing one character.
2711 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2713 // Get text-input's size.
2714 const Vector3& size = GetControlSize();
2716 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2717 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2719 MarkupProcessor::StyledTextArray array;
2720 array.push_back( styledCharacterToDelete );
2721 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2723 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2728 GetTextLayoutInfo();
2730 ShowGrabHandleAndSetVisibility( false );
2732 mCursorPosition = positionToDelete -1;
2734 const TextStyle oldInputStyle( mInputStyle );
2736 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2738 if( oldInputStyle != mInputStyle )
2740 // Updates the line height accordingly with the input style.
2743 EmitStyleChangedSignal();
2748 /*Insert new character into the string and (optionally) redisplay text-input*/
2749 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2751 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2753 // Ensure insertionPosition is not out of bounds.
2754 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2756 bool textExceedsMaximunNumberOfCharacters = false;
2757 bool textExceedsBoundary = false;
2758 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2760 ShowGrabHandleAndSetVisibility( false );
2762 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2766 mIgnoreCommitFlag = true;
2767 mPreEditFlag = false;
2768 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2769 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2772 if( textExceedsMaximunNumberOfCharacters )
2774 EmitMaxInputCharactersReachedSignal();
2777 if( textExceedsBoundary )
2779 EmitInputTextExceedsBoundariesSignal();
2780 PreEditReset( false );
2784 return insertedStringLength;
2787 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2793 cursor = ImageActor::New( cursorImage );
2797 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2800 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2801 cursor.SetNinePatchBorder( border );
2803 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2804 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2805 cursor.SetVisible(false);
2810 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2812 // As cursor is not moving due to grab handle, handle should be hidden.
2813 ShowGrabHandleAndSetVisibility( false );
2815 bool cursorPositionChanged = false;
2818 if ( mCursorPosition >= places )
2820 mCursorPosition = mCursorPosition - places;
2821 cursorPositionChanged = true;
2826 if ((mCursorPosition + places) <= mStyledText.size())
2828 mCursorPosition = mCursorPosition + places;
2829 cursorPositionChanged = true;
2833 if( cursorPositionChanged )
2835 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2837 const TextStyle oldInputStyle( mInputStyle );
2838 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2842 if( oldInputStyle != mInputStyle )
2844 // Updates the line height accordingly with the input style.
2847 EmitStyleChangedSignal();
2850 ImfManager imfManager = ImfManager::Get();
2853 imfManager.SetCursorPosition ( mCursorPosition );
2854 imfManager.NotifyCursorPosition();
2859 void TextInput::DrawCursor(const std::size_t nthChar)
2861 // Get height of cursor and set its size
2862 Size size( CURSOR_THICKNESS, 0.0f );
2863 if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2865 size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2869 // Measure Font so know how big text will be if no initial text to measure.
2870 size.height = mLineHeight;
2873 mCursor.SetSize(size);
2875 // If the character is italic then the cursor also tilts.
2876 mCursor.SetRotation( mInputStyle.GetItalics() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2878 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2880 if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2882 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2883 bool altPositionValid; // Alternate cursor validity flag.
2884 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2885 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2887 SetAltCursorEnabled( altPositionValid );
2889 if(!altPositionValid)
2891 mCursor.SetPosition( position + UI_OFFSET );
2895 size.height *= 0.5f;
2896 mCursor.SetSize(size);
2897 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2899 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2900 Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2901 size.height = rowSize.height * 0.5f;
2902 mCursorRTL.SetSize(size);
2903 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2906 if( IsScrollEnabled() )
2908 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2909 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2914 void TextInput::SetAltCursorEnabled( bool enabled )
2916 mCursorRTLEnabled = enabled;
2917 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2920 void TextInput::SetCursorVisibility( bool visible )
2922 mCursorVisibility = visible;
2923 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2924 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2927 void TextInput::CreateGrabHandle( Dali::Image image )
2933 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2937 mGrabHandleImage = image;
2940 mGrabHandle = ImageActor::New(mGrabHandleImage);
2941 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2942 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2944 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2946 ShowGrabHandleAndSetVisibility( false );
2948 CreateGrabArea( mGrabHandle );
2950 mActiveLayer.Add(mGrabHandle);
2954 void TextInput::CreateGrabArea( Actor& parent )
2956 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2957 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2958 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
2959 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2960 mTapDetector.Attach( mGrabArea );
2961 mPanGestureDetector.Attach( mGrabArea );
2963 parent.Add(mGrabArea);
2966 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2968 Vector3 actualHandlePosition;
2972 mActualGrabHandlePosition.x += displacement.x;
2973 mActualGrabHandlePosition.y += displacement.y;
2975 // Grab handle should jump to the nearest character and take cursor with it
2976 std::size_t newCursorPosition = 0;
2977 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2979 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2981 bool handleVisible = true;
2983 if( IsScrollEnabled() )
2985 const Vector3 controlSize = GetControlSize();
2986 const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2987 // Scrolls the text if the handle is not in a visible position
2988 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2995 mCurrentHandlePosition = actualHandlePosition;
2996 mScrollDisplacement = Vector2::ZERO;
3000 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3002 mScrollDisplacement.x = -SCROLL_SPEED;
3004 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3006 mScrollDisplacement.x = SCROLL_SPEED;
3008 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3010 mScrollDisplacement.y = -SCROLL_SPEED;
3012 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3014 mScrollDisplacement.y = SCROLL_SPEED;
3020 if( handleVisible && // Only redraw cursor and do updates if position changed
3021 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3023 mCursorPosition = newCursorPosition;
3025 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3027 const TextStyle oldInputStyle( mInputStyle );
3029 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3031 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3033 if( oldInputStyle != mInputStyle )
3035 // Updates the line height accordingly with the input style.
3038 EmitStyleChangedSignal();
3043 return actualHandlePosition;
3046 void TextInput::ShowGrabHandle( bool visible )
3048 if ( IsGrabHandleEnabled() )