2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
25 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/key-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/property-notification.h>
32 #include <dali/integration-api/debug.h>
35 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
36 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
37 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
38 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
50 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
51 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
52 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
53 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
54 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
55 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
57 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
58 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
59 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
60 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
61 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
63 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
64 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
65 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
66 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
67 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
69 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
70 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
71 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
72 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
73 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
74 const float CURSOR_THICKNESS(4.0f);
75 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
76 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
78 const std::string NEWLINE( "\n" );
80 const TextStyle DEFAULT_TEXT_STYLE;
82 const unsigned int SCROLL_TICK_INTERVAL = 50u;
83 const float SCROLL_THRESHOLD = 10.f;
84 const float SCROLL_SPEED = 15.f;
87 * Selection state enumeration (FSM)
91 SelectionNone, ///< Currently not encountered selected section.
92 SelectionStarted, ///< Encountered selected section
93 SelectionFinished ///< Finished selected section
96 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
98 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
102 if( ( *it ).mIsVisible )
104 return --cursorPosition;
113 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
115 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
117 if( ( *it ).mIsVisible )
119 return cursorPosition;
125 return cursorPosition;
129 * Whether the given position plus the cursor size offset is inside the given boundary.
131 * @param[in] position The given position.
132 * @param[in] cursorSize The cursor size.
133 * @param[in] controlSize The given boundary.
135 * @return whether the given position is inside the given boundary.
137 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
139 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
140 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
141 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
142 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
146 * Splits a text in two halves.
148 * If the text's number of characters is odd, firstHalf has one more character.
150 * @param[in] text The text to be split.
151 * @param[out] firstHalf The first half of the text.
152 * @param[out] secondHalf The second half of the text.
154 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
155 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
156 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
161 const std::size_t textLength = text.size();
162 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
164 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
165 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
168 } // end of namespace
176 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
177 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
178 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
179 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
180 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
181 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
182 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
183 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
184 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
185 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
186 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
187 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
188 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
189 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
190 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
191 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
202 return Toolkit::TextInput::New();
205 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
207 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
208 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
209 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
210 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
211 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
212 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
216 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
217 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
218 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
219 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
220 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
221 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
222 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
223 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
224 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
225 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
226 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
227 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
228 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
229 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
230 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
231 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
234 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
236 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
238 QuadCoordinates quad(x1, y1, x2, y2);
239 mQuadList.push_back( quad );
242 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
244 for(std::size_t i = 0;i < mQuadList.size(); i++)
246 QuadCoordinates& quad = mQuadList[i];
248 quad.min.Clamp(min, max);
249 quad.max.Clamp(min, max);
253 // [TextInput] ////////////////////////////////////////////////////////////////
255 Dali::Toolkit::TextInput TextInput::New()
257 // Create the implementation
258 TextInputPtr textInput(new TextInput());
259 // Pass ownership to CustomActor via derived handle
260 Dali::Toolkit::TextInput handle(*textInput);
261 handle.SetName( "TextInput");
263 textInput->Initialize();
267 TextInput::TextInput()
268 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
273 mDisplayedTextView(),
274 mStyledPlaceHolderText(),
275 mMaxStringLength( DEFAULT_MAX_SIZE ),
276 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
277 mCursorPosition( 0 ),
278 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
279 mIsSelectionHandleOneFlipped( false ),
280 mIsSelectionHandleTwoFlipped( false ),
281 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
282 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
283 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
284 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
285 mSelectionHandleOnePosition( 0 ),
286 mSelectionHandleTwoPosition( 0 ),
288 mPreEditStartPosition( 0 ),
289 mPreEditLength ( 0 ),
290 mNumberOfSurroundingCharactersDeleted( 0 ),
291 mTouchStartTime( 0 ),
293 mCurrentCopySelecton(),
296 mScrollDisplacement(),
297 mCurrentHandlePosition(),
298 mCurrentSelectionId(),
299 mCurrentSelectionHandlePosition(),
300 mRequestedSelection( 0, 0 ),
301 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
302 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
304 mMaterialColor( LIGHTBLUE ),
305 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
306 mOverrideAutomaticAlignment( false ),
307 mCursorRTLEnabled( false ),
308 mClosestCursorPositionEOL ( false ),
309 mCursorBlinkStatus( true ),
310 mCursorVisibility( false ),
311 mGrabHandleVisibility( false ),
312 mIsCursorInScrollArea( true ),
313 mIsGrabHandleInScrollArea( true ),
314 mEditModeActive( false ),
315 mEditOnTouch( true ),
316 mTextSelection( true ),
317 mExceedEnabled( true ),
318 mGrabHandleEnabled( true ),
319 mIsSelectionHandleFlipEnabled( true ),
320 mPreEditFlag( false ),
321 mIgnoreCommitFlag( false ),
322 mIgnoreFirstCommitFlag( false ),
323 mSelectingText( false ),
324 mPreserveCursorPosition( false ),
325 mSelectTextOnCommit( false ),
326 mUnderlinedPriorToPreEdit ( false ),
327 mCommitByKeyInput( false ),
328 mPlaceHolderSet( false ),
329 mMarkUpEnabled( false )
331 // Updates the line height accordingly with the input style.
335 TextInput::~TextInput()
337 StopCursorBlinkTimer();
342 std::string TextInput::GetText() const
346 // Return text-view's text only if the text-input's text is not empty
347 // in order to not to return the placeholder text.
348 if( !mStyledText.empty() )
350 text = mDisplayedTextView.GetText();
356 std::string TextInput::GetMarkupText() const
358 std::string markupString;
359 MarkupProcessor::GetMarkupString( mStyledText, markupString );
364 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
366 mDisplayedTextView.SetText( stylePlaceHolderText );
367 mPlaceHolderSet = true;
368 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
371 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
373 // Get the placeholder styled text array from the markup string.
374 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
375 if( mStyledText.empty() )
377 ShowPlaceholderText( mStyledPlaceHolderText );
381 std::string TextInput::GetPlaceholderText()
383 // Traverses the styled placeholder array getting only the text.
384 // Note that for some languages a 'character' could be represented by more than one 'char'
386 std::string placeholderText;
387 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
389 placeholderText.append( (*it).mText.GetText() );
392 return placeholderText ;
395 void TextInput::SetInitialText(const std::string& initialText)
397 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
399 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
401 mPreEditFlag = false;
402 mIgnoreCommitFlag = true;
405 SetText( initialText );
406 PreEditReset( false ); // Reset keyboard as text changed
409 void TextInput::SetText(const std::string& initialText)
411 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
413 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
415 if( mStyledText.empty() )
417 ShowPlaceholderText( mStyledPlaceHolderText );
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();
524 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
526 mMaterialColor = color;
527 if ( mCustomMaterial )
529 mCustomMaterial.SetDiffuseColor( mMaterialColor );
530 mMeshData.SetMaterial( mCustomMaterial );
534 const Vector4& TextInput::GetMaterialDiffuseColor() const
536 return mMaterialColor;
541 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
543 return mInputStartedSignal;
546 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
548 return mInputFinishedSignal;
551 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
553 return mCutAndPasteToolBarDisplayed;
556 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
558 return mStyleChangedSignal;
561 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
563 return mTextModifiedSignal;
566 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
568 return mMaxInputCharactersReachedSignal;
571 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
573 return mInputTextExceedBoundariesSignal;
576 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
578 Dali::BaseHandle handle( object );
580 bool connected( true );
581 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
583 if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
585 textInput.InputStartedSignal().Connect( tracker, functor );
587 else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
589 textInput.InputFinishedSignal().Connect( tracker, functor );
591 else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
593 textInput.StyleChangedSignal().Connect( tracker, functor );
595 else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
597 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
599 else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
601 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
605 // signalName does not match any signal
612 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
616 // update line height before calculate the actual position.
621 if( setCursorOnTouchPoint )
623 // Sets the cursor position for the given touch point.
624 ReturnClosestIndex( touchPoint, mCursorPosition );
626 // Creates the grab handle.
627 if( IsGrabHandleEnabled() )
629 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
633 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
634 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
635 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
636 ShowGrabHandleAndSetVisibility( true );
638 // Scrolls the text-view if needed.
639 if( IsScrollEnabled() )
641 ScrollTextViewToMakeCursorVisible( cursorPosition );
647 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
659 bool TextInput::IsEditable() const
661 return mEditModeActive;
664 void TextInput::SetEditOnTouch( bool editOnTouch )
666 mEditOnTouch = editOnTouch;
669 bool TextInput::IsEditOnTouch() const
674 void TextInput::SetTextSelectable( bool textSelectable )
676 mTextSelection = textSelectable;
679 bool TextInput::IsTextSelectable() const
681 return mTextSelection;
684 bool TextInput::IsTextSelected() const
686 return mHighlightMeshActor;
689 void TextInput::DeSelectText()
696 void TextInput::SetGrabHandleImage(Dali::Image image )
700 CreateGrabHandle(image);
704 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
706 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
710 mCursor.SetImage( image );
711 mCursor.SetNinePatchBorder( border );
715 Vector3 TextInput::GetSelectionHandleSize()
717 return DEFAULT_SELECTION_HANDLE_SIZE;
720 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
722 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
726 mCursorRTL.SetImage( image);
727 mCursorRTL.SetNinePatchBorder( border );
731 void TextInput::EnableGrabHandle(bool toggle)
733 // enables grab handle with will in turn de-activate magnifier
734 mGrabHandleEnabled = toggle;
737 bool TextInput::IsGrabHandleEnabled()
739 // if false then magnifier will be shown instead.
740 return mGrabHandleEnabled;
743 void TextInput::EnableSelectionHandleFlip( bool toggle )
745 // Deprecated function. To be removed.
746 mIsSelectionHandleFlipEnabled = toggle;
749 bool TextInput::IsSelectionHandleFlipEnabled()
751 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
755 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
757 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
758 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
759 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
761 mSelectionHandleFlipMargin = margin;
764 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
766 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
767 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
769 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
770 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
772 const Vector4 boundary( originX,
774 originX + boundingRectangle.width,
775 originY + boundingRectangle.height );
777 mBoundingRectangleWorldCoordinates = boundary;
780 const Rect<float> TextInput::GetBoundingRectangle() const
782 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
784 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
785 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
787 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
792 const Vector4& TextInput::GetSelectionHandleFlipMargin()
794 return mSelectionHandleFlipMargin;
797 void TextInput::SetTextColor( const Vector4& color )
799 mDisplayedTextView.SetColor( color );
802 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
804 if( style != mInputStyle )
807 bool emitSignal = false;
809 // mask: modify style according to mask, if different emit signal.
810 const TextStyle oldInputStyle( mInputStyle );
812 // Copy the new style.
813 mInputStyle.Copy( style, mask );
815 // if style has changed, emit signal.
816 if( oldInputStyle != mInputStyle )
821 // Updates the line height accordingly with the input style.
824 // Changing font point size will require the cursor to be re-sized
829 EmitStyleChangedSignal();
834 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
836 if ( IsTextSelected() )
838 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
839 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
841 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
843 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
846 // Keeps the old style to be compared with the new one.
847 const TextStyle oldInputStyle( mInputStyle );
849 // Copy only those parameters from the style which are set in the mask.
850 mInputStyle.Copy( style, mask );
852 if( mInputStyle != oldInputStyle )
854 // Updates the line height accordingly with the input style.
857 EmitStyleChangedSignal();
862 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
864 if( !mStyledText.empty() )
866 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
870 TextStyle TextInput::GetStyleAtCursor() const
874 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
876 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
877 style = mStyledText.at( mCursorPosition-1 ).mStyle;
883 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
885 Dali::Font defaultFont = Dali::Font::New();
886 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
893 TextStyle TextInput::GetStyleAt( std::size_t position ) const
895 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
897 if( position >= mStyledText.size() )
899 position = mStyledText.size() - 1;
902 return mStyledText.at( position ).mStyle;
905 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
907 mDisplayedTextView.SetTextAlignment( align );
908 mOverrideAutomaticAlignment = true;
911 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
913 mDisplayedTextView.SetLineJustification( justification );
914 mOverrideAutomaticAlignment = true;
917 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
919 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
922 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
924 return mDisplayedTextView.GetFadeBoundary();
927 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
929 return mDisplayedTextView.GetTextAlignment();
932 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
934 mDisplayedTextView.SetMultilinePolicy( policy );
937 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
939 return mDisplayedTextView.GetMultilinePolicy();
942 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
944 mDisplayedTextView.SetWidthExceedPolicy( policy );
947 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
949 return mDisplayedTextView.GetWidthExceedPolicy();
952 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
954 mDisplayedTextView.SetHeightExceedPolicy( policy );
957 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
959 return mDisplayedTextView.GetHeightExceedPolicy();
962 void TextInput::SetExceedEnabled( bool enable )
964 mExceedEnabled = enable;
967 bool TextInput::GetExceedEnabled() const
969 return mExceedEnabled;
972 void TextInput::SetBackground(Dali::Image image )
974 // TODO Should add this function and add public api to match.
977 bool TextInput::OnTouchEvent(const TouchEvent& event)
982 bool TextInput::OnKeyEvent(const KeyEvent& event)
984 switch( event.state )
988 return OnKeyDownEvent(event);
994 return OnKeyUpEvent(event);
1006 void TextInput::OnKeyInputFocusGained()
1008 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1010 mEditModeActive = true;
1012 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1014 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1016 // Updates the line height accordingly with the input style.
1019 // Connect the signals to use in text input.
1020 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1021 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1023 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1026 GetTextLayoutInfo();
1029 SetCursorVisibility( true );
1030 StartCursorBlinkTimer();
1032 Toolkit::TextInput handle( GetOwner() );
1033 mInputStartedSignal.Emit( handle );
1035 ImfManager imfManager = ImfManager::Get();
1039 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1041 // Notify that the text editing start.
1042 imfManager.Activate();
1044 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1045 imfManager.SetRestoreAfterFocusLost( true );
1047 imfManager.SetCursorPosition( mCursorPosition );
1048 imfManager.NotifyCursorPosition();
1051 mClipboard = Clipboard::Get(); // Store handle to clipboard
1053 // Now in edit mode we can accept string to paste from clipboard
1054 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1057 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1061 void TextInput::OnKeyInputFocusLost()
1063 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1067 // If key input focus is lost, it removes the
1068 // underline from the last pre-edit text.
1069 RemovePreEditStyle();
1070 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1071 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1075 ImfManager imfManager = ImfManager::Get();
1078 // The text editing is finished. Therefore the imf manager don't have restore activation.
1079 imfManager.SetRestoreAfterFocusLost( false );
1081 // Notify that the text editing finish.
1082 imfManager.Deactivate();
1084 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1086 // Disconnect signal used the text input.
1087 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1089 Toolkit::TextInput handle( GetOwner() );
1090 mInputFinishedSignal.Emit( handle );
1091 mEditModeActive = false;
1092 mPreEditFlag = false;
1094 SetCursorVisibility( false );
1095 StopCursorBlinkTimer();
1097 ShowGrabHandleAndSetVisibility( false );
1100 // No longer in edit mode so do not want to receive string from clipboard
1101 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1104 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1107 Clipboard clipboard = Clipboard::Get();
1110 clipboard.HideClipboard();
1114 void TextInput::OnControlStageConnection()
1116 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1118 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1120 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1124 void TextInput::CreateActiveLayer()
1126 Actor self = Self();
1127 mActiveLayer = Layer::New();
1128 mActiveLayer.SetName ( "ActiveLayerActor" );
1130 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1131 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1132 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1134 self.Add( mActiveLayer );
1135 mActiveLayer.RaiseToTop();
1138 void TextInput::OnInitialize()
1140 CreateTextViewActor();
1144 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1145 // different positions depending on language)
1146 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1147 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1149 Actor self = Self();
1150 self.Add( mCursor );
1151 self.Add( mCursorRTL );
1153 mCursorVisibility = false;
1155 CreateActiveLayer(); // todo move this so layer only created when needed.
1157 // Assign names to image actors
1158 mCursor.SetName("mainCursor");
1159 mCursorRTL.SetName("rtlCursor");
1162 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1164 mDisplayedTextView.SetSize( targetSize );
1165 GetTextLayoutInfo();
1166 mActiveLayer.SetSize(targetSize);
1169 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1171 Relayout( mDisplayedTextView, size, container );
1172 Relayout( mPopupPanel.GetRootActor(), size, container );
1174 GetTextLayoutInfo();
1179 Vector3 TextInput::GetNaturalSize()
1181 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1183 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1185 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1186 naturalSize.height = mLineHeight;
1192 float TextInput::GetHeightForWidth( float width )
1194 float height = mDisplayedTextView.GetHeightForWidth( width );
1196 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1198 // If the height is zero, it means there is no text. Let's return the cursor height.
1199 height = mLineHeight;
1205 /*end of Virtual methods from parent*/
1207 // Private Internal methods
1209 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1211 switch (gesture.state)
1213 case Gesture::Started:
1214 // fall through so code not duplicated
1215 case Gesture::Continuing:
1217 if (actor == mGrabArea)
1219 SetCursorVisibility( true );
1220 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1221 MoveGrabHandle( gesture.displacement );
1222 HidePopup(); // Do not show popup whilst handle is moving
1224 else if (actor == mHandleOneGrabArea)
1226 // the displacement in PanGesture is affected by the actor's rotation.
1227 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1228 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1230 MoveSelectionHandle( HandleOne, gesture.displacement );
1232 mState = StateDraggingHandle;
1235 else if (actor == mHandleTwoGrabArea)
1237 // the displacement in PanGesture is affected by the actor's rotation.
1238 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1239 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1241 MoveSelectionHandle( HandleTwo, gesture.displacement );
1243 mState = StateDraggingHandle;
1249 case Gesture::Finished:
1251 // Revert back to non-pressed selection handle images
1252 if (actor == mGrabArea)
1254 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1255 SetCursorVisibility( true );
1256 SetUpPopupSelection();
1259 if (actor == mHandleOneGrabArea)
1261 // the displacement in PanGesture is affected by the actor's rotation.
1262 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1263 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1265 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1267 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1269 ShowPopupCutCopyPaste();
1271 if (actor == mHandleTwoGrabArea)
1273 // the displacement in PanGesture is affected by the actor's rotation.
1274 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1275 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1277 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1279 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1281 ShowPopupCutCopyPaste();
1290 // Stop the flashing animation so easy to see when moved.
1291 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1293 if (touch.GetPoint(0).state == TouchPoint::Down)
1295 SetCursorVisibility( true );
1296 StopCursorBlinkTimer();
1298 else if (touch.GetPoint(0).state == TouchPoint::Up)
1300 SetCursorVisibility( true );
1301 StartCursorBlinkTimer();
1306 // selection handle one
1307 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1309 if (touch.GetPoint(0).state == TouchPoint::Down)
1311 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1313 else if (touch.GetPoint(0).state == TouchPoint::Up)
1315 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1320 // selection handle two
1321 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1323 if (touch.GetPoint(0).state == TouchPoint::Down)
1325 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1327 else if (touch.GetPoint(0).state == TouchPoint::Up)
1329 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1334 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1336 // If text exists then select nearest word.
1337 if ( !mStyledText.empty())
1341 ShowGrabHandleAndSetVisibility( false );
1346 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1347 // converts the pre-edit word being displayed to a committed word.
1348 if ( !mUnderlinedPriorToPreEdit )
1351 style.SetUnderline( false );
1352 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1354 mPreEditFlag = false;
1355 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1356 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1357 PreEditReset( false );
1359 mCursorPosition = 0;
1361 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1362 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1364 std::size_t start = 0;
1365 std::size_t end = 0;
1366 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1368 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1370 ImfManager imfManager = ImfManager::Get();
1373 imfManager.SetCursorPosition ( mCursorPosition );
1374 imfManager.NotifyCursorPosition();
1377 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1379 SelectText( start, end );
1380 ShowPopupCutCopyPaste();
1384 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1385 HidePopup( false ); // Hide popup with setting to do auto show.
1386 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1390 else if ( mClipboard && mClipboard.NumberOfItems() )
1392 ShowPopupCutCopyPaste();
1395 // If no text and clipboard empty then do nothing
1398 // TODO: Change the function name to be more general.
1399 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1401 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1402 , (mEditOnTouch)?"true":"false"
1403 , (mEditModeActive)?"true":"false");
1405 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1410 if( mGrabArea == actor )
1412 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1414 SetUpPopupSelection();
1424 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1426 // Initially don't create the grab handle.
1427 bool createGrabHandle = false;
1429 if ( !mEditModeActive )
1431 // update line height before calculate the actual position.
1434 // Only start edit mode if TextInput configured to edit on touch
1437 // Set the initial cursor position in the tap point.
1438 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1444 // Show the keyboard if it was hidden.
1445 if (!VirtualKeyboard::IsVisible())
1447 VirtualKeyboard::Show();
1450 // Reset keyboard as tap event has occurred.
1451 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1452 PreEditReset( true );
1454 GetTextLayoutInfo();
1456 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1458 // 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.
1460 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1462 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1464 // Notify keyboard so it can 're-capture' word for predictive text.
1465 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1466 ImfManager imfManager = ImfManager::Get();
1469 imfManager.SetCursorPosition ( mCursorPosition );
1470 imfManager.NotifyCursorPosition();
1472 const TextStyle oldInputStyle( mInputStyle );
1474 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1478 // Create the grab handle.
1479 // Grab handle is created later.
1480 createGrabHandle = true;
1482 if( oldInputStyle != mInputStyle )
1484 // Updates the line height accordingly with the input style.
1487 EmitStyleChangedSignal();
1492 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1493 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1494 // otherwise the Grab handle will be shown when selecting.
1495 if ( createGrabHandle && IsGrabHandleEnabled() )
1497 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1498 bool altPositionValid; // Alternate cursor validity flag.
1499 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1500 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1502 if( altPositionValid )
1504 // Check which of the positions is the closest.
1505 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1507 cursorPosition = altPosition;
1513 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1514 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1515 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1516 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1521 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1523 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1525 // Ignore longpress if in selection mode already
1526 if( mHighlightMeshActor )
1531 if(longPress.state == Dali::Gesture::Started)
1533 // Start edit mode on long press
1534 if ( !mEditModeActive )
1539 // If text exists then select nearest word.
1540 if ( !mStyledText.empty())
1544 ShowGrabHandleAndSetVisibility( false );
1549 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1550 // converts the pre-edit word being displayed to a committed word.
1551 if ( !mUnderlinedPriorToPreEdit )
1554 style.SetUnderline( false );
1555 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1557 mPreEditFlag = false;
1558 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1559 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1560 PreEditReset( false );
1562 mCursorPosition = 0;
1564 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1565 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1567 std::size_t start = 0;
1568 std::size_t end = 0;
1569 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1571 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1573 ImfManager imfManager = ImfManager::Get();
1576 imfManager.SetCursorPosition ( mCursorPosition );
1577 imfManager.NotifyCursorPosition();
1580 SelectText( start, end );
1583 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1584 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1586 ShowPopupCutCopyPaste();
1591 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1593 const Text clipboardText( notifier.GetContent() );
1594 PasteText( clipboardText );
1596 SetCursorVisibility( true );
1597 StartCursorBlinkTimer();
1599 ShowGrabHandleAndSetVisibility( false );
1605 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1607 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1609 const std::string& name = button.GetName();
1611 if(name == TextInputPopup::OPTION_SELECT_WORD)
1613 std::size_t start = 0;
1614 std::size_t end = 0;
1615 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1617 SelectText( start, end );
1619 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1621 SetCursorVisibility(false);
1622 StopCursorBlinkTimer();
1624 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1625 std::size_t start = 0;
1627 SelectText( start, end );
1629 else if(name == TextInputPopup::OPTION_CUT)
1631 bool ret = CopySelectedTextToClipboard();
1635 DeleteHighlightedText( true );
1639 SetCursorVisibility( true );
1640 StartCursorBlinkTimer();
1644 else if(name == TextInputPopup::OPTION_COPY)
1646 CopySelectedTextToClipboard();
1650 SetCursorVisibility( true );
1651 StartCursorBlinkTimer();
1655 else if(name == TextInputPopup::OPTION_PASTE)
1657 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1659 PasteText(retrievedString);
1661 SetCursorVisibility( true );
1662 StartCursorBlinkTimer();
1664 ShowGrabHandleAndSetVisibility( false );
1668 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1670 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1671 // Hence pass the false parameter for signalFinished.
1672 HidePopup( true, false );
1673 mClipboard.ShowClipboard();
1679 bool TextInput::OnCursorBlinkTimerTick()
1682 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1683 if ( mCursorRTLEnabled )
1685 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1687 mCursorBlinkStatus = !mCursorBlinkStatus;
1692 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1694 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1696 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1697 if(mHighlightMeshActor && mState == StateEdit)
1699 ShowPopupCutCopyPaste();
1703 //FIXME this routine needs to be re-written as it contains too many branches.
1704 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1706 std::string keyName = event.keyPressedName;
1707 std::string keyString = event.keyPressed;
1709 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1711 // Do not consume "Tab" and "Escape" keys.
1712 if(keyName == "Tab" || keyName == "Escape")
1714 // Escape key to end the edit mode
1720 HidePopup(); // If Pop-up shown then hides it as editing text.
1722 // Update Flag, indicates whether to update the text-input contents or not.
1723 // Any key stroke that results in a visual change of the text-input should
1724 // set this flag to true.
1727 // Whether to scroll text to cursor position.
1728 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1729 bool scroll = false;
1731 if (keyName == "Return")
1733 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1735 bool preEditFlagPreviouslySet( mPreEditFlag );
1737 // replaces highlighted text with new line
1738 DeleteHighlightedText( false );
1740 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1742 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1743 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1746 mCommitByKeyInput = true;
1749 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1750 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1752 mPreEditFlag = true;
1753 mIgnoreCommitFlag = false;
1763 else if ( keyName == "space" )
1765 if ( mHighlightMeshActor )
1767 // Some text is selected so erase it before adding space.
1768 DeleteHighlightedText( true );
1771 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1773 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1774 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1777 mCommitByKeyInput = true;
1782 else if (keyName == "BackSpace")
1784 if ( mHighlightMeshActor )
1786 // Some text is selected so erase it
1787 DeleteHighlightedText( true );
1792 if ( mCursorPosition > 0 )
1794 DeleteCharacter( mCursorPosition );
1800 else if (keyName == "Right")
1805 else if (keyName == "Left")
1807 AdvanceCursor(true);
1810 else // event is a character
1812 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1813 if ( !keyString.empty() )
1815 // replaces highlighted text with new character
1816 DeleteHighlightedText( false );
1818 // Received key String
1819 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1825 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1826 // as this is a costly operation.
1832 if(update || scroll)
1834 if( IsScrollEnabled() )
1836 // Calculates the new cursor position (in actor coordinates)
1837 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1839 ScrollTextViewToMakeCursorVisible( cursorPosition );
1846 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1848 std::string keyName = event.keyPressedName;
1849 std::string keyString = event.keyPressed;
1851 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1853 // The selected text become deselected when the key code is DALI_KEY_BACK.
1854 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1863 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1864 const Vector3& cursorPositionTwo,
1865 bool altPositionValidOne,
1866 bool altPositionValidTwo,
1867 const Vector3& altPositionOne,
1868 const Vector3& altPositionTwo )
1870 // TODO VCC Valid for one line.
1871 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1872 if( cursorPositionOne != cursorPositionTwo )
1874 if( cursorPositionOne.x < cursorPositionTwo.x )
1876 mSelectionHandleOneActualPosition = cursorPositionOne;
1877 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1881 mSelectionHandleOneActualPosition = cursorPositionTwo;
1882 mSelectionHandleTwoActualPosition = cursorPositionOne;
1887 mSelectionHandleOneActualPosition = cursorPositionOne;
1888 if( altPositionValidOne )
1890 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1892 mSelectionHandleOneActualPosition = altPositionOne;
1895 if( altPositionValidTwo )
1897 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1899 mSelectionHandleOneActualPosition = altPositionTwo;
1903 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1904 if( altPositionValidTwo )
1906 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1908 mSelectionHandleTwoActualPosition = altPositionTwo;
1911 if( altPositionValidOne )
1913 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1915 mSelectionHandleTwoActualPosition = altPositionOne;
1921 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1923 // Updates the stored scroll position.
1924 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1926 const Vector3& controlSize = GetControlSize();
1927 Size cursorSize( CURSOR_THICKNESS, 0.f );
1929 // Updates the cursor and grab handle position and visibility.
1930 if( mGrabHandle || mCursor )
1932 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1934 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1935 bool altPositionValid; // Alternate cursor validity flag.
1936 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1937 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1939 if( altPositionValid )
1941 // Check which of the positions is the closest.
1942 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1944 cursorPosition = altPosition;
1948 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1950 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1954 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1955 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1960 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1961 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1965 // Updates the selection handles and highlighted text position and visibility.
1966 if( mSelectionHandleOne && mSelectionHandleTwo )
1968 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1969 bool altPositionValidOne; // Alternate cursor validity flag.
1970 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1971 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1973 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1974 bool altPositionValidTwo; // Alternate cursor validity flag.
1975 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1976 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1978 // VCC TODO: This method is a hack for one line.
1979 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1981 altPositionValidOne,
1982 altPositionValidTwo,
1986 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1987 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1988 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1989 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1991 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1992 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1993 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1994 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1996 if( mHighlightMeshActor )
1998 mHighlightMeshActor.SetVisible( true );
2004 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2006 // Scroll the text to make the cursor visible.
2007 const Size cursorSize( CURSOR_THICKNESS,
2008 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2010 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2012 const Vector3& controlSize = GetControlSize();
2014 // Calculates the new scroll position.
2015 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2016 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2018 scrollOffset.x += cursorPosition.x;
2021 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2023 scrollOffset.y += cursorPosition.y;
2026 // Sets the new scroll position.
2027 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2028 SetScrollPosition( scrollOffset );
2031 void TextInput::StartScrollTimer()
2035 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2036 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2039 if( !mScrollTimer.IsRunning() )
2041 mScrollTimer.Start();
2045 void TextInput::StopScrollTimer()
2049 mScrollTimer.Stop();
2053 bool TextInput::OnScrollTimerTick()
2055 // TODO: need to set the new style accordingly the new handle position.
2057 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2059 // nothing to do if all handles are invisible or doesn't exist.
2065 // Choose between the grab handle or the selection handles.
2066 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2067 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2068 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2070 std::size_t newCursorPosition = 0;
2071 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2073 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2074 // the new selection handle's position needs to be different of the other one.
2075 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2076 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2077 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2079 if( differentSelectionHandles )
2081 handlePosition = newCursorPosition;
2083 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2085 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2087 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2088 scrollPosition += scrollDelta;
2089 SetScrollPosition( scrollPosition );
2091 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2096 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2099 actualHandlePosition.x += mScrollDisplacement.x;
2100 actualHandlePosition.y += mScrollDisplacement.y;
2105 // Public Internal Methods (public for testing purpose)
2107 void TextInput::SetUpTouchEvents()
2109 if ( !mTapDetector )
2111 mTapDetector = TapGestureDetector::New();
2112 // Attach the actors and connect the signal
2113 mTapDetector.Attach(Self());
2115 // As contains children which may register for tap the default control detector is not used.
2116 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2119 if ( !mDoubleTapDetector )
2121 mDoubleTapDetector = TapGestureDetector::New();
2122 mDoubleTapDetector.SetTapsRequired( 2 );
2123 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2125 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2126 // so that we do not, unnecessarily, have a double tap request all the time
2129 if ( !mPanGestureDetector )
2131 mPanGestureDetector = PanGestureDetector::New();
2132 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2135 if ( !mLongPressDetector )
2137 mLongPressDetector = LongPressGestureDetector::New();
2138 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2139 mLongPressDetector.Attach(Self());
2143 void TextInput::CreateTextViewActor()
2145 mDisplayedTextView = Toolkit::TextView::New();
2146 mDisplayedTextView.SetName( "DisplayedTextView ");
2147 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2148 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2149 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2150 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2151 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2152 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2153 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2154 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2155 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2156 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2158 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2160 Self().Add( mDisplayedTextView );
2163 // Start a timer to initiate, used by the cursor to blink.
2164 void TextInput::StartCursorBlinkTimer()
2166 if ( !mCursorBlinkTimer )
2168 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2169 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2172 if ( !mCursorBlinkTimer.IsRunning() )
2174 mCursorBlinkTimer.Start();
2178 // Start a timer to initiate, used by the cursor to blink.
2179 void TextInput::StopCursorBlinkTimer()
2181 if ( mCursorBlinkTimer )
2183 mCursorBlinkTimer.Stop();
2187 void TextInput::StartEditMode()
2189 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2191 if(!mEditModeActive)
2196 if ( mDoubleTapDetector )
2198 mDoubleTapDetector.Attach( Self() );
2202 void TextInput::EndEditMode()
2204 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2206 ClearKeyInputFocus();
2208 if ( mDoubleTapDetector )
2210 mDoubleTapDetector.Detach( Self() );
2214 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2216 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2218 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2220 style.SetUnderline( true );
2221 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2225 void TextInput::RemovePreEditStyle()
2227 if ( !mUnderlinedPriorToPreEdit )
2230 style.SetUnderline( false );
2231 SetActiveStyle( style, TextStyle::UNDERLINE );
2235 // IMF related methods
2238 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2240 bool update( false );
2241 bool preeditResetRequired ( false );
2243 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2245 HidePopup(); // If Pop-up shown then hides it as editing text.
2248 switch ( imfEvent.eventName )
2250 case ImfManager::PREEDIT:
2252 mIgnoreFirstCommitFlag = false;
2254 // 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
2255 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2257 // replaces highlighted text with new character
2258 DeleteHighlightedText( false );
2261 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2263 if( IsScrollEnabled() )
2265 // Calculates the new cursor position (in actor coordinates)
2266 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2267 ScrollTextViewToMakeCursorVisible( cursorPosition );
2274 case ImfManager::COMMIT:
2276 if( mIgnoreFirstCommitFlag )
2278 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2279 mIgnoreFirstCommitFlag = false;
2283 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2285 // 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
2286 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2288 // replaces highlighted text with new character
2289 DeleteHighlightedText( false );
2292 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2293 // not needed, one such scenario is when the pre-edit word is too long to fit.
2294 if ( !mIgnoreCommitFlag )
2296 update = CommitReceived( imfEvent.predictiveString );
2300 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2306 if( IsScrollEnabled() )
2308 // Calculates the new cursor position (in actor coordinates)
2309 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2311 ScrollTextViewToMakeCursorVisible( cursorPosition );
2316 case ImfManager::DELETESURROUNDING:
2318 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2319 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2321 mPreEditFlag = false;
2323 std::size_t toDelete = 0;
2324 std::size_t numberOfCharacters = 0;
2326 if( mHighlightMeshActor )
2328 // delete highlighted text.
2329 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2330 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2334 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2336 toDelete = mCursorPosition + imfEvent.cursorOffset;
2338 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2340 numberOfCharacters = mStyledText.size() - toDelete;
2344 numberOfCharacters = imfEvent.numberOfChars;
2347 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2348 DeleteRange( toDelete, numberOfCharacters );
2350 mCursorPosition = toDelete;
2351 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2355 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2358 case ImfManager::GETSURROUNDING:
2360 // 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
2361 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2362 if (! ( mHighlightMeshActor || mSelectingText ) )
2364 std::string text( GetText() );
2365 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2367 imfManager.SetCursorPosition( mCursorPosition );
2368 imfManager.SetSurroundingText( text );
2371 if( 0 != mNumberOfSurroundingCharactersDeleted )
2373 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2374 mNumberOfSurroundingCharactersDeleted = 0;
2376 if( mStyledText.empty() )
2378 ShowPlaceholderText( mStyledPlaceHolderText );
2383 case ImfManager::VOID:
2385 DALI_ASSERT_DEBUG( false );
2389 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2391 return callbackData;
2394 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2396 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2398 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2399 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2401 bool preeditResetRequest ( false );
2403 if( mPreEditFlag ) // Already in pre-edit state.
2405 if( mStyledText.size() >= mMaxStringLength )
2407 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2408 // Cannot fit these characters into field, clear pre-edit.
2409 if ( !mUnderlinedPriorToPreEdit )
2412 style.SetUnderline( false );
2413 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2415 mIgnoreCommitFlag = true;
2416 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2417 mPreEditFlag = false;
2418 EmitMaxInputCharactersReachedSignal();
2422 // delete existing pre-edit string
2423 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2425 // Store new pre-edit string
2426 mPreEditString.SetText( keyString );
2428 if ( keyString.empty() )
2430 mPreEditFlag = false;
2431 mCursorPosition = mPreEditStartPosition;
2433 if( mStyledText.empty() )
2435 ShowPlaceholderText( mStyledPlaceHolderText );
2439 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2442 GetTextLayoutInfo();
2447 // Insert new pre-edit string. InsertAt updates the size and position table.
2448 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2449 // 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.
2450 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2451 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2452 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2455 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2459 else // mPreEditFlag not set
2461 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2463 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2464 // new pre-edit so move into pre-edit state by setting flag
2465 mPreEditFlag = true;
2466 mPreEditString.SetText( keyString ); // store new pre-edit string
2467 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2468 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2469 // 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.
2470 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2471 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2472 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2473 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2479 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2483 return preeditResetRequest;
2486 bool TextInput::CommitReceived(const std::string& keyString )
2488 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2489 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2491 bool update( false );
2493 RemovePreEditStyle();
2495 const std::size_t styledTextSize( mStyledText.size() );
2496 if( styledTextSize >= mMaxStringLength )
2498 // Cannot fit these characters into field, clear pre-edit.
2501 mIgnoreCommitFlag = true;
2502 mPreEditFlag = false;
2504 EmitMaxInputCharactersReachedSignal();
2510 // delete existing pre-edit string
2511 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2512 mPreEditFlag = false;
2514 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2515 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2517 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2519 // No need to update cursor position as Cursor location given by touch.
2520 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2521 mPreserveCursorPosition = false;
2525 // Cursor not set by touch so needs to be re-positioned to input more text
2526 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2528 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2529 if ( mCommitByKeyInput )
2531 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2532 mCommitByKeyInput = false;
2538 if ( mSelectTextOnCommit )
2540 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2545 else // mPreEditFlag not set
2547 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2549 if( mStyledText.empty() && mPlaceHolderSet )
2551 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2552 mDisplayedTextView.SetText( "" );
2553 mNumberOfSurroundingCharactersDeleted = 0;
2554 mPlaceHolderSet = false;
2556 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2558 mNumberOfSurroundingCharactersDeleted = 0;
2563 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2568 mSelectTextOnCommit = false;
2570 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2571 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2576 // End of IMF related methods
2578 std::size_t TextInput::DeletePreEdit()
2580 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2582 DALI_ASSERT_DEBUG( mPreEditFlag );
2584 const std::size_t preEditStringLength = mPreEditString.GetLength();
2585 const std::size_t styledTextSize = mStyledText.size();
2587 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2589 // Prevents erase items outside mStyledText bounds.
2590 if( mPreEditStartPosition > styledTextSize )
2592 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2593 mPreEditStartPosition = styledTextSize;
2596 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2598 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2599 endPosition = styledTextSize;
2602 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2604 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2605 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2607 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2609 return preEditStringLength;
2612 void TextInput::PreEditReset( bool preserveCursorPosition )
2614 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2615 preserveCursorPosition, mCursorPosition);
2617 // 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.
2618 mPreserveCursorPosition = preserveCursorPosition;
2620 // Reset incase we are in a pre-edit state.
2621 ImfManager imfManager = ImfManager::Get();
2624 imfManager.Reset(); // Will trigger a commit message
2628 void TextInput::CursorUpdate()
2632 ImfManager imfManager = ImfManager::Get();
2635 std::string text( GetText() );
2636 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2637 imfManager.SetCursorPosition ( mCursorPosition );
2638 imfManager.NotifyCursorPosition();
2642 /* Delete highlighted characters redisplay*/
2643 void TextInput::DeleteHighlightedText( bool inheritStyle )
2645 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2647 if( mHighlightMeshActor )
2649 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2651 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2652 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2654 // Get the styled text of the characters to be deleted as it may be needed if
2655 // the "exceed the text-input's boundaries" option is disabled.
2656 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2658 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2660 mStyledText.erase( start, end ); // erase range of characters
2662 // Remove text from TextView and update place holder text if required
2664 // Set the placeholder text only if the styled text is empty.
2665 if( mStyledText.empty() )
2667 ShowPlaceholderText( mStyledPlaceHolderText );
2671 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2673 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2675 // It may happen than after removing a white space or a new line character,
2676 // two words merge, this new word could be big enough to not fit in its
2677 // current line, so moved to the next one, and make some part of the text to
2678 // exceed the text-input's boundary.
2679 if( !mExceedEnabled )
2681 // Get the new text layout after removing some characters.
2682 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2684 // Get text-input's size.
2685 const Vector3& size = GetControlSize();
2687 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2688 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2690 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2692 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2693 styledCharactersToDelete.begin(),
2694 styledCharactersToDelete.end() );
2698 GetTextLayoutInfo();
2706 const TextStyle oldInputStyle( mInputStyle );
2708 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2710 if( oldInputStyle != mInputStyle )
2712 // Updates the line height accordingly with the input style.
2715 EmitStyleChangedSignal();
2721 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2723 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2724 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2726 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2729 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2731 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2732 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2734 mStyledText.erase(itStart, itEnd);
2736 // update the selection handles if they are visible.
2737 if( mHighlightMeshActor )
2739 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2740 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2742 if( minHandle >= start + ncharacters )
2744 minHandle -= ncharacters;
2746 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2751 if( maxHandle >= start + ncharacters )
2753 maxHandle -= ncharacters;
2755 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2761 // 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.
2764 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2766 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2767 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2768 // Mean we do not re-draw the text more than we have too.
2771 /* Delete character at current cursor position and redisplay*/
2772 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2774 // Ensure positionToDelete is not out of bounds.
2775 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2776 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2777 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2779 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2782 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2784 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2786 // Get the styled text of the character to be deleted as it may be needed if
2787 // the "exceed the text-input's boundaries" option is disabled.
2788 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2790 mStyledText.erase(it); // erase the character left of positionToDelete
2792 if( mStyledText.empty() )
2794 ShowPlaceholderText( mStyledPlaceHolderText );
2798 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2800 const Character characterToDelete = styledCharacterToDelete.mText[0];
2802 // It may happen than after removing a white space or a new line character,
2803 // two words merge, this new word could be big enough to not fit in its
2804 // current line, so moved to the next one, and make some part of the text to
2805 // exceed the text-input's boundary.
2806 if( !mExceedEnabled )
2808 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2810 // Get the new text layout after removing one character.
2811 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2813 // Get text-input's size.
2814 const Vector3& size = GetControlSize();
2816 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2817 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2819 MarkupProcessor::StyledTextArray array;
2820 array.push_back( styledCharacterToDelete );
2821 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2823 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2828 GetTextLayoutInfo();
2830 ShowGrabHandleAndSetVisibility( false );
2832 mCursorPosition = positionToDelete -1;
2834 const TextStyle oldInputStyle( mInputStyle );
2836 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2838 if( oldInputStyle != mInputStyle )
2840 // Updates the line height accordingly with the input style.
2843 EmitStyleChangedSignal();
2848 /*Insert new character into the string and (optionally) redisplay text-input*/
2849 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2851 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2853 // Ensure insertionPosition is not out of bounds.
2854 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2856 bool textExceedsMaximunNumberOfCharacters = false;
2857 bool textExceedsBoundary = false;
2858 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2860 ShowGrabHandleAndSetVisibility( false );
2862 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2866 mIgnoreCommitFlag = true;
2867 mPreEditFlag = false;
2868 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2869 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2872 if( textExceedsMaximunNumberOfCharacters )
2874 EmitMaxInputCharactersReachedSignal();
2877 if( textExceedsBoundary )
2879 EmitInputTextExceedsBoundariesSignal();
2880 PreEditReset( false );
2884 return insertedStringLength;
2887 ImageActor TextInput::CreateCursor( const Vector4& color)
2890 cursor = CreateSolidColorActor(color);
2891 cursor.SetName( "Cursor" );
2893 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2894 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2895 cursor.SetVisible(false);
2900 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2902 // As cursor is not moving due to grab handle, handle should be hidden.
2903 ShowGrabHandleAndSetVisibility( false );
2905 bool cursorPositionChanged = false;
2908 if ( mCursorPosition >= places )
2910 mCursorPosition = mCursorPosition - places;
2911 cursorPositionChanged = true;
2916 if ((mCursorPosition + places) <= mStyledText.size())
2918 mCursorPosition = mCursorPosition + places;
2919 cursorPositionChanged = true;
2923 if( cursorPositionChanged )
2925 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2927 const TextStyle oldInputStyle( mInputStyle );
2928 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2932 if( oldInputStyle != mInputStyle )
2934 // Updates the line height accordingly with the input style.
2937 EmitStyleChangedSignal();
2940 ImfManager imfManager = ImfManager::Get();
2943 imfManager.SetCursorPosition ( mCursorPosition );
2944 imfManager.NotifyCursorPosition();
2949 void TextInput::DrawCursor()
2951 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2953 // Get height of cursor and set its size
2954 Size size( CURSOR_THICKNESS, 0.0f );
2955 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2957 size.height = rowRect.height;
2961 // Measure Font so know how big text will be if no initial text to measure.
2962 size.height = mLineHeight;
2965 mCursor.SetSize(size);
2967 // If the character is italic then the cursor also tilts.
2968 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2970 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2972 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2974 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2975 bool altPositionValid; // Alternate cursor validity flag.
2976 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2977 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2979 SetAltCursorEnabled( altPositionValid );
2981 if( !altPositionValid )
2983 mCursor.SetPosition( position + UI_OFFSET );
2987 size.height *= 0.5f;
2988 mCursor.SetSize(size);
2989 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2991 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2992 size.height = rowRect.height * 0.5f;
2993 mCursorRTL.SetSize(size);
2994 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2997 if( IsScrollEnabled() )
2999 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3000 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3005 void TextInput::SetAltCursorEnabled( bool enabled )
3007 mCursorRTLEnabled = enabled;
3008 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3011 void TextInput::SetCursorVisibility( bool visible )
3013 mCursorVisibility = visible;
3014 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3015 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3018 void TextInput::CreateGrabHandle( Dali::Image image )
3024 mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
3028 mGrabHandleImage = image;
3031 mGrabHandle = ImageActor::New(mGrabHandleImage);
3032 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3033 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3035 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3037 ShowGrabHandleAndSetVisibility( false );
3039 CreateGrabArea( mGrabHandle );
3041 mActiveLayer.Add(mGrabHandle);
3045 void TextInput::CreateGrabArea( Actor& parent )
3047 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3048 mGrabArea.SetName( "GrabArea" );
3049 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3050 mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3051 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3052 mTapDetector.Attach( mGrabArea );
3053 mPanGestureDetector.Attach( mGrabArea );
3054 mLongPressDetector.Attach( mGrabArea );
3056 parent.Add(mGrabArea);
3059 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3061 Vector3 actualHandlePosition;
3065 mActualGrabHandlePosition.x += displacement.x;
3066 mActualGrabHandlePosition.y += displacement.y;
3068 // Grab handle should jump to the nearest character and take cursor with it
3069 std::size_t newCursorPosition = 0;
3070 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3072 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3073 bool altPositionValid; // Alternate cursor validity flag.
3074 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3075 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3077 if( altPositionValid )
3079 // Check which of the positions is the closest.
3080 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3082 actualHandlePosition = altPosition;
3086 bool handleVisible = true;
3088 if( IsScrollEnabled() )
3090 const Vector3 controlSize = GetControlSize();
3091 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3092 // Scrolls the text if the handle is not in a visible position
3093 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3100 mCurrentHandlePosition = actualHandlePosition;
3101 mScrollDisplacement = Vector2::ZERO;
3105 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3107 mScrollDisplacement.x = -SCROLL_SPEED;
3109 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3111 mScrollDisplacement.x = SCROLL_SPEED;
3113 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3115 mScrollDisplacement.y = -SCROLL_SPEED;
3117 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3119 mScrollDisplacement.y = SCROLL_SPEED;
3125 if( handleVisible && // Only redraw cursor and do updates if position changed
3126 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3128 mCursorPosition = newCursorPosition;
3130 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3132 const TextStyle oldInputStyle( mInputStyle );
3134 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3136 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3138 if( oldInputStyle != mInputStyle )
3140 // Updates the line height accordingly with the input style.
3143 EmitStyleChangedSignal();
3148 return actualHandlePosition;
3151 void TextInput::ShowGrabHandle( bool visible )
3153 if ( IsGrabHandleEnabled() )
3157 mGrabHandle.SetVisible( mGrabHandleVisibility );
3159 StartMonitoringStageForTouch();
3163 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3165 mGrabHandleVisibility = visible;
3166 ShowGrabHandle( visible );
3169 // Callbacks connected to be Property notifications for Boundary checking.
3171 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3173 mIsSelectionHandleOneFlipped = true;
3174 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3175 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3178 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3180 mIsSelectionHandleOneFlipped = false;
3181 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3182 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3185 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3187 mIsSelectionHandleTwoFlipped = true;
3188 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3189 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3192 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3194 mIsSelectionHandleTwoFlipped = false;
3195 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3196 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3199 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3200 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3202 mSelectionHandleOne.SetOpacity(0.0f);
3205 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3207 mSelectionHandleOne.SetOpacity(1.0f);
3210 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3212 mSelectionHandleTwo.SetOpacity(0.0f);
3215 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3217 mSelectionHandleTwo.SetOpacity(1.0f);
3220 // End of Callbacks connected to be Property notifications for Boundary checking.
3222 void TextInput::SetUpHandlePropertyNotifications()
3224 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3226 Vector3 handlesize = GetSelectionHandleSize();
3228 // Exceeding horizontal boundary
3229 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3230 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3232 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3233 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3235 // Within horizontal boundary
3236 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3237 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3239 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3240 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3242 // Exceeding vertical boundary
3243 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3244 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3245 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3246 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3248 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3249 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3250 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3251 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3253 // Within vertical boundary
3254 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3255 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3256 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3257 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3259 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3260 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3261 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3262 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3265 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3267 mSelectionHandleOnePosition = start;
3268 mSelectionHandleTwoPosition = end;
3270 if ( !mSelectionHandleOne )
3272 // create normal and pressed images
3273 mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3274 mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3276 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3277 mSelectionHandleOne.SetName("SelectionHandleOne");
3278 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3279 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3280 mIsSelectionHandleOneFlipped = false;
3281 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3283 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3284 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3286 mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3287 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3289 mTapDetector.Attach( mHandleOneGrabArea );
3290 mPanGestureDetector.Attach( mHandleOneGrabArea );
3292 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3294 mSelectionHandleOne.Add( mHandleOneGrabArea );
3295 mActiveLayer.Add( mSelectionHandleOne );
3298 if ( !mSelectionHandleTwo )
3300 // create normal and pressed images
3301 mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3302 mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3304 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3305 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3306 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3307 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3308 mIsSelectionHandleTwoFlipped = false;
3309 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3311 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3312 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3313 mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor
3314 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3316 mTapDetector.Attach( mHandleTwoGrabArea );
3317 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3319 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3321 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3323 mActiveLayer.Add( mSelectionHandleTwo );
3326 SetUpHandlePropertyNotifications();
3328 // update table as text may have changed.
3329 GetTextLayoutInfo();
3331 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3332 bool altPositionValidOne; // Alternate cursor validity flag.
3333 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3334 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3336 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3337 bool altPositionValidTwo; // Alternate cursor validity flag.
3338 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3339 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3341 // VCC TODO: This method is a hack for one line.
3342 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3344 altPositionValidOne,
3345 altPositionValidTwo,
3349 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3350 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3352 // Calculates and set the visibility if the scroll mode is enabled.
3353 bool isSelectionHandleOneVisible = true;
3354 bool isSelectionHandleTwoVisible = true;
3355 if( IsScrollEnabled() )
3357 const Vector3& controlSize( GetControlSize() );
3358 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3359 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3360 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3361 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3364 CreateHighlight(); // function will only create highlight if not already created.
3367 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3369 Vector3 actualHandlePosition;
3371 if ( mSelectionHandleOne && mSelectionHandleTwo )
3373 const Vector3& controlSize = GetControlSize();
3375 Size cursorSize( CURSOR_THICKNESS, 0.f );
3377 // Get a reference of the wanted selection handle (handle one or two).
3378 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3380 // Get a reference for the current position of the handle and a copy of its pair
3381 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3382 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3384 // Get a handle of the selection handle actor
3385 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3387 // Selection handles should jump to the nearest character
3388 std::size_t newHandlePosition = 0;
3389 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3391 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3392 bool altPositionValid; // Alternate cursor validity flag.
3393 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3394 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3395 if( altPositionValid )
3397 // Check which of the positions is the closest.
3398 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3400 actualHandlePosition = altPosition;
3404 bool handleVisible = true;
3406 if( IsScrollEnabled() )
3408 mCurrentSelectionId = handleId;
3410 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3411 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3412 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3419 mCurrentSelectionHandlePosition = actualHandlePosition;
3420 mScrollDisplacement = Vector2::ZERO;
3424 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3426 mScrollDisplacement.x = -SCROLL_SPEED;
3428 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3430 mScrollDisplacement.x = SCROLL_SPEED;
3432 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3434 mScrollDisplacement.y = -SCROLL_SPEED;
3436 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3438 mScrollDisplacement.y = SCROLL_SPEED;
3444 if ( handleVisible && // Ensure the handle is visible.
3445 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3446 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3448 currentSelectionHandlePosition = newHandlePosition;
3450 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3451 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3455 if ( handleId == HandleOne )
3457 const TextStyle oldInputStyle( mInputStyle );
3459 // Set Active Style to that of first character in selection
3460 if( mSelectionHandleOnePosition < mStyledText.size() )
3462 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3465 if( oldInputStyle != mInputStyle )
3467 // Updates the line height accordingly with the input style.
3470 EmitStyleChangedSignal();
3476 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3479 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3481 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3482 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3484 if ( selectionHandleActor )
3486 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3487 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3488 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3490 if( IsScrollEnabled() )
3492 const Size cursorSize( CURSOR_THICKNESS,
3493 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3494 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3496 GetControlSize() ) );
3501 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3503 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3505 // VCC Set true/false in logical order. TODO : It needs to be checked.
3507 if( startSelection > endSelection )
3509 std::swap( startSelection, endSelection );
3511 std::size_t index = 0u;
3512 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3514 if( ( index < startSelection ) || ( endSelection <= index ) )
3525 // Calculate the dimensions of the quads they will make the highlight mesh
3526 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3528 // At the moment there is no public API to modify the block alignment option.
3530 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3532 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3534 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3535 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3537 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3538 std::vector<bool> selectedVisualText;
3539 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3540 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3541 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3543 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3544 float rowLeft = 0.0f;
3545 float rowRight = 0.0f;
3546 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3547 float maxRowLeft = std::numeric_limits<float>::max();
3548 float maxRowRight = 0.0f;
3550 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3552 // Scan through entire text.
3555 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3557 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3558 bool charSelected = false;
3559 if( selectedIt != selectedEndIt )
3561 charSelected = *selectedIt++;
3564 if( selectionState == SelectionNone )
3568 selectionState = SelectionStarted;
3569 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3570 rowRight = rowLeft + charInfo.mSize.width;
3573 else if( selectionState == SelectionStarted )
3575 // break selection on:
3576 // 1. new line causing selection break. (\n or wordwrap)
3577 // 2. character not selected.
3578 if( !charSelected ||
3579 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3581 // finished selection.
3582 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3583 // that it resides on. That way this enumeration is not necessary.
3585 if(lastIt->mIsNewParagraphChar)
3587 // 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.
3588 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3590 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3591 maxRowLeft = std::min(maxRowLeft, min.x);
3592 maxRowRight = std::max(maxRowRight, max.x);
3593 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3594 float rowTop = rowBottom - rowSize.height;
3596 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3599 rowRight = std::numeric_limits<float>::max();
3601 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3603 selectionState = SelectionNone;
3605 // Still selected? start a new selection
3608 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3610 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3611 selectionState = SelectionStarted;
3616 // build up highlight(s) with this selection data.
3617 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3618 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3625 // If reached end, and still on selection, then close selection.
3628 if(selectionState == SelectionStarted)
3630 // finished selection.
3632 if(lastIt->mIsNewParagraphChar)
3634 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3636 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3637 maxRowLeft = std::min(maxRowLeft, min.x);
3638 maxRowRight = std::max(maxRowRight, max.x);
3639 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3640 float rowTop = rowBottom - rowSize.height;
3641 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3645 // Get the top left and bottom right corners.
3646 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3647 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3648 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3650 // Clamp quads so they appear to clip to borders of the whole text.
3651 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3653 // For block-align align Further Clamp quads to max left and right extents
3654 // BlockAlign: Will adjust highlight to block:
3656 // H[ello] (top row right = max of all rows right)
3657 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3658 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3659 // [text] (bottom row left = min of all rows left)
3660 // (common in SMS messaging selection)
3662 // As opposed to the default which is tight text highlighting.
3667 // (common in regular text editors/web browser selection)
3668 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3670 // Finally clamp quads again so they don't exceed the boundry of the control.
3671 const Vector3& controlSize = GetControlSize();
3672 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3675 return mNewHighlightInfo;
3678 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3679 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3680 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3682 // At the moment there is no public API to modify the block alignment option.
3684 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3686 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3688 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3689 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3691 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3692 std::vector<bool> selectedVisualText;
3693 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3694 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3695 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3697 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3698 float rowLeft = 0.0f;
3699 float rowRight = 0.0f;
3701 // VCC TODO this is valid for one line.
3703 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3705 // Scan through entire text.
3708 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3710 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3711 bool charSelected = false;
3712 if( selectedIt != selectedEndIt )
3714 charSelected = *selectedIt++;
3719 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3720 rowRight = rowLeft + charInfo.mSize.width;
3722 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3723 float rowTop = rowBottom - rowSize.height;
3724 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3730 // Finally clamp quads again so they don't exceed the boundry of the control.
3731 const Vector3& controlSize = GetControlSize();
3732 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3735 return mNewHighlightInfo;
3738 void TextInput::UpdateHighlight()
3740 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3742 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3744 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3745 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3746 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3747 // [BOTTOM] [ MIDDLE ]
3750 // Each quad is created as 2 triangles.
3751 // Middle is just 1 quad regardless of its size.
3765 if ( mHighlightMeshActor )
3767 // vertex and triangle buffers should always be present if MeshActor is alive.
3768 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3769 MeshData::VertexContainer vertices;
3770 Dali::MeshData::FaceIndices faceIndices;
3772 if( !newHighlightInfo.mQuadList.empty() )
3774 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3775 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3777 // vertex position defaults to (0 0 0)
3778 MeshData::Vertex vertex;
3779 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3782 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3784 // Add each quad geometry (a sub-selection) to the mesh data.
3794 QuadCoordinates& quad = *iter;
3796 vertex.x = quad.min.x;
3797 vertex.y = quad.min.y;
3798 vertices.push_back( vertex );
3801 vertex.x = quad.max.x;
3802 vertex.y = quad.min.y;
3803 vertices.push_back( vertex );
3805 // bottom-left (v+2)
3806 vertex.x = quad.min.x;
3807 vertex.y = quad.max.y;
3808 vertices.push_back( vertex );
3810 // bottom-right (v+3)
3811 vertex.x = quad.max.x;
3812 vertex.y = quad.max.y;
3813 vertices.push_back( vertex );
3815 // triangle A (3, 1, 0)
3816 faceIndices.push_back( v + 3 );
3817 faceIndices.push_back( v + 1 );
3818 faceIndices.push_back( v );
3820 // triangle B (0, 2, 3)
3821 faceIndices.push_back( v );
3822 faceIndices.push_back( v + 2 );
3823 faceIndices.push_back( v + 3 );
3825 mMeshData.SetFaceIndices( faceIndices );
3828 BoneContainer bones(0); // passed empty as bones not required
3829 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3830 mHighlightMesh.UpdateMeshData(mMeshData);
3835 void TextInput::ClearPopup()
3837 mPopupPanel.Clear();
3840 void TextInput::AddPopupOptions()
3842 mPopupPanel.AddPopupOptions();
3845 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3847 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3849 Vector3 clampedPosition ( position );
3850 Vector3 tailOffsetPosition ( position );
3852 float xOffSet( 0.0f );
3854 Actor self = Self();
3855 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3857 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3858 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3860 // Clamp to left or right or of boundary
3861 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3863 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3865 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3867 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3870 clampedPosition.x = position.x + xOffSet;
3871 tailOffsetPosition.x = -xOffSet;
3873 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3874 bool flipTail( false );
3876 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3878 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3882 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3883 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3886 void TextInput::HidePopup(bool animate, bool signalFinished )
3888 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3890 mPopupPanel.Hide( animate );
3892 if( animate && signalFinished )
3894 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3899 void TextInput::ShowPopup( bool animate )
3902 Vector2 alternativePopupPosition;
3904 if(mHighlightMeshActor && mState == StateEdit)
3907 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3909 // When text is selected, show popup above top handle (and text), or below bottom handle.
3910 // topHandle: referring to the top most point of the handle or the top line of selection.
3911 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3913 topHandle = mSelectionHandleOneActualPosition;
3914 bottomHandle = mSelectionHandleTwoActualPosition;
3915 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3919 topHandle = mSelectionHandleTwoActualPosition;
3920 bottomHandle = mSelectionHandleOneActualPosition;
3921 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3923 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3924 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3926 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3928 position.x = xPosition;
3930 // Alternative position if no upper space
3931 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3932 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3936 // When no text is selected, show popup at world position of grab handle or cursor
3937 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3938 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3939 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3940 // if can't be positioned above, then position below row.
3941 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3944 // If grab handle enabled then position pop-up below the grab handle.
3945 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3949 SetPopupPosition( position, alternativePopupPosition );
3952 mPopupPanel.Show( Self(), animate );
3953 StartMonitoringStageForTouch();
3955 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3958 void TextInput::ShowPopupCutCopyPaste()
3962 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3963 // Check the selected text is whole text or not.
3964 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3966 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3969 if ( !mStyledText.empty() && IsTextSelected() )
3971 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3972 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3975 if( mClipboard && mClipboard.NumberOfItems() )
3977 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3978 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3983 mPopupPanel.Hide(false);
3987 void TextInput::SetUpPopupSelection( bool showCutButton )
3990 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3991 // If no text exists then don't offer to select
3992 if ( !mStyledText.empty() )
3994 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3995 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3996 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
3998 // if clipboard has valid contents then offer paste option
3999 if( mClipboard && mClipboard.NumberOfItems() )
4001 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4002 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4007 mPopupPanel.Hide(false);
4010 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4015 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4016 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4017 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4018 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4020 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4022 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4024 float closestYdifference = std::numeric_limits<float>::max();
4025 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4026 std::size_t numberOfMatchedCharacters = 0;
4028 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4029 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4031 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4033 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4034 float baselinePosition = info.mPosition.y - info.mDescender;
4036 if( info.mIsVisible )
4038 // store difference between source y point and the y position of the current character
4039 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4041 if( currentYdifference < closestYdifference )
4043 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4044 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4045 closestYdifference = currentYdifference;
4046 matchedCharacters.clear();
4047 numberOfMatchedCharacters = 0; // reset count
4050 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4051 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4053 // ignore new line character.
4054 if( !info.mIsNewParagraphChar )
4056 matchedCharacters.push_back( info );
4057 numberOfMatchedCharacters++;
4061 } // End of loop checking each character's y position in the character layout table
4063 // Check if last character is a newline, if it is
4064 // then need pretend there is an imaginary line afterwards,
4065 // and check if user is touching below previous line.
4066 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4068 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4070 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4074 // 2 Iterate through matching list of y positions and find closest matching X position.
4076 bool matched( false );
4078 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4079 std::size_t visualIndex = 0u;
4080 const std::size_t matchedCharactersSize = matchedCharacters.size();
4081 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4083 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4085 if( info.mIsVisible )
4087 // stop when on left side of character's center.
4088 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4089 if( sourceScrollOffset.x < characterMidPointPosition )
4091 if(info.mIsRightToLeftCharacter)
4093 rightToLeftChar = true;
4095 glyphIntersection = info.mPosition.x;
4100 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4104 if( visualIndex == matchedCharactersSize )
4106 rightToLeftChar = lastRightToLeftChar;
4109 closestIndex = lineOffset + visualIndex;
4111 mClosestCursorPositionEOL = false; // reset
4112 if( ( visualIndex == matchedCharactersSize ) && !matched )
4114 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4117 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4118 if( rightToLeftChar && lastRightToLeftChar )
4120 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4125 // closestIndex is the visual index, need to convert it to the logical index
4126 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4128 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4130 // Checks for situations where user is touching between LTR and RTL
4131 // characters. To identify if the user means the end of a LTR string
4132 // or the beginning of an RTL string, and vice versa.
4133 if( closestIndex > 0 )
4135 if( rightToLeftChar && !lastRightToLeftChar )
4140 // A: In this touch range, the user is indicating that they wish to place
4141 // the cursor at the end of the LTR text.
4142 // B: In this touch range, the user is indicating that they wish to place
4143 // the cursor at the end of the RTL text.
4145 // Result of touching A area:
4146 // [.....LTR]|[RTL......]+
4148 // |: primary cursor (for typing LTR chars)
4149 // +: secondary cursor (for typing RTL chars)
4151 // Result of touching B area:
4152 // [.....LTR]+[RTL......]|
4154 // |: primary cursor (for typing RTL chars)
4155 // +: secondary cursor (for typing LTR chars)
4157 if( sourceScrollOffset.x < glyphIntersection )
4162 else if( !rightToLeftChar && lastRightToLeftChar )
4164 if( sourceScrollOffset.x < glyphIntersection )
4171 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4172 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4173 // one further ahead
4174 if( rightToLeftChar && !lastRightToLeftChar )
4179 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4181 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4183 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4192 float TextInput::GetLineJustificationPosition() const
4194 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4195 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4196 float alignmentOffset = 0.f;
4198 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4199 if( alignment & Toolkit::Alignment::HorizontalLeft )
4201 alignmentOffset = 0.f;
4203 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4205 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4207 else if( alignment & Toolkit::Alignment::HorizontalRight )
4209 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4212 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4213 float justificationOffset = 0.f;
4215 switch( justification )
4217 case Toolkit::TextView::Left:
4219 justificationOffset = 0.f;
4222 case Toolkit::TextView::Center:
4224 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4227 case Toolkit::TextView::Right:
4229 justificationOffset = mTextLayoutInfo.mTextSize.width;
4232 case Toolkit::TextView::Justified:
4234 justificationOffset = 0.f;
4239 DALI_ASSERT_ALWAYS( false );
4243 return alignmentOffset + justificationOffset;
4246 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4248 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4249 A newline character is not inserted in this case */
4251 Vector3 cursorPosition;
4253 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4257 if( characterPosition > 0u )
4259 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4261 // If previous character on a different line then use current characters position
4262 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4264 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4265 if ( mClosestCursorPositionEOL )
4267 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4271 cursorPosition = Vector3( currentCharInfo.mPosition );
4280 // If the character is left to right, the position is the character's position plus its width.
4281 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4283 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4284 cursorPosition.y = currentCharInfo.mPosition.y;
4287 return cursorPosition;
4290 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4292 bool direction = false;
4293 Vector3 alternatePosition;
4294 bool alternatePositionValid = false;
4296 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4299 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4301 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4302 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4303 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4305 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4307 alternatePositionValid = false;
4308 directionRTL = false;
4310 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4312 if( characterPosition == 0u )
4314 // When the cursor position is at the beginning, it should be at the start of the current character.
4315 // If the current character is LTR, then the start is on the right side of the glyph.
4316 // If the current character is RTL, then the start is on the left side of the glyph.
4318 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4320 characterPosition = FindVisibleCharacter( Right, 0u );
4323 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4324 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4326 cursorPosition.x = info.mPosition.x + rtlOffset;
4327 cursorPosition.y = info.mPosition.y;
4328 directionRTL = info.mIsRightToLeftCharacter;
4330 else if( characterPosition > 0u )
4332 // Get the direction of the paragraph.
4333 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4334 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4336 // When cursor is not at beginning, consider possibility of
4337 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4339 // Cursor position should be the end of the last character.
4340 // If the last character is LTR, then the end is on the right side of the glyph.
4341 // If the last character is RTL, then the end is on the left side of the glyph.
4343 --characterPosition;
4345 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4347 characterPosition = FindVisibleCharacter( Left, characterPosition );
4350 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4351 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4353 // VCC TODO : check for a new paragraph character.
4355 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4356 const Vector3& size = GetControlSize();
4358 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4360 --characterPosition;
4362 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4365 if( !info.mIsNewParagraphChar )
4367 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4371 // VCC TODO : check for a new paragraph character.
4373 // When cursor points to first character on new line, position cursor at the start of this glyph.
4374 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4376 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4377 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4379 cursorPosition.x = infoNext.mPosition.x + start;
4380 cursorPosition.y = infoNext.mPosition.y;
4384 // If cursor points to the end of text, then can only position
4385 // cursor where the new line starts based on the line-justification position.
4386 cursorPosition.x = GetLineJustificationPosition();
4388 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4390 // If this is after the last character, then we can assume that the new cursor
4391 // should be exactly one row below the current row.
4393 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4394 cursorPosition.y = info.mPosition.y + rowRect.height;
4398 // If this is not after last character, then we can use this row's height.
4399 // should be exactly one row below the current row.
4401 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4402 cursorPosition.y = info.mPosition.y + rowRect.height;
4407 directionRTL = info.mIsRightToLeftCharacter;
4409 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4411 // 1. When the cursor is neither at the beginning or the end,
4412 // we can show multiple cursors under situations when the cursor is
4413 // between RTL and LTR text...
4414 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4416 std::size_t characterAltPosition = characterPosition + 1u;
4418 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4420 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4422 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4423 // Text: [...LTR...]|[...RTL...]
4425 // Alternate cursor pos: ^
4426 // In which case we need to display an alternate cursor for the RTL text.
4428 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4429 alternatePosition.y = infoAlt.mPosition.y;
4430 alternatePositionValid = true;
4432 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4434 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4435 // Text: |[...RTL...] [...LTR....]
4437 // Alternate cursor pos: ^
4438 // In which case we need to display an alternate cursor for the RTL text.
4440 alternatePosition.x = infoAlt.mPosition.x;
4441 alternatePosition.y = infoAlt.mPosition.y;
4442 alternatePositionValid = true;
4447 // 2. When the cursor is at the end of the text,
4448 // and we have multi-directional text,
4449 // we can also consider showing mulitple cursors.
4450 // The rule here is:
4451 // If first and last characters on row are different
4452 // Directions, then two cursors need to be displayed.
4454 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4456 // The last character's direction is differernt than the first one of current paragraph.
4459 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4461 if(info.mIsRightToLeftCharacter)
4463 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4464 // Text: [...LTR...]|[...RTL...]
4466 // Alternate cursor pos: ^
4467 // In which case we need to display an alternate cursor for the RTL text, this cursor
4468 // should be at the end of the given line.
4470 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4471 alternatePosition.y = infoStart.mPosition.y;
4472 alternatePositionValid = true;
4474 else if(!info.mIsRightToLeftCharacter) // starting RTL
4476 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4477 // Text: |[...RTL...] [...LTR....]
4479 // Alternate cursor pos: ^
4480 // In which case we need to display an alternate cursor for the RTL text.
4482 alternatePosition.x = infoStart.mPosition.x;
4483 alternatePosition.y = infoStart.mPosition.y;
4484 alternatePositionValid = true;
4489 } // characterPosition > 0
4493 // If the character table is void, place the cursor accordingly the text alignment.
4494 const Vector3& size = GetControlSize();
4496 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4497 float alignmentOffset = 0.f;
4499 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4500 if( alignment & Toolkit::Alignment::HorizontalLeft )
4502 alignmentOffset = 0.f;
4504 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4506 alignmentOffset = 0.5f * ( size.width );
4508 else if( alignment & Toolkit::Alignment::HorizontalRight )
4510 alignmentOffset = size.width;
4513 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4514 cursorPosition.x = alignmentOffset;
4516 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4517 if( alignment & Toolkit::Alignment::VerticalTop )
4519 cursorPosition.y = mLineHeight;
4521 else if( alignment & Toolkit::Alignment::VerticalCenter )
4523 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4525 else if( alignment & Toolkit::Alignment::VerticalBottom )
4527 cursorPosition.y = size.height;
4531 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4532 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4534 if( alternatePositionValid )
4536 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4537 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4540 return cursorPosition;
4543 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4545 // scan string from current position to beginning of current line to note direction of line
4546 while( logicalPosition )
4549 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4556 return logicalPosition;
4559 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4561 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4563 while( logicalPosition )
4566 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4573 return logicalPosition;
4576 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4580 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4583 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4585 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4586 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4588 min = Vector2::ZERO;
4589 max = Vector2(0.0f, mLineHeight);
4593 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4595 // Initializes the min and max position.
4596 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4597 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4601 // 1) Find the line where the character is laid-out.
4602 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4603 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4606 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4608 // Index within the whole text to the last character of the current line.
4609 std::size_t lastCharacterOfLine = 0u;
4611 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4612 if( lineNextIt != lineEndIt )
4614 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4618 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4621 // Check if the given chracter position is within the line.
4622 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4624 // 2) Get the row rect of all laid-out characters on the line.
4626 // Need to scan all characters of the line because they are in the logical position.
4627 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4628 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4632 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4634 min.x = std::min( min.x, characterInfo.mPosition.x );
4635 min.y = std::min( min.y, characterInfo.mPosition.y );
4636 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4637 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4644 return Size( max.x - min.x, max.y - min.y );
4647 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4649 Actor popUpPanel = mPopupPanel.GetRootActor();
4651 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4657 Dali::Actor parent( touchedActor.GetParent() );
4661 return WasTouchedCheck( parent );
4668 void TextInput::StartMonitoringStageForTouch()
4670 Stage stage = Stage::GetCurrent();
4671 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4674 void TextInput::EndMonitoringStageForTouch()
4676 Stage stage = Stage::GetCurrent();
4677 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4680 void TextInput::OnStageTouched(const TouchEvent& event)
4682 if( event.GetPointCount() > 0 )
4684 if ( TouchPoint::Down == event.GetPoint(0).state )
4686 const Actor touchedActor(event.GetPoint(0).hitActor);
4688 bool popUpShown( false );
4690 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4695 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4697 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4699 EndMonitoringStageForTouch();
4700 HidePopup( true, false );
4703 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4705 EndMonitoringStageForTouch();
4706 ShowGrabHandleAndSetVisibility( false );
4712 void TextInput::SelectText(std::size_t start, std::size_t end)
4714 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4715 IsGrabHandleEnabled()?"true":"false",
4716 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4717 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4718 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4720 StartMonitoringStageForTouch();
4722 if ( mEditModeActive ) // Only allow text selection when in edit mode
4724 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4725 mSelectingText = true;
4727 std::size_t selectionStartPosition = std::min( start, end );
4729 // Hide grab handle when selecting.
4730 ShowGrabHandleAndSetVisibility( false );
4732 if( start != end ) // something to select
4734 SetCursorVisibility( false );
4735 StopCursorBlinkTimer();
4737 CreateSelectionHandles(start, end);
4740 const TextStyle oldInputStyle( mInputStyle );
4741 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4743 if( oldInputStyle != mInputStyle )
4745 // Updates the line height accordingly with the input style.
4748 EmitStyleChangedSignal();
4754 mSelectingText = false;
4758 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4760 MarkupProcessor::StyledTextArray currentSelectedText;
4762 if ( IsTextSelected() )
4764 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4765 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4767 for(; it != end; ++it)
4769 MarkupProcessor::StyledText& styledText( *it );
4770 currentSelectedText.push_back( styledText );
4773 return currentSelectedText;
4776 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4778 const std::size_t beginIndex = std::min( begin, end );
4779 const std::size_t endIndex = std::max( begin, end );
4782 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4784 // Create a styled text array used to replace the text into the text-view.
4785 MarkupProcessor::StyledTextArray text;
4786 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4788 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4789 GetTextLayoutInfo();
4791 if( IsScrollEnabled() )
4793 // Need to set the scroll position as the text's size may have changed.
4794 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4797 ShowGrabHandleAndSetVisibility( false );
4803 // Set Handle positioning as the new style may have repositioned the characters.
4804 SetSelectionHandlePosition(HandleOne);
4805 SetSelectionHandlePosition(HandleTwo);
4808 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4810 // Just hide the grab handle when keyboard is hidden.
4811 if (!keyboardShown )
4813 ShowGrabHandleAndSetVisibility( false );
4815 // If the keyboard is not now being shown, then hide the popup panel
4816 mPopupPanel.Hide( true );
4820 // Removes highlight and resumes edit mode state
4821 void TextInput::RemoveHighlight( bool hidePopup )
4823 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4825 if ( mHighlightMeshActor )
4827 if ( mSelectionHandleOne )
4829 mActiveLayer.Remove( mSelectionHandleOne );
4830 mSelectionHandleOne.Reset();
4831 mSelectionHandleOneOffset.x = 0.0f;
4833 if ( mSelectionHandleTwo )
4835 mActiveLayer.Remove( mSelectionHandleTwo );
4836 mSelectionHandleTwo.Reset();
4837 mSelectionHandleTwoOffset.x = 0.0f;
4840 mNewHighlightInfo.mQuadList.clear();
4842 Self().Remove( mHighlightMeshActor );
4844 SetCursorVisibility( true );
4845 StartCursorBlinkTimer();
4847 mHighlightMeshActor.Reset();
4848 // NOTE: We cannot dereference mHighlightMesh, due
4849 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4857 mSelectionHandleOnePosition = 0;
4858 mSelectionHandleTwoPosition = 0;
4861 void TextInput::CreateHighlight()
4863 if ( !mHighlightMeshActor )
4865 mMeshData = MeshData( );
4866 mMeshData.SetHasNormals( true );
4868 mCustomMaterial = Material::New("CustomMaterial");
4869 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4871 mMeshData.SetMaterial( mCustomMaterial );
4873 mHighlightMesh = Mesh::New( mMeshData );
4875 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4876 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4877 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4878 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4879 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4880 mHighlightMeshActor.SetAffectedByLighting(false);
4882 Self().Add(mHighlightMeshActor);
4887 bool TextInput::CopySelectedTextToClipboard()
4889 mCurrentCopySelecton.clear();
4891 mCurrentCopySelecton = GetSelectedText();
4893 std::string stringToStore;
4895 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4896 * a marked up string.
4898 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4899 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4901 bool success = mClipboard.SetItem( stringToStore );
4905 void TextInput::PasteText( const Text& text )
4907 // Update Flag, indicates whether to update the text-input contents or not.
4908 // Any key stroke that results in a visual change of the text-input should
4909 // set this flag to true.
4910 bool update = false;
4911 if( mHighlightMeshActor )
4913 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4914 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4916 ImfManager imfManager = ImfManager::Get();
4919 imfManager.SetCursorPosition( mCursorPosition );
4920 imfManager.NotifyCursorPosition();
4922 DeleteHighlightedText( true );
4926 bool textExceedsMaximunNumberOfCharacters = false;
4927 bool textExceedsBoundary = false;
4929 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4931 mCursorPosition += insertedStringLength;
4932 ImfManager imfManager = ImfManager::Get();
4935 imfManager.SetCursorPosition ( mCursorPosition );
4936 imfManager.NotifyCursorPosition();
4939 update = update || ( insertedStringLength > 0 );
4946 if( insertedStringLength < text.GetLength() )
4948 EmitMaxInputCharactersReachedSignal();
4951 if( textExceedsBoundary )
4953 EmitInputTextExceedsBoundariesSignal();
4957 void TextInput::SetTextDirection()
4959 // Put the cursor to the right if we are empty and an RTL language is being used.
4960 if ( mStyledText.empty() )
4962 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4964 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4965 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4967 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4968 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4970 int alignment( mDisplayedTextView.GetTextAlignment() &
4971 ( Toolkit::Alignment::VerticalTop |
4972 Toolkit::Alignment::VerticalCenter |
4973 Toolkit::Alignment::VerticalBottom |
4974 Toolkit::Alignment::HorizontalCenter ) );
4975 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4977 // If our alignment is in the center, then do not change.
4978 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4980 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4983 // If our justification is in the center, then do not change.
4984 if ( justification != Toolkit::TextView::Center )
4986 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4989 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4990 mDisplayedTextView.SetLineJustification( justification );
4994 void TextInput::UpdateLineHeight()
4996 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4997 mLineHeight = font.GetLineHeight();
4999 // 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.
5001 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5003 if( !mExceedEnabled || shrink )
5005 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5009 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5011 // VCC check if we need do this in the visual order ...
5012 std::size_t position = 0u;
5014 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5020 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5022 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5024 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5030 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5031 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5033 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5039 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5044 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5051 void TextInput::SetSortModifier( float depthOffset )
5053 if(mDisplayedTextView)
5055 mDisplayedTextView.SetSortModifier(depthOffset);
5059 void TextInput::SetSnapshotModeEnabled( bool enable )
5061 if(mDisplayedTextView)
5063 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5067 bool TextInput::IsSnapshotModeEnabled() const
5069 bool snapshotEnabled = false;
5071 if(mDisplayedTextView)
5073 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5076 return snapshotEnabled;
5079 void TextInput::SetMarkupProcessingEnabled( bool enable )
5081 mMarkUpEnabled = enable;
5084 bool TextInput::IsMarkupProcessingEnabled() const
5086 return mMarkUpEnabled;
5089 void TextInput::SetScrollEnabled( bool enable )
5091 if( mDisplayedTextView )
5093 mDisplayedTextView.SetScrollEnabled( enable );
5098 // Don't set cursor's and handle's visibility to false if they are outside the
5099 // boundaries of the text-input.
5100 mIsCursorInScrollArea = true;
5101 mIsGrabHandleInScrollArea = true;
5102 if( mSelectionHandleOne && mSelectionHandleTwo )
5104 mSelectionHandleOne.SetVisible( true );
5105 mSelectionHandleTwo.SetVisible( true );
5107 if( mHighlightMeshActor )
5109 mHighlightMeshActor.SetVisible( true );
5115 bool TextInput::IsScrollEnabled() const
5117 bool scrollEnabled = false;
5119 if( mDisplayedTextView )
5121 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5124 return scrollEnabled;
5127 void TextInput::SetScrollPosition( const Vector2& position )
5129 if( mDisplayedTextView )
5131 mDisplayedTextView.SetScrollPosition( position );
5135 Vector2 TextInput::GetScrollPosition() const
5137 Vector2 scrollPosition;
5139 if( mDisplayedTextView )
5141 scrollPosition = mDisplayedTextView.GetScrollPosition();
5144 return scrollPosition;
5147 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5149 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5150 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5151 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5153 // Add style to the new input text.
5154 MarkupProcessor::StyledTextArray textToInsert;
5155 for( std::size_t i = 0; i < insertedStringLength; ++i )
5157 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5158 textToInsert.push_back( newStyledCharacter );
5161 //Insert text to the TextView.
5162 const bool emptyTextView = mStyledText.empty();
5163 if( emptyTextView && mPlaceHolderSet )
5165 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5166 mDisplayedTextView.SetText( textToInsert );
5170 if( 0 == numberOfCharactersToReplace )
5172 mDisplayedTextView.InsertTextAt( position, textToInsert );
5176 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5179 mPlaceHolderSet = false;
5181 if( textToInsert.empty() )
5183 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5184 GetTextLayoutInfo();
5188 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5189 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5192 textExceedsBoundary = false;
5194 if( !mExceedEnabled )
5196 const Vector3& size = GetControlSize();
5198 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5200 // If new text does not fit within TextView
5201 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5202 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5203 GetTextLayoutInfo();
5204 textExceedsBoundary = true;
5205 insertedStringLength = 0;
5208 if( textExceedsBoundary )
5210 // Add the part of the text which fits on the text-input.
5212 // Split the text which doesn't fit in two halves.
5213 MarkupProcessor::StyledTextArray firstHalf;
5214 MarkupProcessor::StyledTextArray secondHalf;
5215 SplitText( textToInsert, firstHalf, secondHalf );
5217 // Clear text. This text will be filled with the text inserted.
5218 textToInsert.clear();
5220 // Where to insert the text.
5221 std::size_t positionToInsert = position;
5223 bool end = text.GetLength() <= 1;
5226 // Insert text and check ...
5227 const std::size_t textLength = firstHalf.size();
5228 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5229 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5231 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5233 // Inserted text doesn't fit.
5235 // Remove inserted text
5236 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5237 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5239 // The iteration finishes when only one character doesn't fit.
5240 end = textLength <= 1;
5244 // Prepare next two halves for next iteration.
5245 MarkupProcessor::StyledTextArray copyText = firstHalf;
5246 SplitText( copyText, firstHalf, secondHalf );
5253 // store text to be inserted in mStyledText.
5254 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5256 // Increase the inserted characters counter.
5257 insertedStringLength += textLength;
5259 // Prepare next two halves for next iteration.
5260 MarkupProcessor::StyledTextArray copyText = secondHalf;
5261 SplitText( copyText, firstHalf, secondHalf );
5263 // Update where next text has to be inserted
5264 positionToInsert += textLength;
5270 if( textToInsert.empty() && emptyTextView )
5272 // No character has been added and the text-view was empty.
5273 // Show the placeholder text.
5274 ShowPlaceholderText( mStyledPlaceHolderText );
5278 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5279 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5280 mPlaceHolderSet = false;
5283 return insertedStringLength;
5286 void TextInput::GetTextLayoutInfo()
5288 if( mStyledText.empty() )
5290 // The text-input has no text, clear the text-view's layout info.
5291 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5295 if( mDisplayedTextView )
5297 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5301 // There is no text-view.
5302 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5307 void TextInput::SetOffsetFromText( const Vector4& offset )
5309 mPopupOffsetFromText = offset;
5312 const Vector4& TextInput::GetOffsetFromText() const
5314 return mPopupOffsetFromText;
5317 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5319 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5323 TextInput& textInputImpl( GetImpl( textInput ) );
5325 switch ( propertyIndex )
5327 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5329 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5332 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5334 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5337 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5339 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5342 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5344 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5347 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5349 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5352 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5354 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5357 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5359 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5362 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5364 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5367 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5369 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5372 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5374 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5377 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5379 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5382 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5384 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5387 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5389 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5392 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5394 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5397 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5399 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5402 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5404 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5410 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5412 Property::Value value;
5414 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5418 TextInput& textInputImpl( GetImpl( textInput ) );
5420 switch ( propertyIndex )
5422 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5424 value = textInputImpl.GetMaterialDiffuseColor();
5427 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5429 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5432 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5434 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5437 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5439 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5442 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5444 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5447 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5449 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5452 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5454 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5457 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5459 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5462 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5464 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5467 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5469 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5472 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5474 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5477 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5479 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5482 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5484 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5487 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5489 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5492 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5494 value = textInputImpl.GetOffsetFromText();
5497 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5499 value = textInputImpl.mCursor.GetCurrentColor();
5506 void TextInput::EmitStyleChangedSignal()
5508 // emit signal if input style changes.
5509 Toolkit::TextInput handle( GetOwner() );
5510 mStyleChangedSignal.Emit( handle, mInputStyle );
5513 void TextInput::EmitTextModified()
5515 // emit signal when text changes.
5516 Toolkit::TextInput handle( GetOwner() );
5517 mTextModifiedSignal.Emit( handle );
5521 void TextInput::EmitMaxInputCharactersReachedSignal()
5523 // emit signal if max characters is reached during text input.
5524 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5526 Toolkit::TextInput handle( GetOwner() );
5527 mMaxInputCharactersReachedSignal.Emit( handle );
5530 void TextInput::EmitInputTextExceedsBoundariesSignal()
5532 // Emit a signal when the input text exceeds the boundaries of the text input.
5534 Toolkit::TextInput handle( GetOwner() );
5535 mInputTextExceedBoundariesSignal.Emit( handle );
5538 } // namespace Internal
5540 } // namespace Toolkit