2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
25 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/key-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/property-notification.h>
32 #include <dali/integration-api/debug.h>
33 #include <dali/public-api/images/resource-image.h>
36 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
37 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
38 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
39 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
51 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
52 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
53 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size
54 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
55 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
56 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // Used for Selection highlight
58 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
59 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
60 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
61 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
62 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
64 const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval
65 const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line.
66 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f ); ///< 1. Highlight rendered (z-offset).
67 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f ); ///< 2. Text rendered (z-offset).
68 const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset.
70 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset.
71 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset
72 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset
73 const float TOP_HANDLE_TOP_OFFSET( 34.0f); ///< Offset between top handle and cutCopyPaste pop-up
74 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f); ///< Offset between bottom handle and cutCopyPaste pop-up
75 const float CURSOR_THICKNESS(4.0f);
76 const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle.
77 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
79 const std::string NEWLINE( "\n" );
81 const TextStyle DEFAULT_TEXT_STYLE;
83 const unsigned int SCROLL_TICK_INTERVAL = 50u;
84 const float SCROLL_THRESHOLD = 10.f;
85 const float SCROLL_SPEED = 15.f;
88 * Selection state enumeration (FSM)
92 SelectionNone, ///< Currently not encountered selected section.
93 SelectionStarted, ///< Encountered selected section
94 SelectionFinished ///< Finished selected section
97 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
99 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
103 if( ( *it ).mIsVisible )
105 return --cursorPosition;
114 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
116 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
118 if( ( *it ).mIsVisible )
120 return cursorPosition;
126 return cursorPosition;
130 * Whether the given position plus the cursor size offset is inside the given boundary.
132 * @param[in] position The given position.
133 * @param[in] cursorSize The cursor size.
134 * @param[in] controlSize The given boundary.
136 * @return whether the given position is inside the given boundary.
138 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
140 return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
141 ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
142 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
143 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
147 * Splits a text in two halves.
149 * If the text's number of characters is odd, firstHalf has one more character.
151 * @param[in] text The text to be split.
152 * @param[out] firstHalf The first half of the text.
153 * @param[out] secondHalf The second half of the text.
155 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
156 Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
157 Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
162 const std::size_t textLength = text.size();
163 const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
165 firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
166 secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
169 } // end of namespace
177 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
178 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
179 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
180 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
181 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
182 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
183 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
184 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
185 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
186 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
187 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
188 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
189 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
190 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
191 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
192 const Property::Index TextInput::CURSOR_COLOR_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
203 const char* const SIGNAL_START_INPUT = "start-input";
204 const char* const SIGNAL_END_INPUT = "end-input";
205 const char* const SIGNAL_STYLE_CHANGED = "style-changed";
206 const char* const SIGNAL_MAX_INPUT_CHARACTERS_REACHED = "max-input-characters-reached";
207 const char* const SIGNAL_TOOLBAR_DISPLAYED = "toolbar-displayed";
208 const char* const SIGNAL_TEXT_EXCEED_BOUNDARIES = "text-exceed-boundaries";
212 return Toolkit::TextInput::New();
215 TypeRegistration typeRegistration( typeid( Toolkit::TextInput ), typeid( Toolkit::Control ), Create );
217 SignalConnectorType signalConnector1( typeRegistration, SIGNAL_START_INPUT, &TextInput::DoConnectSignal );
218 SignalConnectorType signalConnector2( typeRegistration, SIGNAL_END_INPUT, &TextInput::DoConnectSignal );
219 SignalConnectorType signalConnector3( typeRegistration, SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal );
220 SignalConnectorType signalConnector4( typeRegistration, SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
221 SignalConnectorType signalConnector5( typeRegistration, SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal );
222 SignalConnectorType signalConnector6( typeRegistration, SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal );
226 PropertyRegistration property1( typeRegistration, "highlight-color", Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
227 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color", Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
228 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
229 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
230 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
231 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
232 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color", Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
233 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color", Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
234 PropertyRegistration property9( typeRegistration, "cut-button-position-priority", Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
235 PropertyRegistration property10( typeRegistration, "copy-button-position-priority", Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
236 PropertyRegistration property11( typeRegistration, "paste-button-position-priority", Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
237 PropertyRegistration property12( typeRegistration, "select-button-position-priority", Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
238 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority", Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
239 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority", Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
246 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
248 QuadCoordinates quad(x1, y1, x2, y2);
249 mQuadList.push_back( quad );
252 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
254 for(std::size_t i = 0;i < mQuadList.size(); i++)
256 QuadCoordinates& quad = mQuadList[i];
258 quad.min.Clamp(min, max);
259 quad.max.Clamp(min, max);
263 // [TextInput] ////////////////////////////////////////////////////////////////
265 Dali::Toolkit::TextInput TextInput::New()
267 // Create the implementation
268 TextInputPtr textInput(new TextInput());
269 // Pass ownership to CustomActor via derived handle
270 Dali::Toolkit::TextInput handle(*textInput);
271 handle.SetName( "TextInput");
273 textInput->Initialize();
277 TextInput::TextInput()
278 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
283 mDisplayedTextView(),
284 mStyledPlaceHolderText(),
285 mMaxStringLength( DEFAULT_MAX_SIZE ),
286 mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
287 mCursorPosition( 0 ),
288 mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
289 mIsSelectionHandleOneFlipped( false ),
290 mIsSelectionHandleTwoFlipped( false ),
291 mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
292 mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
293 mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
294 mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
295 mSelectionHandleOnePosition( 0 ),
296 mSelectionHandleTwoPosition( 0 ),
298 mPreEditStartPosition( 0 ),
299 mPreEditLength ( 0 ),
300 mNumberOfSurroundingCharactersDeleted( 0 ),
301 mTouchStartTime( 0 ),
303 mCurrentCopySelecton(),
306 mScrollDisplacement(),
307 mCurrentHandlePosition(),
308 mCurrentSelectionId(),
309 mCurrentSelectionHandlePosition(),
310 mRequestedSelection( 0, 0 ),
311 mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
312 mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
314 mMaterialColor( LIGHTBLUE ),
315 mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
316 mOverrideAutomaticAlignment( false ),
317 mCursorRTLEnabled( false ),
318 mClosestCursorPositionEOL ( false ),
319 mCursorBlinkStatus( true ),
320 mCursorVisibility( false ),
321 mGrabHandleVisibility( false ),
322 mIsCursorInScrollArea( true ),
323 mIsGrabHandleInScrollArea( true ),
324 mEditModeActive( false ),
325 mEditOnTouch( true ),
326 mTextSelection( true ),
327 mExceedEnabled( true ),
328 mGrabHandleEnabled( true ),
329 mIsSelectionHandleFlipEnabled( true ),
330 mPreEditFlag( false ),
331 mIgnoreCommitFlag( false ),
332 mIgnoreFirstCommitFlag( false ),
333 mSelectingText( false ),
334 mPreserveCursorPosition( false ),
335 mSelectTextOnCommit( false ),
336 mUnderlinedPriorToPreEdit ( false ),
337 mCommitByKeyInput( false ),
338 mPlaceHolderSet( false ),
339 mMarkUpEnabled( false )
341 // Updates the line height accordingly with the input style.
345 TextInput::~TextInput()
347 StopCursorBlinkTimer();
352 std::string TextInput::GetText() const
356 // Return text-view's text only if the text-input's text is not empty
357 // in order to not to return the placeholder text.
358 if( !mStyledText.empty() )
360 text = mDisplayedTextView.GetText();
366 std::string TextInput::GetMarkupText() const
368 std::string markupString;
369 MarkupProcessor::GetMarkupString( mStyledText, markupString );
374 void TextInput::ShowPlaceholderText( const MarkupProcessor::StyledTextArray& stylePlaceHolderText )
376 mDisplayedTextView.SetText( stylePlaceHolderText );
377 mPlaceHolderSet = true;
378 mDisplayedTextView.SetScrollPosition( Vector2( 0.0f,0.0f ) );
381 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
383 // Get the placeholder styled text array from the markup string.
384 MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
385 if( mStyledText.empty() )
387 ShowPlaceholderText( mStyledPlaceHolderText );
391 std::string TextInput::GetPlaceholderText()
393 // Traverses the styled placeholder array getting only the text.
394 // Note that for some languages a 'character' could be represented by more than one 'char'
396 std::string placeholderText;
397 for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
399 placeholderText.append( (*it).mText.GetText() );
402 return placeholderText ;
405 void TextInput::SetInitialText(const std::string& initialText)
407 DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
409 if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
411 mPreEditFlag = false;
412 mIgnoreCommitFlag = true;
415 SetText( initialText );
416 PreEditReset( false ); // Reset keyboard as text changed
419 void TextInput::SetText(const std::string& initialText)
421 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
423 GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
425 if( mStyledText.empty() )
427 ShowPlaceholderText( mStyledPlaceHolderText );
431 mDisplayedTextView.SetText( mStyledText );
432 mPlaceHolderSet = false;
437 mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
439 ImfManager imfManager = ImfManager::Get();
442 imfManager.SetCursorPosition( mCursorPosition );
443 imfManager.SetSurroundingText( initialText );
444 imfManager.NotifyCursorPosition();
447 if( IsScrollEnabled() )
449 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
452 ShowGrabHandleAndSetVisibility( false );
461 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
463 DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
465 mDisplayedTextView.SetText( styleText );
466 mPlaceHolderSet = false;
468 // If text alignment hasn't been manually set by application developer, then we
469 // automatically determine the alignment based on the content of the text i.e. what
470 // language the text begins with.
471 // TODO: This should determine different alignments for each line (broken by '\n') of text.
472 if(!mOverrideAutomaticAlignment)
474 // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
475 bool leftToRight(true);
477 if( !styleText.empty() )
479 bool breakOut(false);
481 for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
483 const Text& text = textIter->mText;
485 for( std::size_t i = 0; i < text.GetLength(); ++i )
487 Character character( text[i] );
488 if( character.GetCharacterDirection() != Character::Neutral )
490 leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
498 // Based on this direction, either left or right align text if not manually set by application developer.
499 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
500 ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
501 Toolkit::Alignment::VerticalTop ) );
502 mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
508 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
510 mMaxStringLength = maxChars;
513 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
515 DALI_ASSERT_DEBUG( maxLines > 0 )
519 mNumberOflinesLimit = maxLines;
523 std::size_t TextInput::GetNumberOfLinesLimit() const
525 return mNumberOflinesLimit;
528 std::size_t TextInput::GetNumberOfCharacters() const
530 return mStyledText.size();
534 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
536 mMaterialColor = color;
537 if ( mCustomMaterial )
539 mCustomMaterial.SetDiffuseColor( mMaterialColor );
540 mMeshData.SetMaterial( mCustomMaterial );
544 const Vector4& TextInput::GetMaterialDiffuseColor() const
546 return mMaterialColor;
551 Toolkit::TextInput::InputSignalType& TextInput::InputStartedSignal()
553 return mInputStartedSignal;
556 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
558 return mInputFinishedSignal;
561 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
563 return mCutAndPasteToolBarDisplayed;
566 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
568 return mStyleChangedSignal;
571 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
573 return mTextModifiedSignal;
576 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
578 return mMaxInputCharactersReachedSignal;
581 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
583 return mInputTextExceedBoundariesSignal;
586 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
588 Dali::BaseHandle handle( object );
590 bool connected( true );
591 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( handle );
593 if( 0 == strcmp( signalName.c_str(), SIGNAL_START_INPUT ) )
595 textInput.InputStartedSignal().Connect( tracker, functor );
597 else if( 0 == strcmp( signalName.c_str(), SIGNAL_END_INPUT ) )
599 textInput.InputFinishedSignal().Connect( tracker, functor );
601 else if( 0 == strcmp( signalName.c_str(), SIGNAL_STYLE_CHANGED ) )
603 textInput.StyleChangedSignal().Connect( tracker, functor );
605 else if( 0 == strcmp( signalName.c_str(), SIGNAL_MAX_INPUT_CHARACTERS_REACHED ) )
607 textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
609 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TOOLBAR_DISPLAYED ) )
611 textInput.CutAndPasteToolBarDisplayedSignal().Connect( tracker, functor );
613 else if( 0 == strcmp( signalName.c_str(), SIGNAL_TEXT_EXCEED_BOUNDARIES ) )
615 textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
619 // signalName does not match any signal
626 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
630 // update line height before calculate the actual position.
635 if( setCursorOnTouchPoint )
637 // Sets the cursor position for the given touch point.
638 ReturnClosestIndex( touchPoint, mCursorPosition );
640 // Creates the grab handle.
641 if( IsGrabHandleEnabled() )
643 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
647 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
648 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
649 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
650 ShowGrabHandleAndSetVisibility( true );
652 // Scrolls the text-view if needed.
653 if( IsScrollEnabled() )
655 ScrollTextViewToMakeCursorVisible( cursorPosition );
661 mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
673 bool TextInput::IsEditable() const
675 return mEditModeActive;
678 void TextInput::SetEditOnTouch( bool editOnTouch )
680 mEditOnTouch = editOnTouch;
683 bool TextInput::IsEditOnTouch() const
688 void TextInput::SetTextSelectable( bool textSelectable )
690 mTextSelection = textSelectable;
693 bool TextInput::IsTextSelectable() const
695 return mTextSelection;
698 bool TextInput::IsTextSelected() const
700 return mHighlightMeshActor;
703 void TextInput::DeSelectText()
710 void TextInput::SetGrabHandleImage(Dali::Image image )
714 CreateGrabHandle(image);
718 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
720 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
724 mCursor.SetImage( image );
725 mCursor.SetNinePatchBorder( border );
729 Vector3 TextInput::GetSelectionHandleSize()
731 return DEFAULT_SELECTION_HANDLE_SIZE;
734 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
736 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
740 mCursorRTL.SetImage( image);
741 mCursorRTL.SetNinePatchBorder( border );
745 void TextInput::EnableGrabHandle(bool toggle)
747 // enables grab handle with will in turn de-activate magnifier
748 mGrabHandleEnabled = toggle;
751 bool TextInput::IsGrabHandleEnabled()
753 // if false then magnifier will be shown instead.
754 return mGrabHandleEnabled;
757 void TextInput::EnableSelectionHandleFlip( bool toggle )
759 // Deprecated function. To be removed.
760 mIsSelectionHandleFlipEnabled = toggle;
763 bool TextInput::IsSelectionHandleFlipEnabled()
765 // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
769 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
771 // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
772 Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
773 const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
775 mSelectionHandleFlipMargin = margin;
778 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
780 // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
781 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
783 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
784 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
786 const Vector4 boundary( originX,
788 originX + boundingRectangle.width,
789 originY + boundingRectangle.height );
791 mBoundingRectangleWorldCoordinates = boundary;
794 const Rect<float> TextInput::GetBoundingRectangle() const
796 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
798 const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
799 const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
801 Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
806 const Vector4& TextInput::GetSelectionHandleFlipMargin()
808 return mSelectionHandleFlipMargin;
811 void TextInput::SetTextColor( const Vector4& color )
813 mDisplayedTextView.SetColor( color );
816 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
818 if( style != mInputStyle )
821 bool emitSignal = false;
823 // mask: modify style according to mask, if different emit signal.
824 const TextStyle oldInputStyle( mInputStyle );
826 // Copy the new style.
827 mInputStyle.Copy( style, mask );
829 // if style has changed, emit signal.
830 if( oldInputStyle != mInputStyle )
835 // Updates the line height accordingly with the input style.
838 // Changing font point size will require the cursor to be re-sized
843 EmitStyleChangedSignal();
848 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
850 if ( IsTextSelected() )
852 const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
853 const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
855 if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
857 ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
860 // Keeps the old style to be compared with the new one.
861 const TextStyle oldInputStyle( mInputStyle );
863 // Copy only those parameters from the style which are set in the mask.
864 mInputStyle.Copy( style, mask );
866 if( mInputStyle != oldInputStyle )
868 // Updates the line height accordingly with the input style.
871 EmitStyleChangedSignal();
876 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
878 if( !mStyledText.empty() )
880 ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
884 TextStyle TextInput::GetStyleAtCursor() const
888 if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
890 DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
891 style = mStyledText.at( mCursorPosition-1 ).mStyle;
897 if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 )
899 Dali::Font defaultFont = Dali::Font::New();
900 style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
907 TextStyle TextInput::GetStyleAt( std::size_t position ) const
909 DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
911 if( position >= mStyledText.size() )
913 position = mStyledText.size() - 1;
916 return mStyledText.at( position ).mStyle;
919 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
921 mDisplayedTextView.SetTextAlignment( align );
922 mOverrideAutomaticAlignment = true;
925 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
927 mDisplayedTextView.SetLineJustification( justification );
928 mOverrideAutomaticAlignment = true;
931 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
933 mDisplayedTextView.SetFadeBoundary( fadeBoundary );
936 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
938 return mDisplayedTextView.GetFadeBoundary();
941 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
943 return mDisplayedTextView.GetTextAlignment();
946 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
948 mDisplayedTextView.SetMultilinePolicy( policy );
951 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
953 return mDisplayedTextView.GetMultilinePolicy();
956 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
958 mDisplayedTextView.SetWidthExceedPolicy( policy );
961 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
963 return mDisplayedTextView.GetWidthExceedPolicy();
966 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
968 mDisplayedTextView.SetHeightExceedPolicy( policy );
971 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
973 return mDisplayedTextView.GetHeightExceedPolicy();
976 void TextInput::SetExceedEnabled( bool enable )
978 mExceedEnabled = enable;
981 bool TextInput::GetExceedEnabled() const
983 return mExceedEnabled;
986 void TextInput::SetBackground(Dali::Image image )
988 // TODO Should add this function and add public api to match.
991 bool TextInput::OnTouchEvent(const TouchEvent& event)
996 bool TextInput::OnKeyEvent(const KeyEvent& event)
998 switch( event.state )
1000 case KeyEvent::Down:
1002 return OnKeyDownEvent(event);
1008 return OnKeyUpEvent(event);
1020 void TextInput::OnKeyInputFocusGained()
1022 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1024 mEditModeActive = true;
1026 mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1028 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1030 // Updates the line height accordingly with the input style.
1033 // Connect the signals to use in text input.
1034 VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1035 VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1037 // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1040 GetTextLayoutInfo();
1043 SetCursorVisibility( true );
1044 StartCursorBlinkTimer();
1046 Toolkit::TextInput handle( GetOwner() );
1047 mInputStartedSignal.Emit( handle );
1049 ImfManager imfManager = ImfManager::Get();
1053 imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1055 // Notify that the text editing start.
1056 imfManager.Activate();
1058 // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1059 imfManager.SetRestoreAfterFocusLost( true );
1061 imfManager.SetCursorPosition( mCursorPosition );
1062 imfManager.NotifyCursorPosition();
1065 mClipboard = Clipboard::Get(); // Store handle to clipboard
1067 // Now in edit mode we can accept string to paste from clipboard
1068 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1071 notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1075 void TextInput::OnKeyInputFocusLost()
1077 DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1081 // If key input focus is lost, it removes the
1082 // underline from the last pre-edit text.
1083 RemovePreEditStyle();
1084 const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1085 InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1089 ImfManager imfManager = ImfManager::Get();
1092 // The text editing is finished. Therefore the imf manager don't have restore activation.
1093 imfManager.SetRestoreAfterFocusLost( false );
1095 // Notify that the text editing finish.
1096 imfManager.Deactivate();
1098 imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1100 // Disconnect signal used the text input.
1101 VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1103 Toolkit::TextInput handle( GetOwner() );
1104 mInputFinishedSignal.Emit( handle );
1105 mEditModeActive = false;
1106 mPreEditFlag = false;
1108 SetCursorVisibility( false );
1109 StopCursorBlinkTimer();
1111 ShowGrabHandleAndSetVisibility( false );
1114 // No longer in edit mode so do not want to receive string from clipboard
1115 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1118 notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1121 Clipboard clipboard = Clipboard::Get();
1124 clipboard.HideClipboard();
1128 void TextInput::OnControlStageConnection()
1130 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1132 if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1134 SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1138 void TextInput::CreateActiveLayer()
1140 Actor self = Self();
1141 mActiveLayer = Layer::New();
1142 mActiveLayer.SetName ( "ActiveLayerActor" );
1144 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1145 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1146 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1148 self.Add( mActiveLayer );
1149 mActiveLayer.RaiseToTop();
1152 void TextInput::OnInitialize()
1154 CreateTextViewActor();
1158 // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1159 // different positions depending on language)
1160 mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1161 mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1163 Actor self = Self();
1164 self.Add( mCursor );
1165 self.Add( mCursorRTL );
1167 mCursorVisibility = false;
1169 CreateActiveLayer(); // todo move this so layer only created when needed.
1171 // Assign names to image actors
1172 mCursor.SetName("mainCursor");
1173 mCursorRTL.SetName("rtlCursor");
1176 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1178 mDisplayedTextView.SetSize( targetSize );
1179 GetTextLayoutInfo();
1180 mActiveLayer.SetSize(targetSize);
1183 void TextInput::OnRelayout( const Vector2& size, ActorSizeContainer& container )
1185 Relayout( mDisplayedTextView, size, container );
1186 Relayout( mPopupPanel.GetRootActor(), size, container );
1188 GetTextLayoutInfo();
1193 Vector3 TextInput::GetNaturalSize()
1195 Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1197 if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1199 // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1200 naturalSize.height = mLineHeight;
1206 float TextInput::GetHeightForWidth( float width )
1208 float height = mDisplayedTextView.GetHeightForWidth( width );
1210 if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1212 // If the height is zero, it means there is no text. Let's return the cursor height.
1213 height = mLineHeight;
1219 /*end of Virtual methods from parent*/
1221 // Private Internal methods
1223 void TextInput::OnHandlePan(Actor actor, const PanGesture& gesture)
1225 switch (gesture.state)
1227 case Gesture::Started:
1228 // fall through so code not duplicated
1229 case Gesture::Continuing:
1231 if (actor == mGrabArea)
1233 SetCursorVisibility( true );
1234 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1235 MoveGrabHandle( gesture.displacement );
1236 HidePopup(); // Do not show popup whilst handle is moving
1238 else if (actor == mHandleOneGrabArea)
1240 // the displacement in PanGesture is affected by the actor's rotation.
1241 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1242 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1244 MoveSelectionHandle( HandleOne, gesture.displacement );
1246 mState = StateDraggingHandle;
1249 else if (actor == mHandleTwoGrabArea)
1251 // the displacement in PanGesture is affected by the actor's rotation.
1252 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1253 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1255 MoveSelectionHandle( HandleTwo, gesture.displacement );
1257 mState = StateDraggingHandle;
1263 case Gesture::Finished:
1265 // Revert back to non-pressed selection handle images
1266 if (actor == mGrabArea)
1268 mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1269 SetCursorVisibility( true );
1270 SetUpPopupSelection();
1273 if (actor == mHandleOneGrabArea)
1275 // the displacement in PanGesture is affected by the actor's rotation.
1276 mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1277 mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1279 mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1281 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1283 ShowPopupCutCopyPaste();
1285 if (actor == mHandleTwoGrabArea)
1287 // the displacement in PanGesture is affected by the actor's rotation.
1288 mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1289 mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1291 mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1293 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1295 ShowPopupCutCopyPaste();
1304 // Stop the flashing animation so easy to see when moved.
1305 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1307 if (touch.GetPoint(0).state == TouchPoint::Down)
1309 SetCursorVisibility( true );
1310 StopCursorBlinkTimer();
1312 else if (touch.GetPoint(0).state == TouchPoint::Up)
1314 SetCursorVisibility( true );
1315 StartCursorBlinkTimer();
1320 // selection handle one
1321 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1323 if (touch.GetPoint(0).state == TouchPoint::Down)
1325 mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1327 else if (touch.GetPoint(0).state == TouchPoint::Up)
1329 mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1334 // selection handle two
1335 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1337 if (touch.GetPoint(0).state == TouchPoint::Down)
1339 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1341 else if (touch.GetPoint(0).state == TouchPoint::Up)
1343 mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1348 void TextInput::OnDoubleTap(Dali::Actor actor, const Dali::TapGesture& tap)
1350 // If text exists then select nearest word.
1351 if ( !mStyledText.empty())
1355 ShowGrabHandleAndSetVisibility( false );
1360 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1361 // converts the pre-edit word being displayed to a committed word.
1362 if ( !mUnderlinedPriorToPreEdit )
1365 style.SetUnderline( false );
1366 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1368 mPreEditFlag = false;
1369 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1370 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1371 PreEditReset( false );
1373 mCursorPosition = 0;
1375 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1376 ReturnClosestIndex( tap.localPoint, mCursorPosition );
1378 std::size_t start = 0;
1379 std::size_t end = 0;
1380 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1382 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1384 ImfManager imfManager = ImfManager::Get();
1387 imfManager.SetCursorPosition ( mCursorPosition );
1388 imfManager.NotifyCursorPosition();
1391 if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1393 SelectText( start, end );
1394 ShowPopupCutCopyPaste();
1398 RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1399 HidePopup( false ); // Hide popup with setting to do auto show.
1400 SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1404 else if ( mClipboard && mClipboard.NumberOfItems() )
1406 ShowPopupCutCopyPaste();
1409 // If no text and clipboard empty then do nothing
1412 // TODO: Change the function name to be more general.
1413 void TextInput::OnTextTap(Dali::Actor actor, const Dali::TapGesture& tap)
1415 DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1416 , (mEditOnTouch)?"true":"false"
1417 , (mEditModeActive)?"true":"false");
1419 if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1424 if( mGrabArea == actor )
1426 if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1428 SetUpPopupSelection();
1438 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1440 // Initially don't create the grab handle.
1441 bool createGrabHandle = false;
1443 if ( !mEditModeActive )
1445 // update line height before calculate the actual position.
1448 // Only start edit mode if TextInput configured to edit on touch
1451 // Set the initial cursor position in the tap point.
1452 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1458 // Show the keyboard if it was hidden.
1459 if (!VirtualKeyboard::IsVisible())
1461 VirtualKeyboard::Show();
1464 // Reset keyboard as tap event has occurred.
1465 // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1466 PreEditReset( true );
1468 GetTextLayoutInfo();
1470 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1472 // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1474 ReturnClosestIndex(tap.localPoint, mCursorPosition );
1476 DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1478 // Notify keyboard so it can 're-capture' word for predictive text.
1479 // As we have done a reset, is this required, expect IMF keyboard to request this information.
1480 ImfManager imfManager = ImfManager::Get();
1483 imfManager.SetCursorPosition ( mCursorPosition );
1484 imfManager.NotifyCursorPosition();
1486 const TextStyle oldInputStyle( mInputStyle );
1488 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1492 // Create the grab handle.
1493 // Grab handle is created later.
1494 createGrabHandle = true;
1496 if( oldInputStyle != mInputStyle )
1498 // Updates the line height accordingly with the input style.
1501 EmitStyleChangedSignal();
1506 // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1507 // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1508 // otherwise the Grab handle will be shown when selecting.
1509 if ( createGrabHandle && IsGrabHandleEnabled() )
1511 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1512 bool altPositionValid; // Alternate cursor validity flag.
1513 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1514 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1516 if( altPositionValid )
1518 // Check which of the positions is the closest.
1519 if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) )
1521 cursorPosition = altPosition;
1527 mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1528 mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1529 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1530 ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1535 void TextInput::OnLongPress(Dali::Actor actor, const Dali::LongPressGesture& longPress)
1537 DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1539 // Ignore longpress if in selection mode already
1540 if( mHighlightMeshActor )
1545 if(longPress.state == Dali::Gesture::Started)
1547 // Start edit mode on long press
1548 if ( !mEditModeActive )
1553 // If text exists then select nearest word.
1554 if ( !mStyledText.empty())
1558 ShowGrabHandleAndSetVisibility( false );
1563 // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which
1564 // converts the pre-edit word being displayed to a committed word.
1565 if ( !mUnderlinedPriorToPreEdit )
1568 style.SetUnderline( false );
1569 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1571 mPreEditFlag = false;
1572 mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1573 // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1574 PreEditReset( false );
1576 mCursorPosition = 0;
1578 mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1579 ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1581 std::size_t start = 0;
1582 std::size_t end = 0;
1583 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1585 mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1587 ImfManager imfManager = ImfManager::Get();
1590 imfManager.SetCursorPosition ( mCursorPosition );
1591 imfManager.NotifyCursorPosition();
1594 SelectText( start, end );
1597 // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1598 if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1600 ShowPopupCutCopyPaste();
1605 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1607 const Text clipboardText( notifier.GetContent() );
1608 PasteText( clipboardText );
1610 SetCursorVisibility( true );
1611 StartCursorBlinkTimer();
1613 ShowGrabHandleAndSetVisibility( false );
1619 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1621 mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1623 const std::string& name = button.GetName();
1625 if(name == TextInputPopup::OPTION_SELECT_WORD)
1627 std::size_t start = 0;
1628 std::size_t end = 0;
1629 Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1631 SelectText( start, end );
1633 else if(name == TextInputPopup::OPTION_SELECT_ALL)
1635 SetCursorVisibility(false);
1636 StopCursorBlinkTimer();
1638 std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1639 std::size_t start = 0;
1641 SelectText( start, end );
1643 else if(name == TextInputPopup::OPTION_CUT)
1645 bool ret = CopySelectedTextToClipboard();
1649 DeleteHighlightedText( true );
1653 SetCursorVisibility( true );
1654 StartCursorBlinkTimer();
1658 else if(name == TextInputPopup::OPTION_COPY)
1660 CopySelectedTextToClipboard();
1664 SetCursorVisibility( true );
1665 StartCursorBlinkTimer();
1669 else if(name == TextInputPopup::OPTION_PASTE)
1671 const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0;
1673 PasteText(retrievedString);
1675 SetCursorVisibility( true );
1676 StartCursorBlinkTimer();
1678 ShowGrabHandleAndSetVisibility( false );
1682 else if(name == TextInputPopup::OPTION_CLIPBOARD)
1684 // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1685 // Hence pass the false parameter for signalFinished.
1686 HidePopup( true, false );
1687 mClipboard.ShowClipboard();
1693 bool TextInput::OnCursorBlinkTimerTick()
1696 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1697 if ( mCursorRTLEnabled )
1699 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1701 mCursorBlinkStatus = !mCursorBlinkStatus;
1706 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1708 popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1710 // Change Popup menu to Cut/Copy/Paste if text has been selected.
1711 if(mHighlightMeshActor && mState == StateEdit)
1713 ShowPopupCutCopyPaste();
1717 //FIXME this routine needs to be re-written as it contains too many branches.
1718 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1720 std::string keyName = event.keyPressedName;
1721 std::string keyString = event.keyPressed;
1723 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1725 // Do not consume "Tab" and "Escape" keys.
1726 if(keyName == "Tab" || keyName == "Escape")
1728 // Escape key to end the edit mode
1734 HidePopup(); // If Pop-up shown then hides it as editing text.
1736 // Update Flag, indicates whether to update the text-input contents or not.
1737 // Any key stroke that results in a visual change of the text-input should
1738 // set this flag to true.
1741 // Whether to scroll text to cursor position.
1742 // Scroll is needed always the cursor is updated and after the pre-edit is received.
1743 bool scroll = false;
1745 if (keyName == "Return")
1747 if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1749 bool preEditFlagPreviouslySet( mPreEditFlag );
1751 // replaces highlighted text with new line
1752 DeleteHighlightedText( false );
1754 mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1756 // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the
1757 // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1760 mCommitByKeyInput = true;
1763 // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1764 if ( preEditFlagPreviouslySet && !mPreEditFlag )
1766 mPreEditFlag = true;
1767 mIgnoreCommitFlag = false;
1777 else if ( keyName == "space" )
1779 if ( mHighlightMeshActor )
1781 // Some text is selected so erase it before adding space.
1782 DeleteHighlightedText( true );
1785 mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1787 // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the
1788 // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1791 mCommitByKeyInput = true;
1796 else if (keyName == "BackSpace")
1798 if ( mHighlightMeshActor )
1800 // Some text is selected so erase it
1801 DeleteHighlightedText( true );
1806 if ( mCursorPosition > 0 )
1808 DeleteCharacter( mCursorPosition );
1814 else if (keyName == "Right")
1819 else if (keyName == "Left")
1821 AdvanceCursor(true);
1824 else // event is a character
1826 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1827 if ( !keyString.empty() )
1829 // replaces highlighted text with new character
1830 DeleteHighlightedText( false );
1832 // Received key String
1833 mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1839 // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1840 // as this is a costly operation.
1846 if(update || scroll)
1848 if( IsScrollEnabled() )
1850 // Calculates the new cursor position (in actor coordinates)
1851 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1853 ScrollTextViewToMakeCursorVisible( cursorPosition );
1860 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1862 std::string keyName = event.keyPressedName;
1863 std::string keyString = event.keyPressed;
1865 DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1867 // The selected text become deselected when the key code is DALI_KEY_BACK.
1868 if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1877 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1878 const Vector3& cursorPositionTwo,
1879 bool altPositionValidOne,
1880 bool altPositionValidTwo,
1881 const Vector3& altPositionOne,
1882 const Vector3& altPositionTwo )
1884 // TODO VCC Valid for one line.
1885 // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1886 if( cursorPositionOne != cursorPositionTwo )
1888 if( cursorPositionOne.x < cursorPositionTwo.x )
1890 mSelectionHandleOneActualPosition = cursorPositionOne;
1891 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1895 mSelectionHandleOneActualPosition = cursorPositionTwo;
1896 mSelectionHandleTwoActualPosition = cursorPositionOne;
1901 mSelectionHandleOneActualPosition = cursorPositionOne;
1902 if( altPositionValidOne )
1904 if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1906 mSelectionHandleOneActualPosition = altPositionOne;
1909 if( altPositionValidTwo )
1911 if( altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1913 mSelectionHandleOneActualPosition = altPositionTwo;
1917 mSelectionHandleTwoActualPosition = cursorPositionTwo;
1918 if( altPositionValidTwo )
1920 if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1922 mSelectionHandleTwoActualPosition = altPositionTwo;
1925 if( altPositionValidOne )
1927 if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1929 mSelectionHandleTwoActualPosition = altPositionOne;
1935 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1937 // Updates the stored scroll position.
1938 mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1940 const Vector3& controlSize = GetControlSize();
1941 Size cursorSize( CURSOR_THICKNESS, 0.f );
1943 // Updates the cursor and grab handle position and visibility.
1944 if( mGrabHandle || mCursor )
1946 cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1948 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
1949 bool altPositionValid; // Alternate cursor validity flag.
1950 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1951 Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1953 if( altPositionValid )
1955 // Check which of the positions is the closest.
1956 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1958 cursorPosition = altPosition;
1962 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1964 mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1968 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1969 mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1974 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1975 mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1979 // Updates the selection handles and highlighted text position and visibility.
1980 if( mSelectionHandleOne && mSelectionHandleTwo )
1982 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
1983 bool altPositionValidOne; // Alternate cursor validity flag.
1984 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1985 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1987 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
1988 bool altPositionValidTwo; // Alternate cursor validity flag.
1989 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1990 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1992 // VCC TODO: This method is a hack for one line.
1993 ChooseRtlSelectionHandlePosition( cursorPositionOne,
1995 altPositionValidOne,
1996 altPositionValidTwo,
2000 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
2001 const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
2002 cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
2003 const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
2005 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
2006 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
2007 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
2008 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
2010 if( mHighlightMeshActor )
2012 mHighlightMeshActor.SetVisible( true );
2018 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2020 // Scroll the text to make the cursor visible.
2021 const Size cursorSize( CURSOR_THICKNESS,
2022 GetRowRectFromCharacterPosition( mCursorPosition ).height );
2024 // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2026 const Vector3& controlSize = GetControlSize();
2028 // Calculates the new scroll position.
2029 Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2030 if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2032 scrollOffset.x += cursorPosition.x;
2035 if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2037 scrollOffset.y += cursorPosition.y;
2040 // Sets the new scroll position.
2041 SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2042 SetScrollPosition( scrollOffset );
2045 void TextInput::StartScrollTimer()
2049 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2050 mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2053 if( !mScrollTimer.IsRunning() )
2055 mScrollTimer.Start();
2059 void TextInput::StopScrollTimer()
2063 mScrollTimer.Stop();
2067 bool TextInput::OnScrollTimerTick()
2069 // TODO: need to set the new style accordingly the new handle position.
2071 if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2073 // nothing to do if all handles are invisible or doesn't exist.
2079 // Choose between the grab handle or the selection handles.
2080 Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2081 std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2082 Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2084 std::size_t newCursorPosition = 0;
2085 ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2087 // Whether the handle's position is different of the previous one and in the case of the selection handle,
2088 // the new selection handle's position needs to be different of the other one.
2089 const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2090 ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2091 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2093 if( differentSelectionHandles )
2095 handlePosition = newCursorPosition;
2097 const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2099 Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2101 Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2102 scrollPosition += scrollDelta;
2103 SetScrollPosition( scrollPosition );
2105 if( mDisplayedTextView.IsScrollPositionTrimmed() )
2110 currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2113 actualHandlePosition.x += mScrollDisplacement.x;
2114 actualHandlePosition.y += mScrollDisplacement.y;
2119 // Public Internal Methods (public for testing purpose)
2121 void TextInput::SetUpTouchEvents()
2123 if ( !mTapDetector )
2125 mTapDetector = TapGestureDetector::New();
2126 // Attach the actors and connect the signal
2127 mTapDetector.Attach(Self());
2129 // As contains children which may register for tap the default control detector is not used.
2130 mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2133 if ( !mDoubleTapDetector )
2135 mDoubleTapDetector = TapGestureDetector::New( 2 );
2136 mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2138 // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2139 // so that we do not, unnecessarily, have a double tap request all the time
2142 if ( !mPanGestureDetector )
2144 mPanGestureDetector = PanGestureDetector::New();
2145 mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2148 if ( !mLongPressDetector )
2150 mLongPressDetector = LongPressGestureDetector::New();
2151 mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2152 mLongPressDetector.Attach(Self());
2156 void TextInput::CreateTextViewActor()
2158 mDisplayedTextView = Toolkit::TextView::New();
2159 mDisplayedTextView.SetName( "DisplayedTextView ");
2160 mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2161 mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2162 mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2163 mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2164 mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2165 mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2166 mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2167 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2168 mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2169 mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2171 mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2173 Self().Add( mDisplayedTextView );
2176 // Start a timer to initiate, used by the cursor to blink.
2177 void TextInput::StartCursorBlinkTimer()
2179 if ( !mCursorBlinkTimer )
2181 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2182 mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2185 if ( !mCursorBlinkTimer.IsRunning() )
2187 mCursorBlinkTimer.Start();
2191 // Start a timer to initiate, used by the cursor to blink.
2192 void TextInput::StopCursorBlinkTimer()
2194 if ( mCursorBlinkTimer )
2196 mCursorBlinkTimer.Stop();
2200 void TextInput::StartEditMode()
2202 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2204 if(!mEditModeActive)
2209 if ( mDoubleTapDetector )
2211 mDoubleTapDetector.Attach( Self() );
2215 void TextInput::EndEditMode()
2217 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2219 ClearKeyInputFocus();
2221 if ( mDoubleTapDetector )
2223 mDoubleTapDetector.Detach( Self() );
2227 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2229 if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2231 mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2233 style.SetUnderline( true );
2234 ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2238 void TextInput::RemovePreEditStyle()
2240 if ( !mUnderlinedPriorToPreEdit )
2243 style.SetUnderline( false );
2244 SetActiveStyle( style, TextStyle::UNDERLINE );
2248 // IMF related methods
2251 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2253 bool update( false );
2254 bool preeditResetRequired ( false );
2256 if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2258 HidePopup(); // If Pop-up shown then hides it as editing text.
2261 switch ( imfEvent.eventName )
2263 case ImfManager::PREEDIT:
2265 mIgnoreFirstCommitFlag = false;
2267 // 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
2268 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2270 // replaces highlighted text with new character
2271 DeleteHighlightedText( false );
2274 preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2276 if( IsScrollEnabled() )
2278 // Calculates the new cursor position (in actor coordinates)
2279 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2280 ScrollTextViewToMakeCursorVisible( cursorPosition );
2287 case ImfManager::COMMIT:
2289 if( mIgnoreFirstCommitFlag )
2291 // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2292 mIgnoreFirstCommitFlag = false;
2296 // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2298 // 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
2299 if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) )
2301 // replaces highlighted text with new character
2302 DeleteHighlightedText( false );
2305 // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2306 // not needed, one such scenario is when the pre-edit word is too long to fit.
2307 if ( !mIgnoreCommitFlag )
2309 update = CommitReceived( imfEvent.predictiveString );
2313 mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2319 if( IsScrollEnabled() )
2321 // Calculates the new cursor position (in actor coordinates)
2322 const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2324 ScrollTextViewToMakeCursorVisible( cursorPosition );
2329 case ImfManager::DELETESURROUNDING:
2331 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2332 (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2334 mPreEditFlag = false;
2336 std::size_t toDelete = 0;
2337 std::size_t numberOfCharacters = 0;
2339 if( mHighlightMeshActor )
2341 // delete highlighted text.
2342 toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2343 numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2347 if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2349 toDelete = mCursorPosition + imfEvent.cursorOffset;
2351 if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2353 numberOfCharacters = mStyledText.size() - toDelete;
2357 numberOfCharacters = imfEvent.numberOfChars;
2360 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2361 DeleteRange( toDelete, numberOfCharacters );
2363 mCursorPosition = toDelete;
2364 mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2368 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2371 case ImfManager::GETSURROUNDING:
2373 // 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
2374 // the next key pressed. Instead the Select function sets the cursor position and surrounding text.
2375 if (! ( mHighlightMeshActor || mSelectingText ) )
2377 std::string text( GetText() );
2378 DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2380 imfManager.SetCursorPosition( mCursorPosition );
2381 imfManager.SetSurroundingText( text );
2384 if( 0 != mNumberOfSurroundingCharactersDeleted )
2386 mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2387 mNumberOfSurroundingCharactersDeleted = 0;
2389 if( mStyledText.empty() )
2391 ShowPlaceholderText( mStyledPlaceHolderText );
2396 case ImfManager::VOID:
2398 DALI_ASSERT_DEBUG( false );
2402 ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2404 return callbackData;
2407 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2409 mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2411 DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2412 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2414 bool preeditResetRequest ( false );
2416 if( mPreEditFlag ) // Already in pre-edit state.
2418 if( mStyledText.size() >= mMaxStringLength )
2420 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2421 // Cannot fit these characters into field, clear pre-edit.
2422 if ( !mUnderlinedPriorToPreEdit )
2425 style.SetUnderline( false );
2426 ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2428 mIgnoreCommitFlag = true;
2429 preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2430 mPreEditFlag = false;
2431 EmitMaxInputCharactersReachedSignal();
2435 // delete existing pre-edit string
2436 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2438 // Store new pre-edit string
2439 mPreEditString.SetText( keyString );
2441 if ( keyString.empty() )
2443 mPreEditFlag = false;
2444 mCursorPosition = mPreEditStartPosition;
2446 if( mStyledText.empty() )
2448 ShowPlaceholderText( mStyledPlaceHolderText );
2452 mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2455 GetTextLayoutInfo();
2460 // Insert new pre-edit string. InsertAt updates the size and position table.
2461 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2462 // 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.
2463 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2464 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2465 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2468 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2472 else // mPreEditFlag not set
2474 if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2476 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2477 // new pre-edit so move into pre-edit state by setting flag
2478 mPreEditFlag = true;
2479 mPreEditString.SetText( keyString ); // store new pre-edit string
2480 mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2481 mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2482 // 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.
2483 mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2484 ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2485 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2486 // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2492 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2496 return preeditResetRequest;
2499 bool TextInput::CommitReceived(const std::string& keyString )
2501 DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2502 mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2504 bool update( false );
2506 RemovePreEditStyle();
2508 const std::size_t styledTextSize( mStyledText.size() );
2509 if( styledTextSize >= mMaxStringLength )
2511 // Cannot fit these characters into field, clear pre-edit.
2514 mIgnoreCommitFlag = true;
2515 mPreEditFlag = false;
2517 EmitMaxInputCharactersReachedSignal();
2523 // delete existing pre-edit string
2524 const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2525 mPreEditFlag = false;
2527 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2528 (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2530 if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2532 // No need to update cursor position as Cursor location given by touch.
2533 InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2534 mPreserveCursorPosition = false;
2538 // Cursor not set by touch so needs to be re-positioned to input more text
2539 mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2541 // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2542 if ( mCommitByKeyInput )
2544 mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2545 mCommitByKeyInput = false;
2551 if ( mSelectTextOnCommit )
2553 SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2558 else // mPreEditFlag not set
2560 if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2562 if( mStyledText.empty() && mPlaceHolderSet )
2564 // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2565 mDisplayedTextView.SetText( "" );
2566 mNumberOfSurroundingCharactersDeleted = 0;
2567 mPlaceHolderSet = false;
2569 mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2571 mNumberOfSurroundingCharactersDeleted = 0;
2576 mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2581 mSelectTextOnCommit = false;
2583 DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2584 mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2589 // End of IMF related methods
2591 std::size_t TextInput::DeletePreEdit()
2593 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2595 DALI_ASSERT_DEBUG( mPreEditFlag );
2597 const std::size_t preEditStringLength = mPreEditString.GetLength();
2598 const std::size_t styledTextSize = mStyledText.size();
2600 std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2602 // Prevents erase items outside mStyledText bounds.
2603 if( mPreEditStartPosition > styledTextSize )
2605 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2606 mPreEditStartPosition = styledTextSize;
2609 if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2611 DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2612 endPosition = styledTextSize;
2615 mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2617 // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2618 // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2620 // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2622 return preEditStringLength;
2625 void TextInput::PreEditReset( bool preserveCursorPosition )
2627 DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2628 preserveCursorPosition, mCursorPosition);
2630 // 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.
2631 mPreserveCursorPosition = preserveCursorPosition;
2633 // Reset incase we are in a pre-edit state.
2634 ImfManager imfManager = ImfManager::Get();
2637 imfManager.Reset(); // Will trigger a commit message
2641 void TextInput::CursorUpdate()
2645 ImfManager imfManager = ImfManager::Get();
2648 std::string text( GetText() );
2649 imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2650 imfManager.SetCursorPosition ( mCursorPosition );
2651 imfManager.NotifyCursorPosition();
2655 /* Delete highlighted characters redisplay*/
2656 void TextInput::DeleteHighlightedText( bool inheritStyle )
2658 DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2660 if( mHighlightMeshActor )
2662 mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2664 MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2665 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2667 // Get the styled text of the characters to be deleted as it may be needed if
2668 // the "exceed the text-input's boundaries" option is disabled.
2669 MarkupProcessor::StyledTextArray styledCharactersToDelete;
2671 styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2673 mStyledText.erase( start, end ); // erase range of characters
2675 // Remove text from TextView and update place holder text if required
2677 // Set the placeholder text only if the styled text is empty.
2678 if( mStyledText.empty() )
2680 ShowPlaceholderText( mStyledPlaceHolderText );
2684 const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2686 mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2688 // It may happen than after removing a white space or a new line character,
2689 // two words merge, this new word could be big enough to not fit in its
2690 // current line, so moved to the next one, and make some part of the text to
2691 // exceed the text-input's boundary.
2692 if( !mExceedEnabled )
2694 // Get the new text layout after removing some characters.
2695 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2697 // Get text-input's size.
2698 const Vector3& size = GetControlSize();
2700 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2701 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2703 mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2705 mStyledText.insert( mStyledText.begin() + mCursorPosition,
2706 styledCharactersToDelete.begin(),
2707 styledCharactersToDelete.end() );
2711 GetTextLayoutInfo();
2719 const TextStyle oldInputStyle( mInputStyle );
2721 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2723 if( oldInputStyle != mInputStyle )
2725 // Updates the line height accordingly with the input style.
2728 EmitStyleChangedSignal();
2734 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2736 DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2737 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2739 DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2742 if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2744 MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start;
2745 MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters;
2747 mStyledText.erase(itStart, itEnd);
2749 // update the selection handles if they are visible.
2750 if( mHighlightMeshActor )
2752 std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2753 std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2755 if( minHandle >= start + ncharacters )
2757 minHandle -= ncharacters;
2759 else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2764 if( maxHandle >= start + ncharacters )
2766 maxHandle -= ncharacters;
2768 else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2774 // 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.
2777 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2779 // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2780 // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2781 // Mean we do not re-draw the text more than we have too.
2784 /* Delete character at current cursor position and redisplay*/
2785 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2787 // Ensure positionToDelete is not out of bounds.
2788 DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2789 DALI_ASSERT_DEBUG( !mStyledText.empty() );
2790 DALI_ASSERT_DEBUG( positionToDelete > 0 );
2792 DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2795 if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor
2797 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1;
2799 // Get the styled text of the character to be deleted as it may be needed if
2800 // the "exceed the text-input's boundaries" option is disabled.
2801 const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2803 mStyledText.erase(it); // erase the character left of positionToDelete
2805 if( mStyledText.empty() )
2807 ShowPlaceholderText( mStyledPlaceHolderText );
2811 mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2813 const Character characterToDelete = styledCharacterToDelete.mText[0];
2815 // It may happen than after removing a white space or a new line character,
2816 // two words merge, this new word could be big enough to not fit in its
2817 // current line, so moved to the next one, and make some part of the text to
2818 // exceed the text-input's boundary.
2819 if( !mExceedEnabled )
2821 if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2823 // Get the new text layout after removing one character.
2824 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2826 // Get text-input's size.
2827 const Vector3& size = GetControlSize();
2829 if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2830 ( mTextLayoutInfo.mTextSize.height > size.height ) )
2832 MarkupProcessor::StyledTextArray array;
2833 array.push_back( styledCharacterToDelete );
2834 mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2836 mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2841 GetTextLayoutInfo();
2843 ShowGrabHandleAndSetVisibility( false );
2845 mCursorPosition = positionToDelete -1;
2847 const TextStyle oldInputStyle( mInputStyle );
2849 mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2851 if( oldInputStyle != mInputStyle )
2853 // Updates the line height accordingly with the input style.
2856 EmitStyleChangedSignal();
2861 /*Insert new character into the string and (optionally) redisplay text-input*/
2862 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2864 DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2866 // Ensure insertionPosition is not out of bounds.
2867 DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2869 bool textExceedsMaximunNumberOfCharacters = false;
2870 bool textExceedsBoundary = false;
2871 std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2873 ShowGrabHandleAndSetVisibility( false );
2875 if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2879 mIgnoreCommitFlag = true;
2880 mPreEditFlag = false;
2881 // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2882 // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2885 if( textExceedsMaximunNumberOfCharacters )
2887 EmitMaxInputCharactersReachedSignal();
2890 if( textExceedsBoundary )
2892 EmitInputTextExceedsBoundariesSignal();
2893 PreEditReset( false );
2897 return insertedStringLength;
2900 ImageActor TextInput::CreateCursor( const Vector4& color)
2903 cursor = CreateSolidColorActor(color);
2904 cursor.SetName( "Cursor" );
2906 cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2907 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2908 cursor.SetVisible(false);
2913 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2915 // As cursor is not moving due to grab handle, handle should be hidden.
2916 ShowGrabHandleAndSetVisibility( false );
2918 bool cursorPositionChanged = false;
2921 if ( mCursorPosition >= places )
2923 mCursorPosition = mCursorPosition - places;
2924 cursorPositionChanged = true;
2929 if ((mCursorPosition + places) <= mStyledText.size())
2931 mCursorPosition = mCursorPosition + places;
2932 cursorPositionChanged = true;
2936 if( cursorPositionChanged )
2938 const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2940 const TextStyle oldInputStyle( mInputStyle );
2941 mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2945 if( oldInputStyle != mInputStyle )
2947 // Updates the line height accordingly with the input style.
2950 EmitStyleChangedSignal();
2953 ImfManager imfManager = ImfManager::Get();
2956 imfManager.SetCursorPosition ( mCursorPosition );
2957 imfManager.NotifyCursorPosition();
2962 void TextInput::DrawCursor()
2964 const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2966 // Get height of cursor and set its size
2967 Size size( CURSOR_THICKNESS, 0.0f );
2968 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2970 size.height = rowRect.height;
2974 // Measure Font so know how big text will be if no initial text to measure.
2975 size.height = mLineHeight;
2978 mCursor.SetSize(size);
2980 // If the character is italic then the cursor also tilts.
2981 mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2983 DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2985 if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2987 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
2988 bool altPositionValid; // Alternate cursor validity flag.
2989 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2990 Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2992 SetAltCursorEnabled( altPositionValid );
2994 if( !altPositionValid )
2996 mCursor.SetPosition( position + UI_OFFSET );
3000 size.height *= 0.5f;
3001 mCursor.SetSize(size);
3002 mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
3004 // TODO: change this cursor pos, to be the one where the cursor is sourced from.
3005 size.height = rowRect.height * 0.5f;
3006 mCursorRTL.SetSize(size);
3007 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
3010 if( IsScrollEnabled() )
3012 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3013 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3018 void TextInput::SetAltCursorEnabled( bool enabled )
3020 mCursorRTLEnabled = enabled;
3021 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3024 void TextInput::SetCursorVisibility( bool visible )
3026 mCursorVisibility = visible;
3027 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3028 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3031 void TextInput::CreateGrabHandle( Dali::Image image )
3037 mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3041 mGrabHandleImage = image;
3044 mGrabHandle = ImageActor::New(mGrabHandleImage);
3045 mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3046 mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3048 mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3050 ShowGrabHandleAndSetVisibility( false );
3052 CreateGrabArea( mGrabHandle );
3054 mActiveLayer.Add(mGrabHandle);
3058 void TextInput::CreateGrabArea( Actor& parent )
3060 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3061 mGrabArea.SetName( "GrabArea" );
3062 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3063 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3064 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
3065 mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3066 mTapDetector.Attach( mGrabArea );
3067 mPanGestureDetector.Attach( mGrabArea );
3068 mLongPressDetector.Attach( mGrabArea );
3070 parent.Add(mGrabArea);
3073 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3075 Vector3 actualHandlePosition;
3079 mActualGrabHandlePosition.x += displacement.x;
3080 mActualGrabHandlePosition.y += displacement.y;
3082 // Grab handle should jump to the nearest character and take cursor with it
3083 std::size_t newCursorPosition = 0;
3084 ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3086 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3087 bool altPositionValid; // Alternate cursor validity flag.
3088 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3089 actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3091 if( altPositionValid )
3093 // Check which of the positions is the closest.
3094 if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3096 actualHandlePosition = altPosition;
3100 bool handleVisible = true;
3102 if( IsScrollEnabled() )
3104 const Vector3 controlSize = GetControlSize();
3105 const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3106 // Scrolls the text if the handle is not in a visible position
3107 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3114 mCurrentHandlePosition = actualHandlePosition;
3115 mScrollDisplacement = Vector2::ZERO;
3119 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3121 mScrollDisplacement.x = -SCROLL_SPEED;
3123 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3125 mScrollDisplacement.x = SCROLL_SPEED;
3127 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3129 mScrollDisplacement.y = -SCROLL_SPEED;
3131 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3133 mScrollDisplacement.y = SCROLL_SPEED;
3139 if( handleVisible && // Only redraw cursor and do updates if position changed
3140 ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3142 mCursorPosition = newCursorPosition;
3144 mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3146 const TextStyle oldInputStyle( mInputStyle );
3148 mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3150 CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction.
3152 if( oldInputStyle != mInputStyle )
3154 // Updates the line height accordingly with the input style.
3157 EmitStyleChangedSignal();
3162 return actualHandlePosition;
3165 void TextInput::ShowGrabHandle( bool visible )
3167 if ( IsGrabHandleEnabled() )
3171 mGrabHandle.SetVisible( mGrabHandleVisibility );
3173 StartMonitoringStageForTouch();
3177 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3179 mGrabHandleVisibility = visible;
3180 ShowGrabHandle( visible );
3183 // Callbacks connected to be Property notifications for Boundary checking.
3185 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3187 mIsSelectionHandleOneFlipped = true;
3188 mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3189 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3192 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3194 mIsSelectionHandleOneFlipped = false;
3195 mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3196 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3199 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3201 mIsSelectionHandleTwoFlipped = true;
3202 mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3203 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3206 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3208 mIsSelectionHandleTwoFlipped = false;
3209 mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3210 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3213 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3214 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3216 mSelectionHandleOne.SetOpacity(0.0f);
3219 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3221 mSelectionHandleOne.SetOpacity(1.0f);
3224 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3226 mSelectionHandleTwo.SetOpacity(0.0f);
3229 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3231 mSelectionHandleTwo.SetOpacity(1.0f);
3234 // End of Callbacks connected to be Property notifications for Boundary checking.
3236 void TextInput::SetUpHandlePropertyNotifications()
3238 /* Property notifications for handles exceeding the boundary and returning back within boundary */
3240 Vector3 handlesize = GetSelectionHandleSize();
3242 // Exceeding horizontal boundary
3243 PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3244 leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3246 PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3247 rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3249 // Within horizontal boundary
3250 PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3251 leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3253 PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3254 rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3256 // Exceeding vertical boundary
3257 PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3258 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3259 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3260 verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3262 PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3263 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3264 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3265 verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3267 // Within vertical boundary
3268 PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3269 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3270 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3271 verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3273 PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
3274 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3275 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3276 verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3279 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage )
3281 mSelectionHandleOnePosition = start;
3282 mSelectionHandleTwoPosition = end;
3284 if ( !mSelectionHandleOne )
3286 // create normal and pressed images
3287 mSelectionHandleOneImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3288 mSelectionHandleOneImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3290 mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3291 mSelectionHandleOne.SetName("SelectionHandleOne");
3292 mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3293 mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3294 mIsSelectionHandleOneFlipped = false;
3295 mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3297 mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3298 mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3300 mHandleOneGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3301 mHandleOneGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3302 mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3304 mTapDetector.Attach( mHandleOneGrabArea );
3305 mPanGestureDetector.Attach( mHandleOneGrabArea );
3307 mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3309 mSelectionHandleOne.Add( mHandleOneGrabArea );
3310 mActiveLayer.Add( mSelectionHandleOne );
3313 if ( !mSelectionHandleTwo )
3315 // create normal and pressed images
3316 mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3317 mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3319 mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3320 mSelectionHandleTwo.SetName("SelectionHandleTwo");
3321 mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3322 mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3323 mIsSelectionHandleTwoFlipped = false;
3324 mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3326 mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3327 mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3328 mHandleTwoGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
3329 mHandleTwoGrabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
3330 mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3332 mTapDetector.Attach( mHandleTwoGrabArea );
3333 mPanGestureDetector.Attach( mHandleTwoGrabArea );
3335 mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3337 mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3339 mActiveLayer.Add( mSelectionHandleTwo );
3342 SetUpHandlePropertyNotifications();
3344 // update table as text may have changed.
3345 GetTextLayoutInfo();
3347 Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position.
3348 bool altPositionValidOne; // Alternate cursor validity flag.
3349 bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3350 Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3352 Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position.
3353 bool altPositionValidTwo; // Alternate cursor validity flag.
3354 bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3355 Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3357 // VCC TODO: This method is a hack for one line.
3358 ChooseRtlSelectionHandlePosition( cursorPositionOne,
3360 altPositionValidOne,
3361 altPositionValidTwo,
3365 mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3366 mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3368 // Calculates and set the visibility if the scroll mode is enabled.
3369 bool isSelectionHandleOneVisible = true;
3370 bool isSelectionHandleTwoVisible = true;
3371 if( IsScrollEnabled() )
3373 const Vector3& controlSize( GetControlSize() );
3374 isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3375 isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3376 mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3377 mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3380 CreateHighlight(); // function will only create highlight if not already created.
3383 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3385 Vector3 actualHandlePosition;
3387 if ( mSelectionHandleOne && mSelectionHandleTwo )
3389 const Vector3& controlSize = GetControlSize();
3391 Size cursorSize( CURSOR_THICKNESS, 0.f );
3393 // Get a reference of the wanted selection handle (handle one or two).
3394 Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3396 // Get a reference for the current position of the handle and a copy of its pair
3397 std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3398 const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3400 // Get a handle of the selection handle actor
3401 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3403 // Selection handles should jump to the nearest character
3404 std::size_t newHandlePosition = 0;
3405 ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3407 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
3408 bool altPositionValid; // Alternate cursor validity flag.
3409 bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3410 actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3411 if( altPositionValid )
3413 // Check which of the positions is the closest.
3414 if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3416 actualHandlePosition = altPosition;
3420 bool handleVisible = true;
3422 if( IsScrollEnabled() )
3424 mCurrentSelectionId = handleId;
3426 cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3427 // Restricts the movement of the grab handle inside the boundaries of the text-input.
3428 handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3435 mCurrentSelectionHandlePosition = actualHandlePosition;
3436 mScrollDisplacement = Vector2::ZERO;
3440 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3442 mScrollDisplacement.x = -SCROLL_SPEED;
3444 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3446 mScrollDisplacement.x = SCROLL_SPEED;
3448 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3450 mScrollDisplacement.y = -SCROLL_SPEED;
3452 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3454 mScrollDisplacement.y = SCROLL_SPEED;
3460 if ( handleVisible && // Ensure the handle is visible.
3461 ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two.
3462 ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3464 currentSelectionHandlePosition = newHandlePosition;
3466 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3467 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3471 if ( handleId == HandleOne )
3473 const TextStyle oldInputStyle( mInputStyle );
3475 // Set Active Style to that of first character in selection
3476 if( mSelectionHandleOnePosition < mStyledText.size() )
3478 mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3481 if( oldInputStyle != mInputStyle )
3483 // Updates the line height accordingly with the input style.
3486 EmitStyleChangedSignal();
3492 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3495 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3497 const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3498 ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3500 if ( selectionHandleActor )
3502 const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3503 Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3504 selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3506 if( IsScrollEnabled() )
3508 const Size cursorSize( CURSOR_THICKNESS,
3509 GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3510 selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3512 GetControlSize() ) );
3517 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3519 selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3521 // VCC Set true/false in logical order. TODO : It needs to be checked.
3523 if( startSelection > endSelection )
3525 std::swap( startSelection, endSelection );
3527 std::size_t index = 0u;
3528 for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3530 if( ( index < startSelection ) || ( endSelection <= index ) )
3541 // Calculate the dimensions of the quads they will make the highlight mesh
3542 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3544 // At the moment there is no public API to modify the block alignment option.
3546 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3548 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3550 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3551 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3553 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3554 std::vector<bool> selectedVisualText;
3555 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3556 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3557 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3559 SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3560 float rowLeft = 0.0f;
3561 float rowRight = 0.0f;
3562 // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3563 float maxRowLeft = std::numeric_limits<float>::max();
3564 float maxRowRight = 0.0f;
3566 Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3568 // Scan through entire text.
3571 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3573 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3574 bool charSelected = false;
3575 if( selectedIt != selectedEndIt )
3577 charSelected = *selectedIt++;
3580 if( selectionState == SelectionNone )
3584 selectionState = SelectionStarted;
3585 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3586 rowRight = rowLeft + charInfo.mSize.width;
3589 else if( selectionState == SelectionStarted )
3591 // break selection on:
3592 // 1. new line causing selection break. (\n or wordwrap)
3593 // 2. character not selected.
3594 if( !charSelected ||
3595 ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3597 // finished selection.
3598 // TODO: TextView should have a table of visual rows, and each character a reference to the row
3599 // that it resides on. That way this enumeration is not necessary.
3601 if(lastIt->mIsNewParagraphChar)
3603 // 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.
3604 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3606 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3607 maxRowLeft = std::min(maxRowLeft, min.x);
3608 maxRowRight = std::max(maxRowRight, max.x);
3609 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3610 float rowTop = rowBottom - rowSize.height;
3612 // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3615 rowRight = std::numeric_limits<float>::max();
3617 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3619 selectionState = SelectionNone;
3621 // Still selected? start a new selection
3624 // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3626 rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3627 selectionState = SelectionStarted;
3632 // build up highlight(s) with this selection data.
3633 rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3634 rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3641 // If reached end, and still on selection, then close selection.
3644 if(selectionState == SelectionStarted)
3646 // finished selection.
3648 if(lastIt->mIsNewParagraphChar)
3650 lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3652 const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3653 maxRowLeft = std::min(maxRowLeft, min.x);
3654 maxRowRight = std::max(maxRowRight, max.x);
3655 float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3656 float rowTop = rowBottom - rowSize.height;
3657 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3661 // Get the top left and bottom right corners.
3662 const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3663 const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3664 const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3666 // Clamp quads so they appear to clip to borders of the whole text.
3667 mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3669 // For block-align align Further Clamp quads to max left and right extents
3670 // BlockAlign: Will adjust highlight to block:
3672 // H[ello] (top row right = max of all rows right)
3673 // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3674 // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3675 // [text] (bottom row left = min of all rows left)
3676 // (common in SMS messaging selection)
3678 // As opposed to the default which is tight text highlighting.
3683 // (common in regular text editors/web browser selection)
3684 mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3686 // Finally clamp quads again so they don't exceed the boundry of the control.
3687 const Vector3& controlSize = GetControlSize();
3688 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3691 return mNewHighlightInfo;
3694 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3695 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3696 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3698 // At the moment there is no public API to modify the block alignment option.
3700 mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3702 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3704 Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3705 Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3707 // Get vector of flags representing characters that are selected (true) vs unselected (false).
3708 std::vector<bool> selectedVisualText;
3709 GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3710 std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3711 std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3713 // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text.
3714 float rowLeft = 0.0f;
3715 float rowRight = 0.0f;
3717 // VCC TODO this is valid for one line.
3719 const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3721 // Scan through entire text.
3724 // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3726 Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3727 bool charSelected = false;
3728 if( selectedIt != selectedEndIt )
3730 charSelected = *selectedIt++;
3735 rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3736 rowRight = rowLeft + charInfo.mSize.width;
3738 float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3739 float rowTop = rowBottom - rowSize.height;
3740 mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3746 // Finally clamp quads again so they don't exceed the boundry of the control.
3747 const Vector3& controlSize = GetControlSize();
3748 mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3751 return mNewHighlightInfo;
3754 void TextInput::UpdateHighlight()
3756 // Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3758 // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3760 // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ]
3761 // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ]
3762 // [ BOTTOM] [ MIDDLE ] [ MIDDLE ]
3763 // [BOTTOM] [ MIDDLE ]
3766 // Each quad is created as 2 triangles.
3767 // Middle is just 1 quad regardless of its size.
3781 if ( mHighlightMeshActor )
3783 // vertex and triangle buffers should always be present if MeshActor is alive.
3784 HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3785 MeshData::VertexContainer vertices;
3786 Dali::MeshData::FaceIndices faceIndices;
3788 if( !newHighlightInfo.mQuadList.empty() )
3790 std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3791 std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3793 // vertex position defaults to (0 0 0)
3794 MeshData::Vertex vertex;
3795 // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3798 for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3800 // Add each quad geometry (a sub-selection) to the mesh data.
3810 QuadCoordinates& quad = *iter;
3812 vertex.x = quad.min.x;
3813 vertex.y = quad.min.y;
3814 vertices.push_back( vertex );
3817 vertex.x = quad.max.x;
3818 vertex.y = quad.min.y;
3819 vertices.push_back( vertex );
3821 // bottom-left (v+2)
3822 vertex.x = quad.min.x;
3823 vertex.y = quad.max.y;
3824 vertices.push_back( vertex );
3826 // bottom-right (v+3)
3827 vertex.x = quad.max.x;
3828 vertex.y = quad.max.y;
3829 vertices.push_back( vertex );
3831 // triangle A (3, 1, 0)
3832 faceIndices.push_back( v + 3 );
3833 faceIndices.push_back( v + 1 );
3834 faceIndices.push_back( v );
3836 // triangle B (0, 2, 3)
3837 faceIndices.push_back( v );
3838 faceIndices.push_back( v + 2 );
3839 faceIndices.push_back( v + 3 );
3841 mMeshData.SetFaceIndices( faceIndices );
3844 BoneContainer bones(0); // passed empty as bones not required
3845 mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3846 mHighlightMesh.UpdateMeshData(mMeshData);
3851 void TextInput::ClearPopup()
3853 mPopupPanel.Clear();
3856 void TextInput::AddPopupOptions()
3858 mPopupPanel.AddPopupOptions();
3861 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3863 const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3865 Vector3 clampedPosition ( position );
3866 Vector3 tailOffsetPosition ( position );
3868 float xOffSet( 0.0f );
3870 Actor self = Self();
3871 const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3873 const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3874 const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3876 // Clamp to left or right or of boundary
3877 if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3879 xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3881 else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3883 xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3886 clampedPosition.x = position.x + xOffSet;
3887 tailOffsetPosition.x = -xOffSet;
3889 // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3890 bool flipTail( false );
3892 if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3894 clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3898 mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3899 mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3902 void TextInput::HidePopup(bool animate, bool signalFinished )
3904 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
3906 mPopupPanel.Hide( animate );
3908 if( animate && signalFinished )
3910 mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3915 void TextInput::ShowPopup( bool animate )
3918 Vector2 alternativePopupPosition;
3920 if(mHighlightMeshActor && mState == StateEdit)
3923 Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3925 // When text is selected, show popup above top handle (and text), or below bottom handle.
3926 // topHandle: referring to the top most point of the handle or the top line of selection.
3927 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3929 topHandle = mSelectionHandleOneActualPosition;
3930 bottomHandle = mSelectionHandleTwoActualPosition;
3931 rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3935 topHandle = mSelectionHandleTwoActualPosition;
3936 bottomHandle = mSelectionHandleOneActualPosition;
3937 rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3939 topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3940 position = Vector3(topHandle.x, topHandle.y, 0.0f);
3942 float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3944 position.x = xPosition;
3946 // Alternative position if no upper space
3947 bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3948 alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3952 // When no text is selected, show popup at world position of grab handle or cursor
3953 position = GetActualPositionFromCharacterPosition( mCursorPosition );
3954 const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3955 position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3956 // if can't be positioned above, then position below row.
3957 alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3960 // If grab handle enabled then position pop-up below the grab handle.
3961 alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3965 SetPopupPosition( position, alternativePopupPosition );
3968 mPopupPanel.Show( Self(), animate );
3969 StartMonitoringStageForTouch();
3971 mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3974 void TextInput::ShowPopupCutCopyPaste()
3978 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3979 // Check the selected text is whole text or not.
3980 if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3982 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3985 if ( !mStyledText.empty() && IsTextSelected() )
3987 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3988 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3991 if( mClipboard && mClipboard.NumberOfItems() )
3993 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3994 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3999 mPopupPanel.Hide(false);
4003 void TextInput::SetUpPopupSelection( bool showCutButton )
4006 mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
4007 // If no text exists then don't offer to select
4008 if ( !mStyledText.empty() )
4010 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
4011 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
4012 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
4014 // if clipboard has valid contents then offer paste option
4015 if( mClipboard && mClipboard.NumberOfItems() )
4017 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4018 mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4023 mPopupPanel.Hide(false);
4026 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4031 std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4032 bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point)
4033 bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point)
4034 float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched.
4036 const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4038 if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4040 float closestYdifference = std::numeric_limits<float>::max();
4041 std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest.
4042 std::size_t numberOfMatchedCharacters = 0;
4044 // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4045 // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4047 for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4049 const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4050 float baselinePosition = info.mPosition.y - info.mDescender;
4052 if( info.mIsVisible )
4054 // store difference between source y point and the y position of the current character
4055 float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4057 if( currentYdifference < closestYdifference )
4059 // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4060 lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4061 closestYdifference = currentYdifference;
4062 matchedCharacters.clear();
4063 numberOfMatchedCharacters = 0; // reset count
4066 // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4067 if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD )
4069 // ignore new line character.
4070 if( !info.mIsNewParagraphChar )
4072 matchedCharacters.push_back( info );
4073 numberOfMatchedCharacters++;
4077 } // End of loop checking each character's y position in the character layout table
4079 // Check if last character is a newline, if it is
4080 // then need pretend there is an imaginary line afterwards,
4081 // and check if user is touching below previous line.
4082 const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4084 if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4086 closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4090 // 2 Iterate through matching list of y positions and find closest matching X position.
4092 bool matched( false );
4094 // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4095 std::size_t visualIndex = 0u;
4096 const std::size_t matchedCharactersSize = matchedCharacters.size();
4097 for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4099 const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4101 if( info.mIsVisible )
4103 // stop when on left side of character's center.
4104 const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ;
4105 if( sourceScrollOffset.x < characterMidPointPosition )
4107 if(info.mIsRightToLeftCharacter)
4109 rightToLeftChar = true;
4111 glyphIntersection = info.mPosition.x;
4116 lastRightToLeftChar = info.mIsRightToLeftCharacter;
4120 if( visualIndex == matchedCharactersSize )
4122 rightToLeftChar = lastRightToLeftChar;
4125 closestIndex = lineOffset + visualIndex;
4127 mClosestCursorPositionEOL = false; // reset
4128 if( ( visualIndex == matchedCharactersSize ) && !matched )
4130 mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4133 // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4134 if( rightToLeftChar && lastRightToLeftChar )
4136 --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4141 // closestIndex is the visual index, need to convert it to the logical index
4142 if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4144 if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4146 // Checks for situations where user is touching between LTR and RTL
4147 // characters. To identify if the user means the end of a LTR string
4148 // or the beginning of an RTL string, and vice versa.
4149 if( closestIndex > 0 )
4151 if( rightToLeftChar && !lastRightToLeftChar )
4156 // A: In this touch range, the user is indicating that they wish to place
4157 // the cursor at the end of the LTR text.
4158 // B: In this touch range, the user is indicating that they wish to place
4159 // the cursor at the end of the RTL text.
4161 // Result of touching A area:
4162 // [.....LTR]|[RTL......]+
4164 // |: primary cursor (for typing LTR chars)
4165 // +: secondary cursor (for typing RTL chars)
4167 // Result of touching B area:
4168 // [.....LTR]+[RTL......]|
4170 // |: primary cursor (for typing RTL chars)
4171 // +: secondary cursor (for typing LTR chars)
4173 if( sourceScrollOffset.x < glyphIntersection )
4178 else if( !rightToLeftChar && lastRightToLeftChar )
4180 if( sourceScrollOffset.x < glyphIntersection )
4187 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4188 // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4189 // one further ahead
4190 if( rightToLeftChar && !lastRightToLeftChar )
4195 else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4197 closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4199 else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4208 float TextInput::GetLineJustificationPosition() const
4210 const Vector3& size = mDisplayedTextView.GetCurrentSize();
4211 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4212 float alignmentOffset = 0.f;
4214 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4215 if( alignment & Toolkit::Alignment::HorizontalLeft )
4217 alignmentOffset = 0.f;
4219 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4221 alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4223 else if( alignment & Toolkit::Alignment::HorizontalRight )
4225 alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4228 Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4229 float justificationOffset = 0.f;
4231 switch( justification )
4233 case Toolkit::TextView::Left:
4235 justificationOffset = 0.f;
4238 case Toolkit::TextView::Center:
4240 justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4243 case Toolkit::TextView::Right:
4245 justificationOffset = mTextLayoutInfo.mTextSize.width;
4248 case Toolkit::TextView::Justified:
4250 justificationOffset = 0.f;
4255 DALI_ASSERT_ALWAYS( false );
4259 return alignmentOffset + justificationOffset;
4262 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4264 /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4265 A newline character is not inserted in this case */
4267 Vector3 cursorPosition;
4269 Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4273 if( characterPosition > 0u )
4275 Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4277 // If previous character on a different line then use current characters position
4278 if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4280 // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4281 if ( mClosestCursorPositionEOL )
4283 cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4287 cursorPosition = Vector3( currentCharInfo.mPosition );
4296 // If the character is left to right, the position is the character's position plus its width.
4297 const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4299 cursorPosition.x = currentCharInfo.mPosition.x + ltrOffset;
4300 cursorPosition.y = currentCharInfo.mPosition.y;
4303 return cursorPosition;
4306 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4308 bool direction = false;
4309 Vector3 alternatePosition;
4310 bool alternatePositionValid = false;
4312 return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4315 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4317 DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4318 ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4319 "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4321 Vector3 cursorPosition( 0.f, 0.f, 0.f );
4323 alternatePositionValid = false;
4324 directionRTL = false;
4326 if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4328 if( characterPosition == 0u )
4330 // When the cursor position is at the beginning, it should be at the start of the current character.
4331 // If the current character is LTR, then the start is on the right side of the glyph.
4332 // If the current character is RTL, then the start is on the left side of the glyph.
4334 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4336 characterPosition = FindVisibleCharacter( Right, 0u );
4339 const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4340 const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4342 cursorPosition.x = info.mPosition.x + rtlOffset;
4343 cursorPosition.y = info.mPosition.y;
4344 directionRTL = info.mIsRightToLeftCharacter;
4346 else if( characterPosition > 0u )
4348 // Get the direction of the paragraph.
4349 const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4350 const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4352 // When cursor is not at beginning, consider possibility of
4353 // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4355 // Cursor position should be the end of the last character.
4356 // If the last character is LTR, then the end is on the right side of the glyph.
4357 // If the last character is RTL, then the end is on the left side of the glyph.
4359 --characterPosition;
4361 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4363 characterPosition = FindVisibleCharacter( Left, characterPosition );
4366 Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4367 if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4369 // VCC TODO : check for a new paragraph character.
4371 // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4372 const Vector3& size = GetControlSize();
4374 if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4376 --characterPosition;
4378 info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4381 if( !info.mIsNewParagraphChar )
4383 cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4387 // VCC TODO : check for a new paragraph character.
4389 // When cursor points to first character on new line, position cursor at the start of this glyph.
4390 if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4392 const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4393 const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4395 cursorPosition.x = infoNext.mPosition.x + start;
4396 cursorPosition.y = infoNext.mPosition.y;
4400 // If cursor points to the end of text, then can only position
4401 // cursor where the new line starts based on the line-justification position.
4402 cursorPosition.x = GetLineJustificationPosition();
4404 if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4406 // If this is after the last character, then we can assume that the new cursor
4407 // should be exactly one row below the current row.
4409 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4410 cursorPosition.y = info.mPosition.y + rowRect.height;
4414 // If this is not after last character, then we can use this row's height.
4415 // should be exactly one row below the current row.
4417 const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4418 cursorPosition.y = info.mPosition.y + rowRect.height;
4423 directionRTL = info.mIsRightToLeftCharacter;
4425 if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4427 // 1. When the cursor is neither at the beginning or the end,
4428 // we can show multiple cursors under situations when the cursor is
4429 // between RTL and LTR text...
4430 if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4432 std::size_t characterAltPosition = characterPosition + 1u;
4434 const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4436 if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4438 // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4439 // Text: [...LTR...]|[...RTL...]
4441 // Alternate cursor pos: ^
4442 // In which case we need to display an alternate cursor for the RTL text.
4444 alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4445 alternatePosition.y = infoAlt.mPosition.y;
4446 alternatePositionValid = true;
4448 else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4450 // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4451 // Text: |[...RTL...] [...LTR....]
4453 // Alternate cursor pos: ^
4454 // In which case we need to display an alternate cursor for the RTL text.
4456 alternatePosition.x = infoAlt.mPosition.x;
4457 alternatePosition.y = infoAlt.mPosition.y;
4458 alternatePositionValid = true;
4463 // 2. When the cursor is at the end of the text,
4464 // and we have multi-directional text,
4465 // we can also consider showing mulitple cursors.
4466 // The rule here is:
4467 // If first and last characters on row are different
4468 // Directions, then two cursors need to be displayed.
4470 if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4472 // The last character's direction is differernt than the first one of current paragraph.
4475 const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4477 if(info.mIsRightToLeftCharacter)
4479 // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4480 // Text: [...LTR...]|[...RTL...]
4482 // Alternate cursor pos: ^
4483 // In which case we need to display an alternate cursor for the RTL text, this cursor
4484 // should be at the end of the given line.
4486 alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4487 alternatePosition.y = infoStart.mPosition.y;
4488 alternatePositionValid = true;
4490 else if(!info.mIsRightToLeftCharacter) // starting RTL
4492 // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4493 // Text: |[...RTL...] [...LTR....]
4495 // Alternate cursor pos: ^
4496 // In which case we need to display an alternate cursor for the RTL text.
4498 alternatePosition.x = infoStart.mPosition.x;
4499 alternatePosition.y = infoStart.mPosition.y;
4500 alternatePositionValid = true;
4505 } // characterPosition > 0
4509 // If the character table is void, place the cursor accordingly the text alignment.
4510 const Vector3& size = GetControlSize();
4512 Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4513 float alignmentOffset = 0.f;
4515 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4516 if( alignment & Toolkit::Alignment::HorizontalLeft )
4518 alignmentOffset = 0.f;
4520 else if( alignment & Toolkit::Alignment::HorizontalCenter )
4522 alignmentOffset = 0.5f * ( size.width );
4524 else if( alignment & Toolkit::Alignment::HorizontalRight )
4526 alignmentOffset = size.width;
4529 // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4530 cursorPosition.x = alignmentOffset;
4532 // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4533 if( alignment & Toolkit::Alignment::VerticalTop )
4535 cursorPosition.y = mLineHeight;
4537 else if( alignment & Toolkit::Alignment::VerticalCenter )
4539 cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4541 else if( alignment & Toolkit::Alignment::VerticalBottom )
4543 cursorPosition.y = size.height;
4547 cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4548 cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4550 if( alternatePositionValid )
4552 alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4553 alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4556 return cursorPosition;
4559 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4561 // scan string from current position to beginning of current line to note direction of line
4562 while( logicalPosition )
4565 if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4572 return logicalPosition;
4575 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4577 const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4579 while( logicalPosition )
4582 if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4589 return logicalPosition;
4592 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4596 return GetRowRectFromCharacterPosition( characterPosition, min, max );
4599 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4601 // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4602 if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4604 min = Vector2::ZERO;
4605 max = Vector2(0.0f, mLineHeight);
4609 DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4611 // Initializes the min and max position.
4612 const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4613 min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4617 // 1) Find the line where the character is laid-out.
4618 for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4619 !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4622 const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4624 // Index within the whole text to the last character of the current line.
4625 std::size_t lastCharacterOfLine = 0u;
4627 Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4628 if( lineNextIt != lineEndIt )
4630 lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4634 lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4637 // Check if the given chracter position is within the line.
4638 if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4640 // 2) Get the row rect of all laid-out characters on the line.
4642 // Need to scan all characters of the line because they are in the logical position.
4643 for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4644 endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4648 const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4650 min.x = std::min( min.x, characterInfo.mPosition.x );
4651 min.y = std::min( min.y, characterInfo.mPosition.y );
4652 max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4653 max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4660 return Size( max.x - min.x, max.y - min.y );
4663 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4665 Actor popUpPanel = mPopupPanel.GetRootActor();
4667 if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4673 Dali::Actor parent( touchedActor.GetParent() );
4677 return WasTouchedCheck( parent );
4684 void TextInput::StartMonitoringStageForTouch()
4686 Stage stage = Stage::GetCurrent();
4687 stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4690 void TextInput::EndMonitoringStageForTouch()
4692 Stage stage = Stage::GetCurrent();
4693 stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4696 void TextInput::OnStageTouched(const TouchEvent& event)
4698 if( event.GetPointCount() > 0 )
4700 if ( TouchPoint::Down == event.GetPoint(0).state )
4702 const Actor touchedActor(event.GetPoint(0).hitActor);
4704 bool popUpShown( false );
4706 if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4711 bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4713 if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4715 EndMonitoringStageForTouch();
4716 HidePopup( true, false );
4719 if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched )
4721 EndMonitoringStageForTouch();
4722 ShowGrabHandleAndSetVisibility( false );
4728 void TextInput::SelectText(std::size_t start, std::size_t end)
4730 DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4731 IsGrabHandleEnabled()?"true":"false",
4732 start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4733 DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4734 DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4736 StartMonitoringStageForTouch();
4738 if ( mEditModeActive ) // Only allow text selection when in edit mode
4740 // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4741 mSelectingText = true;
4743 std::size_t selectionStartPosition = std::min( start, end );
4745 // Hide grab handle when selecting.
4746 ShowGrabHandleAndSetVisibility( false );
4748 if( start != end ) // something to select
4750 SetCursorVisibility( false );
4751 StopCursorBlinkTimer();
4753 CreateSelectionHandles(start, end);
4756 const TextStyle oldInputStyle( mInputStyle );
4757 mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4759 if( oldInputStyle != mInputStyle )
4761 // Updates the line height accordingly with the input style.
4764 EmitStyleChangedSignal();
4770 mSelectingText = false;
4774 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4776 MarkupProcessor::StyledTextArray currentSelectedText;
4778 if ( IsTextSelected() )
4780 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4781 MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4783 for(; it != end; ++it)
4785 MarkupProcessor::StyledText& styledText( *it );
4786 currentSelectedText.push_back( styledText );
4789 return currentSelectedText;
4792 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4794 const std::size_t beginIndex = std::min( begin, end );
4795 const std::size_t endIndex = std::max( begin, end );
4798 MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4800 // Create a styled text array used to replace the text into the text-view.
4801 MarkupProcessor::StyledTextArray text;
4802 text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4804 mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4805 GetTextLayoutInfo();
4807 if( IsScrollEnabled() )
4809 // Need to set the scroll position as the text's size may have changed.
4810 ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4813 ShowGrabHandleAndSetVisibility( false );
4819 // Set Handle positioning as the new style may have repositioned the characters.
4820 SetSelectionHandlePosition(HandleOne);
4821 SetSelectionHandlePosition(HandleTwo);
4824 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4826 // Just hide the grab handle when keyboard is hidden.
4827 if (!keyboardShown )
4829 ShowGrabHandleAndSetVisibility( false );
4831 // If the keyboard is not now being shown, then hide the popup panel
4832 mPopupPanel.Hide( true );
4836 // Removes highlight and resumes edit mode state
4837 void TextInput::RemoveHighlight( bool hidePopup )
4839 DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4841 if ( mHighlightMeshActor )
4843 if ( mSelectionHandleOne )
4845 mActiveLayer.Remove( mSelectionHandleOne );
4846 mSelectionHandleOne.Reset();
4847 mSelectionHandleOneOffset.x = 0.0f;
4849 if ( mSelectionHandleTwo )
4851 mActiveLayer.Remove( mSelectionHandleTwo );
4852 mSelectionHandleTwo.Reset();
4853 mSelectionHandleTwoOffset.x = 0.0f;
4856 mNewHighlightInfo.mQuadList.clear();
4858 Self().Remove( mHighlightMeshActor );
4860 SetCursorVisibility( true );
4861 StartCursorBlinkTimer();
4863 mHighlightMeshActor.Reset();
4864 // NOTE: We cannot dereference mHighlightMesh, due
4865 // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4873 mSelectionHandleOnePosition = 0;
4874 mSelectionHandleTwoPosition = 0;
4877 void TextInput::CreateHighlight()
4879 if ( !mHighlightMeshActor )
4881 mMeshData = MeshData( );
4882 mMeshData.SetHasNormals( true );
4884 mCustomMaterial = Material::New("CustomMaterial");
4885 mCustomMaterial.SetDiffuseColor( mMaterialColor );
4887 mMeshData.SetMaterial( mCustomMaterial );
4889 mHighlightMesh = Mesh::New( mMeshData );
4891 mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4892 mHighlightMeshActor.SetName( "HighlightMeshActor" );
4893 mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4894 mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4895 mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4897 Self().Add(mHighlightMeshActor);
4902 bool TextInput::CopySelectedTextToClipboard()
4904 mCurrentCopySelecton.clear();
4906 mCurrentCopySelecton = GetSelectedText();
4908 std::string stringToStore;
4910 /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4911 * a marked up string.
4913 MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4914 MarkupProcessor::GetPlainString( selectedText, stringToStore );
4916 bool success = mClipboard.SetItem( stringToStore );
4920 void TextInput::PasteText( const Text& text )
4922 // Update Flag, indicates whether to update the text-input contents or not.
4923 // Any key stroke that results in a visual change of the text-input should
4924 // set this flag to true.
4925 bool update = false;
4926 if( mHighlightMeshActor )
4928 /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4929 mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4931 ImfManager imfManager = ImfManager::Get();
4934 imfManager.SetCursorPosition( mCursorPosition );
4935 imfManager.NotifyCursorPosition();
4937 DeleteHighlightedText( true );
4941 bool textExceedsMaximunNumberOfCharacters = false;
4942 bool textExceedsBoundary = false;
4944 std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4946 mCursorPosition += insertedStringLength;
4947 ImfManager imfManager = ImfManager::Get();
4950 imfManager.SetCursorPosition ( mCursorPosition );
4951 imfManager.NotifyCursorPosition();
4954 update = update || ( insertedStringLength > 0 );
4961 if( insertedStringLength < text.GetLength() )
4963 EmitMaxInputCharactersReachedSignal();
4966 if( textExceedsBoundary )
4968 EmitInputTextExceedsBoundariesSignal();
4972 void TextInput::SetTextDirection()
4974 // Put the cursor to the right if we are empty and an RTL language is being used.
4975 if ( mStyledText.empty() )
4977 VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4979 // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4980 // alignment as we do not want to set the text direction if we've been asked to be in the center.
4982 // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4983 // set vertical alignment but are being forced to set the horizontal alignment as well with the
4985 int alignment( mDisplayedTextView.GetTextAlignment() &
4986 ( Toolkit::Alignment::VerticalTop |
4987 Toolkit::Alignment::VerticalCenter |
4988 Toolkit::Alignment::VerticalBottom |
4989 Toolkit::Alignment::HorizontalCenter ) );
4990 Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4992 // If our alignment is in the center, then do not change.
4993 if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4995 alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4998 // If our justification is in the center, then do not change.
4999 if ( justification != Toolkit::TextView::Center )
5001 justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
5004 mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
5005 mDisplayedTextView.SetLineJustification( justification );
5009 void TextInput::UpdateLineHeight()
5011 Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
5012 mLineHeight = font.GetLineHeight();
5014 // 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.
5016 const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5018 if( !mExceedEnabled || shrink )
5020 mLineHeight = std::min( mLineHeight, GetControlSize().height );
5024 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5026 // VCC check if we need do this in the visual order ...
5027 std::size_t position = 0u;
5029 const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5035 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5037 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5039 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5045 position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5046 if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5048 position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5054 position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5059 DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5066 void TextInput::SetSortModifier( float depthOffset )
5068 if(mDisplayedTextView)
5070 mDisplayedTextView.SetSortModifier(depthOffset);
5074 void TextInput::SetSnapshotModeEnabled( bool enable )
5076 if(mDisplayedTextView)
5078 mDisplayedTextView.SetSnapshotModeEnabled( enable );
5082 bool TextInput::IsSnapshotModeEnabled() const
5084 bool snapshotEnabled = false;
5086 if(mDisplayedTextView)
5088 snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5091 return snapshotEnabled;
5094 void TextInput::SetMarkupProcessingEnabled( bool enable )
5096 mMarkUpEnabled = enable;
5099 bool TextInput::IsMarkupProcessingEnabled() const
5101 return mMarkUpEnabled;
5104 void TextInput::SetScrollEnabled( bool enable )
5106 if( mDisplayedTextView )
5108 mDisplayedTextView.SetScrollEnabled( enable );
5113 // Don't set cursor's and handle's visibility to false if they are outside the
5114 // boundaries of the text-input.
5115 mIsCursorInScrollArea = true;
5116 mIsGrabHandleInScrollArea = true;
5117 if( mSelectionHandleOne && mSelectionHandleTwo )
5119 mSelectionHandleOne.SetVisible( true );
5120 mSelectionHandleTwo.SetVisible( true );
5122 if( mHighlightMeshActor )
5124 mHighlightMeshActor.SetVisible( true );
5130 bool TextInput::IsScrollEnabled() const
5132 bool scrollEnabled = false;
5134 if( mDisplayedTextView )
5136 scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5139 return scrollEnabled;
5142 void TextInput::SetScrollPosition( const Vector2& position )
5144 if( mDisplayedTextView )
5146 mDisplayedTextView.SetScrollPosition( position );
5150 Vector2 TextInput::GetScrollPosition() const
5152 Vector2 scrollPosition;
5154 if( mDisplayedTextView )
5156 scrollPosition = mDisplayedTextView.GetScrollPosition();
5159 return scrollPosition;
5162 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5164 // determine number of characters that we can write to style text buffer, this is the insertStringLength
5165 std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5166 textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5168 // Add style to the new input text.
5169 MarkupProcessor::StyledTextArray textToInsert;
5170 for( std::size_t i = 0; i < insertedStringLength; ++i )
5172 const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5173 textToInsert.push_back( newStyledCharacter );
5176 //Insert text to the TextView.
5177 const bool emptyTextView = mStyledText.empty();
5178 if( emptyTextView && mPlaceHolderSet )
5180 // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5181 mDisplayedTextView.SetText( textToInsert );
5185 if( 0 == numberOfCharactersToReplace )
5187 mDisplayedTextView.InsertTextAt( position, textToInsert );
5191 mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5194 mPlaceHolderSet = false;
5196 if( textToInsert.empty() )
5198 // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5199 GetTextLayoutInfo();
5203 // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5204 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5207 textExceedsBoundary = false;
5209 if( !mExceedEnabled )
5211 const Vector3& size = GetControlSize();
5213 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5215 // If new text does not fit within TextView
5216 mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5217 // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5218 GetTextLayoutInfo();
5219 textExceedsBoundary = true;
5220 insertedStringLength = 0;
5223 if( textExceedsBoundary )
5225 // Add the part of the text which fits on the text-input.
5227 // Split the text which doesn't fit in two halves.
5228 MarkupProcessor::StyledTextArray firstHalf;
5229 MarkupProcessor::StyledTextArray secondHalf;
5230 SplitText( textToInsert, firstHalf, secondHalf );
5232 // Clear text. This text will be filled with the text inserted.
5233 textToInsert.clear();
5235 // Where to insert the text.
5236 std::size_t positionToInsert = position;
5238 bool end = text.GetLength() <= 1;
5241 // Insert text and check ...
5242 const std::size_t textLength = firstHalf.size();
5243 mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5244 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5246 if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5248 // Inserted text doesn't fit.
5250 // Remove inserted text
5251 mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5252 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5254 // The iteration finishes when only one character doesn't fit.
5255 end = textLength <= 1;
5259 // Prepare next two halves for next iteration.
5260 MarkupProcessor::StyledTextArray copyText = firstHalf;
5261 SplitText( copyText, firstHalf, secondHalf );
5268 // store text to be inserted in mStyledText.
5269 textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5271 // Increase the inserted characters counter.
5272 insertedStringLength += textLength;
5274 // Prepare next two halves for next iteration.
5275 MarkupProcessor::StyledTextArray copyText = secondHalf;
5276 SplitText( copyText, firstHalf, secondHalf );
5278 // Update where next text has to be inserted
5279 positionToInsert += textLength;
5285 if( textToInsert.empty() && emptyTextView )
5287 // No character has been added and the text-view was empty.
5288 // Show the placeholder text.
5289 ShowPlaceholderText( mStyledPlaceHolderText );
5293 MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5294 mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5295 mPlaceHolderSet = false;
5298 return insertedStringLength;
5301 void TextInput::GetTextLayoutInfo()
5303 if( mStyledText.empty() )
5305 // The text-input has no text, clear the text-view's layout info.
5306 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5310 if( mDisplayedTextView )
5312 mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5316 // There is no text-view.
5317 mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5322 void TextInput::SetOffsetFromText( const Vector4& offset )
5324 mPopupOffsetFromText = offset;
5327 const Vector4& TextInput::GetOffsetFromText() const
5329 return mPopupOffsetFromText;
5332 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5334 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5338 TextInput& textInputImpl( GetImpl( textInput ) );
5340 switch ( propertyIndex )
5342 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5344 textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5347 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5349 textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5352 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5354 textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5357 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5359 textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5362 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5364 textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5367 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5369 textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5372 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5374 textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5377 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5379 textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5382 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5384 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5387 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5389 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5392 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5394 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5397 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5399 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5402 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5404 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5407 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5409 textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5412 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5414 textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5417 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5419 textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5425 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5427 Property::Value value;
5429 Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5433 TextInput& textInputImpl( GetImpl( textInput ) );
5435 switch ( propertyIndex )
5437 case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5439 value = textInputImpl.GetMaterialDiffuseColor();
5442 case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5444 value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5447 case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5449 value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5452 case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5454 value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5457 case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5459 value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5462 case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5464 value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5467 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5469 value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5472 case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5474 value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5477 case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5479 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5482 case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5484 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5487 case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5489 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5492 case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5494 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5497 case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5499 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5502 case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5504 value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5507 case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5509 value = textInputImpl.GetOffsetFromText();
5512 case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5514 value = textInputImpl.mCursor.GetCurrentColor();
5521 void TextInput::EmitStyleChangedSignal()
5523 // emit signal if input style changes.
5524 Toolkit::TextInput handle( GetOwner() );
5525 mStyleChangedSignal.Emit( handle, mInputStyle );
5528 void TextInput::EmitTextModified()
5530 // emit signal when text changes.
5531 Toolkit::TextInput handle( GetOwner() );
5532 mTextModifiedSignal.Emit( handle );
5536 void TextInput::EmitMaxInputCharactersReachedSignal()
5538 // emit signal if max characters is reached during text input.
5539 DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5541 Toolkit::TextInput handle( GetOwner() );
5542 mMaxInputCharactersReachedSignal.Emit( handle );
5545 void TextInput::EmitInputTextExceedsBoundariesSignal()
5547 // Emit a signal when the input text exceeds the boundaries of the text input.
5549 Toolkit::TextInput handle( GetOwner() );
5550 mInputTextExceedBoundariesSignal.Emit( handle );
5553 } // namespace Internal
5555 } // namespace Toolkit