2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
17 #include <dali/integration-api/debug.h>
20 #include <dali-toolkit/internal/controls/text-input/text-input-decorator-impl.h>
22 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
28 #if defined(DEBUG_ENABLED)
29 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
32 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
33 const float TOP_HANDLE_TOP_OFFSET(-1.5f); // Offset between top handle and cutCopyPaste pop-up
34 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); // Offset between bottom handle and cutCopyPaste pop-up
35 const float UI_Z_OFFSET( 0.2f ); // Text Selection Handles/Cursor z-offset.
36 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); // Text Selection Handles/Cursor offset.
37 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
38 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
39 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
40 const float CURSOR_THICKNESS(6.0f);
41 const Degree CURSOR_ANGLE_OFFSET(2.0f); // Offset from the angle
43 const unsigned int SCROLL_TICK_INTERVAL = 50u;
44 const float SCROLL_THRESHOLD = 10.f;
45 const float SCROLL_SPEED = 15.f;
48 * Whether the given position plus the cursor size offset is inside the given boundary.
50 * @param[in] position The given position.
51 * @param[in] cursorSize The cursor size.
52 * @param[in] controlSize The given boundary.
53 * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
55 * @return whether the given position is inside the given boundary.
57 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
59 return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
60 ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
61 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
62 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
75 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
76 mTextViewCharacterPositioning( textViewManager ),
77 mTextStyle( textStyle ),
79 mTextHighlight( textViewManager ),
80 mCursorBlinkStatus( true ),
81 mCursorVisibility( true ),
82 mGrabHandleEnabled( true )
86 Decorator::~Decorator()
93 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
95 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
96 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
98 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
99 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
101 const Vector4 boundary( originX,
103 originX + boundingRectangle.width,
104 originY + boundingRectangle.height );
106 mBoundingRectangleWorldCoordinates = boundary;
109 Vector4 Decorator::GetBoundingBox() const
111 return mBoundingRectangleWorldCoordinates;
117 void Decorator::OnHandlePan(Actor actor, PanGesture gesture)
119 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
120 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
122 switch (gesture.state)
124 case Gesture::Started:
125 // fall through so code not duplicated
126 case Gesture::Continuing:
128 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
130 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
133 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
135 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
138 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
140 SetCursorVisibility( true );
141 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
142 MoveGrabHandle( gesture.displacement );
143 HidePopUp(); // Do not show popup while handle is moving
148 case Gesture::Finished:
150 // Revert back to non-pressed selection handle images
151 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
153 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
154 ShowPopupCutCopyPaste();
157 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
159 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
160 ShowPopupCutCopyPaste();
163 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
165 MoveGrabHandle( gesture.displacement );
166 SetCursorVisibility( true );
167 ShowPopupCutCopyPaste();
177 void Decorator::CreateSelectionHandles( Actor targetParent )
179 if ( !mPanGestureDetector )
181 mPanGestureDetector = PanGestureDetector::New();
182 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
185 if ( !mTextInputHandles.GetSelectionHandleOne() )
187 mTextInputHandles.CreateSelectionHandles();
189 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
191 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
192 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
194 SetUpHandlePropertyNotifications();
198 void Decorator::RemoveSelectionHandles()
200 mTextInputHandles.DestorySelectionHandles();
203 Vector3 Decorator::GetSelectionHandleSize()
205 return DEFAULT_SELECTION_HANDLE_SIZE;
208 std::size_t Decorator::GetHandleOnePosition() const
210 return mSelectionHandleOnePosition;
213 std::size_t Decorator::GetHandleTwoPosition() const
215 return mSelectionHandleTwoPosition;
218 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
220 bool direction(false);
221 Vector3 alternatePosition;
222 bool alternatePositionValid(false);
224 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
226 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
230 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
232 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
234 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
236 if( mTextViewCharacterPositioning.IsScrollEnabled() )
238 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
239 const Size cursorSize( GetCursorSizeAt( position ) );
240 bool handleVisible = IsPositionWithinControl( actualPosition, Vector2(DEFAULT_HANDLE_OFFSET.width, DEFAULT_HANDLE_OFFSET.height), controlSize );
242 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::PositionSelectionHandle controlSize[%f,%f] cursorSize[%f,%f] actualPos[%f,%f] visible[%s] \n",
243 controlSize.x, controlSize.y, cursorSize.x, cursorSize.y, actualPosition.x, actualPosition.y, ( handleVisible )?"true":"false" );
245 selectionHandle.SetVisible( handleVisible );
247 return actualPosition;
250 void Decorator::SetSelectionHandlesVisibility(bool visible )
252 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
253 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
256 bool Decorator::OnHandleReleased()
262 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
264 mSelectionHandleOnePosition = start;
265 mSelectionHandleTwoPosition = end;
267 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
269 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
270 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
272 mTextInputHandles.ReleasedSignal().Connect( this, &Decorator::OnHandleReleased );
275 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
276 Vector3& actualSelectionHandlePosition,
277 std::size_t& currentSelectionHandlePosition,
278 const Vector2& displacement )
280 Vector3 actualHandlePosition;
281 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
282 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;;
284 // Selection handles should jump to the nearest character
285 std::size_t newHandlePosition = 0;
286 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
288 bool direction(false);
289 Vector3 alternatePosition;
290 bool alternatePositionValid(false);
291 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
293 bool handleVisible = true;
296 if( mTextViewCharacterPositioning.IsScrollEnabled() )
298 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
299 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
301 handleVisible = IsPositionWithinControl( actualHandlePosition,
308 mCurrentHandlePosition = actualHandlePosition;
309 mScrollDisplacement = Vector2::ZERO;
314 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
316 mScrollDisplacement.x = -SCROLL_SPEED;
318 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
320 mScrollDisplacement.x = SCROLL_SPEED;
322 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
324 mScrollDisplacement.y = -SCROLL_SPEED;
326 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
328 mScrollDisplacement.y = SCROLL_SPEED;
330 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle not visible scroll displacement [%f]\n", mScrollDisplacement.x);
336 if ( handleVisible && // Ensure the handle is visible.
337 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
338 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
339 ( newHandlePosition != mSelectionHandleOnePosition ) )
341 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
343 currentSelectionHandlePosition = newHandlePosition;
345 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
347 ShowUpdatedHighlight();
349 // Set Active Style to that of first character in selection
350 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
352 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
353 mTextStyle.SetInputStyle( inputStyle );
355 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
361 void Decorator::PositionGrabHandle( std::size_t positionInText )
363 bool direction(false);
364 Vector3 alternatePosition;
365 bool alternatePositionValid(false);
367 mGrabHandlePosition = positionInText;
369 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
370 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
372 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
375 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
377 mActualGrabHandlePosition.x += displacement.x;
378 mActualGrabHandlePosition.y += displacement.y;
380 // Grab handle should jump to the nearest character and take cursor with it
381 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
383 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
385 bool handleVisible = true;
387 if( mTextViewCharacterPositioning.IsScrollEnabled() )
389 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
390 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
391 // Scrolls the text if the handle is not in a visible position
392 handleVisible = IsPositionWithinControl( actualHandlePosition,
396 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveGrabHandle handleVisible[%s]\n", ( handleVisible )?"true":"false");
401 mCurrentHandlePosition = actualHandlePosition;
402 mScrollDisplacement = Vector2::ZERO;
406 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
408 mScrollDisplacement.x = -SCROLL_SPEED;
410 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
412 mScrollDisplacement.x = SCROLL_SPEED;
414 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
416 mScrollDisplacement.y = -SCROLL_SPEED;
418 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
420 mScrollDisplacement.y = SCROLL_SPEED;
426 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
427 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
429 mActualGrabHandlePosition = actualHandlePosition;
430 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
432 //PositionGrabHandle( newHandlePosition );
433 mGrabHandlePosition = newHandlePosition;
434 SetCurrentCursorPosition( mGrabHandlePosition );
435 DrawCursor( mGrabHandlePosition );
437 const std::size_t cursorPosition = GetCurrentCursorPosition();
439 // Let keyboard know the new cursor position so can 're-capture' for prediction.
440 mCursorRePositionedSignal.Emit();
442 // Set Input Style to that of cursor position
443 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
445 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
446 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
447 mTextStyle.SetInputStyle( inputStyle );
452 void Decorator::ShowGrabHandle( bool visible )
454 mGrabHandleVisibility = visible;
455 mTextInputHandles.SetGrabHandleVisibility( visible );
458 void Decorator::CreateGrabHandle( Actor targetParent )
460 if ( !mPanGestureDetector )
462 mPanGestureDetector = PanGestureDetector::New();
463 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
466 if ( !mTextInputHandles.GetGrabHandle() )
468 mTextInputHandles.CreateGrabHandle();
469 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
470 targetParent.Add( mTextInputHandles.GetGrabHandle() );
474 void Decorator::SetGrabHandleImage( Image image )
476 mTextInputHandles.SetGrabHandleImage( image );
479 void Decorator::EnableGrabHandle( bool toggle)
481 // enables grab handle with will in turn de-activate magnifier
482 mGrabHandleEnabled = toggle;
485 bool Decorator::IsGrabHandleEnabled()
487 // if false then magnifier will be shown instead.
488 return mGrabHandleEnabled;
494 std::size_t Decorator::GetCurrentCursorPosition() const
496 return mCursorPosition;
499 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
501 mCursorPosition = newCursorPosition;
504 void Decorator::SetCursorVisibility( bool visible )
506 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
508 mCursorVisibility = visible;
509 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
510 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
513 void Decorator::DrawCursor(const std::size_t nthChar)
515 std::size_t cursorPosition = GetCurrentCursorPosition();
517 // Get height of cursor and set its size
518 Size size( CURSOR_THICKNESS, 0.0f );
519 if ( !mTextViewCharacterPositioning.IsTextEmpty() )
521 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
522 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
526 // Measure Font so know how big text will be if no initial text to measure.
527 size.height = mTextViewCharacterPositioning.GetLineHeight( nthChar );
530 mCursor.SetSize(size);
532 // If the character is italic then the cursor also tilts.
533 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
535 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
536 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
537 mCursor.SetRotation( styleAtCursor.GetItalics() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
540 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
541 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
543 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
544 bool altPositionValid( false ); // Alternate cursor validity flag.
545 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
546 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
548 SetAltCursorEnabled( altPositionValid );
550 if(!altPositionValid)
552 mCursor.SetPosition( position + UI_OFFSET );
557 mCursor.SetSize(size);
558 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
559 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
560 Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max );
561 size.height = rowSize.height * 0.5f;
562 mCursorRTL.SetSize(size);
563 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
566 if( mTextViewCharacterPositioning.IsScrollEnabled() )
568 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
569 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
570 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionWithinControl( position, size, controlSize );
575 void Decorator::SetAltCursorEnabled( bool enabled )
577 mCursorRTLEnabled = enabled;
578 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
581 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
583 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
587 mCursor.SetImage( image );
588 mCursor.SetNinePatchBorder( border );
592 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
594 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
598 mCursorRTL.SetImage( image );
599 mCursorRTL.SetNinePatchBorder( border );
603 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
609 cursor = ImageActor::New( cursorImage );
613 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
616 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
617 cursor.SetNinePatchBorder( border );
618 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
619 cursor.SetVisible(false);
620 cursor.SetName( cursorName );
624 void Decorator::CreateCursors( Actor targetParent )
626 Image mCursorImage = Image::New( DEFAULT_CURSOR );
627 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
628 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
629 targetParent.Add( mCursor );
630 targetParent.Add( mCursorRTL );
633 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
635 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
639 const Size cursorSize( CURSOR_THICKNESS,
640 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
645 void Decorator::StartCursorBlinkTimer()
647 if ( !mCursorBlinkTimer )
649 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
650 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
653 if ( !mCursorBlinkTimer.IsRunning() )
655 mCursorBlinkTimer.Start();
659 void Decorator::StopCursorBlinkTimer()
661 if ( mCursorBlinkTimer )
663 mCursorBlinkTimer.Stop();
667 bool Decorator::OnCursorBlinkTimerTick()
670 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
671 if ( mCursorRTLEnabled )
673 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
675 mCursorBlinkStatus = !mCursorBlinkStatus;
683 void Decorator::ShowUpdatedHighlight()
685 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
686 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
688 // Clamp highlightInfo so they don't exceed the boundary of the control.
689 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
690 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
692 mTextHighlight.UpdateHighlight( highlightInfo );
695 void Decorator::CreateHighlight( Actor parent )
697 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
699 if ( !mHighlightMeshActor )
701 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
702 mHighlightMeshActor.SetName( "HighlightMeshActor" );
703 mHighlightMeshActor.SetInheritShaderEffect( false );
704 mHighlightMeshActor.SetAffectedByLighting(false);
705 parent.Add( mHighlightMeshActor );
709 void Decorator::RemoveHighlight()
711 if ( mHighlightMeshActor )
713 mHighlightMeshActor.Unparent();
714 mHighlightMeshActor.Reset();
715 // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
719 void Decorator::HighlightVisibility( bool visiblility )
721 if ( mHighlightMeshActor )
723 mHighlightMeshActor.SetVisible( visiblility );
728 * Callbacks connected to be Property notifications for Boundary checking.
730 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
731 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
733 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
736 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
738 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
741 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
743 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
746 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
748 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
751 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
753 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
754 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
755 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
756 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
759 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
761 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
762 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
763 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
764 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
767 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
769 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
770 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
771 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
774 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
776 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
777 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
778 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
781 void Decorator::SetUpHandlePropertyNotifications()
783 /* Property notifications for handles exceeding the boundary and returning back within boundary */
785 Vector3 handlesize = GetSelectionHandleSize();
787 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
788 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
790 // Exceeding horizontal boundary
791 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
792 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
794 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
795 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
797 // Within horizontal boundary
798 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
799 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
801 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
802 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
804 // Exceeding vertical boundary
805 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
806 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
807 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
808 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
810 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
811 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
812 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
813 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
815 // Within vertical boundary
816 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
817 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
818 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
819 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
821 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
822 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
823 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
824 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
830 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
837 // When text is selected, show popup above top handle (and text), or below bottom handle.
839 // topHandle: referring to the top most point of the handle or the top line of selection.
840 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
842 topHandle = mSelectionHandleOneActualPosition;
843 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
847 topHandle = mSelectionHandleTwoActualPosition;
848 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
850 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
851 position = Vector3(topHandle.x, topHandle.y, 0.0f);
856 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
858 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
859 Vector3 alternativePosition;
860 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
861 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
863 return alternativePosition;
866 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
868 // When no text is selected, show PopUp at position of cursor
871 std::size_t cursorPosition = GetCurrentCursorPosition();
872 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
873 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
874 position.y -= rowSize.height;
879 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
881 std::size_t cursorPosition = GetCurrentCursorPosition();
882 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
884 if ( mTextInputHandles.GetGrabHandle() )
886 // If grab handle enabled then position pop-up below the grab handle.
887 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
891 alternativePosition.y += mPopUpPanel.GetSize().y;
894 return alternativePosition;
898 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
900 return Vector3::ZERO;
903 void Decorator::ShowPopUp()
906 Vector3 alternativePosition;
909 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
911 if( mHighlightMeshActor ) // Text Selection mode
913 position = PositionOfPopUpRelativeToSelectionHandles();
915 else // Not in Text Selection mode so position relative to cursor.
917 position = PositionOfPopUpRelativeToCursor();
920 // reposition popup above the desired cursor position.
921 mPopUpPanel.Show( mPopUpTarget, true );
922 mPopUpPanel.Self().SetPosition( position );
923 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
925 SetUpPopUpPositionNotifications();
926 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
929 void Decorator::ShowPopUp( Actor target )
931 mPopUpTarget = target;
932 ShowPopupCutCopyPaste();
935 void Decorator::ShowPopupCutCopyPaste()
937 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
938 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
939 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
941 Clipboard clipboard = Clipboard::Get();
942 bool hasClipboardGotContent = clipboard.NumberOfItems();
944 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
948 void Decorator::HidePopUp( bool animate, bool signalFinished )
950 if ( ( mPopUpPanel.GetState() == TextInputPopupNew::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopupNew::StateShown ) )
952 mPopUpPanel.Hide( animate );
956 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
958 mPopUpPanel.AddButton(name, caption, icon, finalOption);
961 void Decorator::ClearPopup()
966 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
968 Vector3 position, alternativePosition;
970 if( mHighlightMeshActor ) // Text Selection mode
972 alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
974 else // Not in Text Selection mode
976 alternativePosition = AlternatePopUpPositionRelativeToCursor();
977 // if can't be positioned above, then position below row.
979 // reposition popup above the desired cursor position.
980 mPopUpPanel.Self().SetPosition( alternativePosition );
983 void Decorator::SetUpPopUpPositionNotifications( )
985 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
987 // Exceeding vertical boundary
988 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
989 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
990 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
991 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
994 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
996 mPopUpButtonPressedSignal.Emit( button );
1000 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
1002 return mPopUpButtonPressedSignal;
1005 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
1007 return mCursorRePositionedSignal;
1011 * Decoration Positioning during Scrolling
1013 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1015 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
1017 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
1018 Size cursorSize( CURSOR_THICKNESS, 0.f );
1020 // Updates the cursor and grab handle position and visibility.
1021 if( mTextInputHandles.GetGrabHandle() || mCursor )
1024 size_t cursorTextPosition = GetCurrentCursorPosition();
1025 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
1027 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
1029 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
1030 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
1032 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
1034 if( mTextInputHandles.GetGrabHandle() )
1036 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1037 PositionGrabHandle( cursorTextPosition );
1042 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1043 DrawCursor( cursorTextPosition );
1044 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
1048 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1049 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1051 // Updates the selection handles and highlighted text position and visibility.
1052 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
1054 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
1055 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
1057 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
1058 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
1060 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
1061 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
1063 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1064 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1066 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1067 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1069 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
1070 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
1072 if( mHighlightMeshActor )
1074 mHighlightMeshActor.SetVisible( true );
1075 ShowUpdatedHighlight();
1080 void Decorator::StartScrollTimer()
1084 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1085 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
1088 if( !mScrollTimer.IsRunning() )
1090 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
1091 mScrollTimer.Start();
1095 void Decorator::StopScrollTimer()
1099 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
1101 mScrollTimer.Stop();
1102 mScrollTimer.Reset();
1106 bool Decorator::OnScrollTimerTick()
1108 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
1110 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
1112 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
1113 if ( mGrabHandlePosition != newGrabHandlePosition )
1115 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1116 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
1117 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
1118 scrollPosition += scrollDelta;
1119 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1121 // If scroll position goes too far TextView will trim it to fit.
1122 if ( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1127 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1131 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1132 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1134 if ( selectionHandleOne && selectionHandleTwo )
1136 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1138 // todo duplicated code should be a function
1140 if ( mSelectionHandleOnePosition != newHandleOnePosition )
1142 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1144 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1146 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1147 scrollPosition += scrollDelta;
1148 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1150 if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1155 mSelectionHandleOnePosition = newHandleOnePosition;
1156 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1160 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1161 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1164 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1166 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1168 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1170 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1172 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1173 scrollPosition += scrollDelta;
1174 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1176 if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1181 mSelectionHandleTwoPosition = newHandleTwoPosition;
1182 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1187 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1188 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1198 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1200 MarkupProcessor::StyledTextArray currentSelectedText;
1202 if ( mHighlightMeshActor ) // Text Selected
1204 MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1205 MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1207 for(; it != end; ++it)
1209 MarkupProcessor::StyledText& styledText( *it );
1210 currentSelectedText.push_back( styledText );
1213 return currentSelectedText;
1218 } // namespace Toolkit