2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali/integration-api/debug.h>
21 #include <dali-toolkit/internal/controls/text-input/text-input-decorator-impl.h>
23 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
33 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
34 const float TOP_HANDLE_TOP_OFFSET(-1.5f); // Offset between top handle and cutCopyPaste pop-up
35 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); // Offset between bottom handle and cutCopyPaste pop-up
36 const float UI_Z_OFFSET( 0.2f ); // Text Selection Handles/Cursor z-offset.
37 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); // Text Selection Handles/Cursor offset.
38 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
39 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
40 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
41 const float CURSOR_THICKNESS(6.0f);
42 const Degree CURSOR_ANGLE_OFFSET(2.0f); // Offset from the angle
44 const unsigned int SCROLL_TICK_INTERVAL = 50u;
45 const float SCROLL_THRESHOLD = 10.f;
46 const float SCROLL_SPEED = 15.f;
49 * Whether the given position plus the cursor size offset is inside the given boundary.
51 * @param[in] position The given position.
52 * @param[in] cursorSize The cursor size.
53 * @param[in] controlSize The given boundary.
54 * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
56 * @return whether the given position is inside the given boundary.
58 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
60 return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
61 ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
62 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
63 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
77 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
78 mTextViewCharacterPositioning( textViewManager ),
79 mTextStyle( textStyle ),
81 mTextHighlight( textViewManager ),
82 mCursorBlinkStatus( true ),
83 mCursorVisibility( true ),
84 mGrabHandleEnabled( true )
88 Decorator::~Decorator()
95 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
97 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
98 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
100 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
101 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
103 const Vector4 boundary( originX,
105 originX + boundingRectangle.width,
106 originY + boundingRectangle.height );
108 mBoundingRectangleWorldCoordinates = boundary;
111 Vector4 Decorator::GetBoundingBox() const
113 return mBoundingRectangleWorldCoordinates;
119 void Decorator::OnHandlePan(Actor actor, PanGesture gesture)
121 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
122 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
124 switch (gesture.state)
126 case Gesture::Started:
127 // fall through so code not duplicated
128 case Gesture::Continuing:
130 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
132 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
135 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
137 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
140 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
142 SetCursorVisibility( true );
143 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
144 MoveGrabHandle( gesture.displacement );
145 HidePopUp(); // Do not show popup while handle is moving
150 case Gesture::Finished:
152 // Revert back to non-pressed selection handle images
153 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
155 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
156 ShowPopupCutCopyPaste();
158 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
160 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
161 ShowPopupCutCopyPaste();
163 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
165 MoveGrabHandle( gesture.displacement );
166 SetCursorVisibility( true );
167 ShowPopupCutCopyPaste();
176 void Decorator::CreateSelectionHandles( Actor targetParent )
178 if ( !mPanGestureDetector )
180 mPanGestureDetector = PanGestureDetector::New();
181 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
184 if ( !mTextInputHandles.GetSelectionHandleOne() )
186 mTextInputHandles.CreateSelectionHandles();
188 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
190 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
191 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
193 SetUpHandlePropertyNotifications();
197 void Decorator::RemoveSelectionHandles()
199 mTextInputHandles.DestorySelectionHandles();
202 Vector3 Decorator::GetSelectionHandleSize()
204 return DEFAULT_SELECTION_HANDLE_SIZE;
207 std::size_t Decorator::GetHandleOnePosition() const
209 return mSelectionHandleOnePosition;
212 std::size_t Decorator::GetHandleTwoPosition() const
214 return mSelectionHandleTwoPosition;
217 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
219 bool direction(false);
220 Vector3 alternatePosition;
221 bool alternatePositionValid(false);
223 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
225 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
229 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
231 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
233 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
235 if( mTextViewCharacterPositioning.IsScrollEnabled() )
237 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
238 const Size cursorSize( GetCursorSizeAt( position ) );
239 bool handleVisible = IsPositionWithinControl( actualPosition, Vector2(DEFAULT_HANDLE_OFFSET.width, DEFAULT_HANDLE_OFFSET.height), controlSize );
241 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::PositionSelectionHandle controlSize[%f,%f] cursorSize[%f,%f] actualPos[%f,%f] visible[%s] \n",
242 controlSize.x, controlSize.y, cursorSize.x, cursorSize.y, actualPosition.x, actualPosition.y, ( handleVisible )?"true":"false" );
244 selectionHandle.SetVisible( handleVisible );
246 return actualPosition;
249 void Decorator::SetSelectionHandlesVisibility(bool visible )
251 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
252 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
255 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
257 mSelectionHandleOnePosition = start;
258 mSelectionHandleTwoPosition = end;
260 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
262 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
263 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
266 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
267 Vector3& actualSelectionHandlePosition,
268 std::size_t& currentSelectionHandlePosition,
269 const Vector2& displacement )
271 Vector3 actualHandlePosition;
272 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
273 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;;
275 // Selection handles should jump to the nearest character
276 std::size_t newHandlePosition = 0;
277 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
279 bool direction(false);
280 Vector3 alternatePosition;
281 bool alternatePositionValid(false);
282 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
284 bool handleVisible = true;
287 if( mTextViewCharacterPositioning.IsScrollEnabled() )
289 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
290 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
292 handleVisible = IsPositionWithinControl( actualHandlePosition,
299 mCurrentHandlePosition = actualHandlePosition;
300 mScrollDisplacement = Vector2::ZERO;
305 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
307 mScrollDisplacement.x = -SCROLL_SPEED;
309 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
311 mScrollDisplacement.x = SCROLL_SPEED;
313 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
315 mScrollDisplacement.y = -SCROLL_SPEED;
317 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
319 mScrollDisplacement.y = SCROLL_SPEED;
321 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle not visible scroll displacement [%f]\n", mScrollDisplacement.x);
327 if ( handleVisible && // Ensure the handle is visible.
328 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
329 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
330 ( newHandlePosition != mSelectionHandleOnePosition ) )
332 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
334 currentSelectionHandlePosition = newHandlePosition;
336 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
338 ShowUpdatedHighlight();
340 // Set Active Style to that of first character in selection
341 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
343 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
344 mTextStyle.SetInputStyle( inputStyle );
346 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
352 void Decorator::PositionGrabHandle( std::size_t positionInText )
354 bool direction(false);
355 Vector3 alternatePosition;
356 bool alternatePositionValid(false);
358 mGrabHandlePosition = positionInText;
360 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
361 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
363 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
366 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
368 mActualGrabHandlePosition.x += displacement.x;
369 mActualGrabHandlePosition.y += displacement.y;
371 // Grab handle should jump to the nearest character and take cursor with it
372 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
374 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
376 bool handleVisible = true;
378 if( mTextViewCharacterPositioning.IsScrollEnabled() )
380 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
381 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
382 // Scrolls the text if the handle is not in a visible position
383 handleVisible = IsPositionWithinControl( actualHandlePosition,
387 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveGrabHandle handleVisible[%s]\n", ( handleVisible )?"true":"false");
392 mCurrentHandlePosition = actualHandlePosition;
393 mScrollDisplacement = Vector2::ZERO;
397 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
399 mScrollDisplacement.x = -SCROLL_SPEED;
401 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
403 mScrollDisplacement.x = SCROLL_SPEED;
405 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
407 mScrollDisplacement.y = -SCROLL_SPEED;
409 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
411 mScrollDisplacement.y = SCROLL_SPEED;
417 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
418 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
420 mActualGrabHandlePosition = actualHandlePosition;
421 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
423 //PositionGrabHandle( newHandlePosition );
424 mGrabHandlePosition = newHandlePosition;
425 SetCurrentCursorPosition( mGrabHandlePosition );
426 DrawCursor( mGrabHandlePosition );
428 const std::size_t cursorPosition = GetCurrentCursorPosition();
430 // Let keyboard know the new cursor position so can 're-capture' for prediction.
431 mCursorRePositionedSignal.Emit();
433 // Set Input Style to that of cursor position
434 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
436 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
437 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
438 mTextStyle.SetInputStyle( inputStyle );
443 void Decorator::ShowGrabHandle( bool visible )
445 mGrabHandleVisibility = visible;
446 mTextInputHandles.SetGrabHandleVisibility( visible );
449 void Decorator::CreateGrabHandle( Actor targetParent )
451 if ( !mPanGestureDetector )
453 mPanGestureDetector = PanGestureDetector::New();
454 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
457 if ( !mTextInputHandles.GetGrabHandle() )
459 mTextInputHandles.CreateGrabHandle();
460 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
461 targetParent.Add( mTextInputHandles.GetGrabHandle() );
465 void Decorator::SetGrabHandleImage( Image image )
467 mTextInputHandles.SetGrabHandleImage( image );
470 void Decorator::EnableGrabHandle( bool toggle)
472 // enables grab handle with will in turn de-activate magnifier
473 mGrabHandleEnabled = toggle;
476 bool Decorator::IsGrabHandleEnabled()
478 // if false then magnifier will be shown instead.
479 return mGrabHandleEnabled;
485 std::size_t Decorator::GetCurrentCursorPosition() const
487 return mCursorPosition;
490 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
492 mCursorPosition = newCursorPosition;
495 void Decorator::SetCursorVisibility( bool visible )
497 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
499 mCursorVisibility = visible;
500 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
501 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
504 void Decorator::DrawCursor(const std::size_t nthChar)
506 std::size_t cursorPosition = GetCurrentCursorPosition();
508 // Get height of cursor and set its size
509 Size size( CURSOR_THICKNESS, 0.0f );
510 if ( !mTextViewCharacterPositioning.IsTextEmpty() )
512 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
513 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
517 // Measure Font so know how big text will be if no initial text to measure.
518 size.height = mTextViewCharacterPositioning.GetLineHeight( nthChar );
521 mCursor.SetSize(size);
523 // If the character is italic then the cursor also tilts.
524 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
526 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
527 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
528 mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
531 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
532 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
534 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
535 bool altPositionValid( false ); // Alternate cursor validity flag.
536 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
537 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
539 SetAltCursorEnabled( altPositionValid );
541 if(!altPositionValid)
543 mCursor.SetPosition( position + UI_OFFSET );
548 mCursor.SetSize(size);
549 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
550 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
551 Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max );
552 size.height = rowSize.height * 0.5f;
553 mCursorRTL.SetSize(size);
554 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
557 if( mTextViewCharacterPositioning.IsScrollEnabled() )
559 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
560 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
561 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionWithinControl( position, size, controlSize );
566 void Decorator::SetAltCursorEnabled( bool enabled )
568 mCursorRTLEnabled = enabled;
569 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
572 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
574 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
578 mCursor.SetImage( image );
579 mCursor.SetNinePatchBorder( border );
583 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
585 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
589 mCursorRTL.SetImage( image );
590 mCursorRTL.SetNinePatchBorder( border );
594 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
600 cursor = ImageActor::New( cursorImage );
604 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
607 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
608 cursor.SetNinePatchBorder( border );
609 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
610 cursor.SetVisible(false);
611 cursor.SetName( cursorName );
615 void Decorator::CreateCursors( Actor targetParent )
617 Image mCursorImage = Image::New( DEFAULT_CURSOR );
618 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
619 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
620 targetParent.Add( mCursor );
621 targetParent.Add( mCursorRTL );
624 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
626 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
630 const Size cursorSize( CURSOR_THICKNESS,
631 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
636 void Decorator::StartCursorBlinkTimer()
638 if ( !mCursorBlinkTimer )
640 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
641 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
644 if ( !mCursorBlinkTimer.IsRunning() )
646 mCursorBlinkTimer.Start();
650 void Decorator::StopCursorBlinkTimer()
652 if ( mCursorBlinkTimer )
654 mCursorBlinkTimer.Stop();
658 bool Decorator::OnCursorBlinkTimerTick()
661 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
662 if ( mCursorRTLEnabled )
664 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
666 mCursorBlinkStatus = !mCursorBlinkStatus;
674 void Decorator::ShowUpdatedHighlight()
676 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
677 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
679 // Clamp highlightInfo so they don't exceed the boundary of the control.
680 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
681 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
683 mTextHighlight.UpdateHighlight( highlightInfo );
686 void Decorator::CreateHighlight( Actor parent )
688 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
690 if ( !mHighlightMeshActor )
692 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
693 mHighlightMeshActor.SetName( "HighlightMeshActor" );
694 mHighlightMeshActor.SetAffectedByLighting(false);
695 parent.Add( mHighlightMeshActor );
699 void Decorator::RemoveHighlight()
701 if ( mHighlightMeshActor )
703 mHighlightMeshActor.Unparent();
704 mHighlightMeshActor.Reset();
705 // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
709 void Decorator::HighlightVisibility( bool visiblility )
711 if ( mHighlightMeshActor )
713 mHighlightMeshActor.SetVisible( visiblility );
718 * Callbacks connected to be Property notifications for Boundary checking.
720 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
721 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
723 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
726 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
728 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
731 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
733 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
736 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
738 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
741 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
743 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
744 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
745 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
746 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
749 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
751 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
752 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
753 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
754 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
757 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
759 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
760 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
761 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
764 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
766 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
767 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
768 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
771 void Decorator::SetUpHandlePropertyNotifications()
773 /* Property notifications for handles exceeding the boundary and returning back within boundary */
775 Vector3 handlesize = GetSelectionHandleSize();
777 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
778 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
780 // Exceeding horizontal boundary
781 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
782 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
784 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
785 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
787 // Within horizontal boundary
788 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
789 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
791 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
792 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
794 // Exceeding vertical boundary
795 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
796 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
797 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
798 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
800 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
801 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
802 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
803 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
805 // Within vertical boundary
806 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
807 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
808 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
809 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
811 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
812 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
813 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
814 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
820 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
827 // When text is selected, show popup above top handle (and text), or below bottom handle.
829 // topHandle: referring to the top most point of the handle or the top line of selection.
830 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
832 topHandle = mSelectionHandleOneActualPosition;
833 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
837 topHandle = mSelectionHandleTwoActualPosition;
838 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
840 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
841 position = Vector3(topHandle.x, topHandle.y, 0.0f);
846 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
848 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
849 Vector3 alternativePosition;
850 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
851 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
853 return alternativePosition;
856 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
858 // When no text is selected, show PopUp at position of cursor
861 std::size_t cursorPosition = GetCurrentCursorPosition();
862 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
863 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
864 position.y -= rowSize.height;
869 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
871 std::size_t cursorPosition = GetCurrentCursorPosition();
872 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
874 if ( mTextInputHandles.GetGrabHandle() )
876 // If grab handle enabled then position pop-up below the grab handle.
877 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
881 alternativePosition.y += mPopUpPanel.GetSize().y;
884 return alternativePosition;
888 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
890 return Vector3::ZERO;
893 void Decorator::ShowPopUp()
896 Vector3 alternativePosition;
899 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
901 if( mHighlightMeshActor ) // Text Selection mode
903 position = PositionOfPopUpRelativeToSelectionHandles();
905 else // Not in Text Selection mode so position relative to cursor.
907 position = PositionOfPopUpRelativeToCursor();
910 // reposition popup above the desired cursor position.
911 mPopUpPanel.Show( mPopUpTarget, true );
912 mPopUpPanel.Self().SetPosition( position );
913 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
915 SetUpPopUpPositionNotifications();
916 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
919 void Decorator::ShowPopUp( Actor target )
921 mPopUpTarget = target;
922 ShowPopupCutCopyPaste();
925 void Decorator::ShowPopupCutCopyPaste()
927 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
928 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
929 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
931 Clipboard clipboard = Clipboard::Get();
932 bool hasClipboardGotContent = clipboard.NumberOfItems();
934 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
938 void Decorator::HidePopUp( bool animate, bool signalFinished )
940 if ( ( mPopUpPanel.GetState() == TextInputPopupNew::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopupNew::StateShown ) )
942 mPopUpPanel.Hide( animate );
946 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
948 mPopUpPanel.AddButton(name, caption, icon, finalOption);
951 void Decorator::ClearPopup()
956 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
958 Vector3 position, alternativePosition;
960 if( mHighlightMeshActor ) // Text Selection mode
962 alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
964 else // Not in Text Selection mode
966 alternativePosition = AlternatePopUpPositionRelativeToCursor();
967 // if can't be positioned above, then position below row.
969 // reposition popup above the desired cursor position.
970 mPopUpPanel.Self().SetPosition( alternativePosition );
973 void Decorator::SetUpPopUpPositionNotifications( )
975 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
977 // Exceeding vertical boundary
978 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
979 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
980 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
981 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
984 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
986 mPopUpButtonPressedSignal.Emit( button );
990 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
992 return mPopUpButtonPressedSignal;
995 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
997 return mCursorRePositionedSignal;
1001 * Decoration Positioning during Scrolling
1003 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1005 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
1007 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
1008 Size cursorSize( CURSOR_THICKNESS, 0.f );
1010 // Updates the cursor and grab handle position and visibility.
1011 if( mTextInputHandles.GetGrabHandle() || mCursor )
1014 size_t cursorTextPosition = GetCurrentCursorPosition();
1015 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
1017 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
1019 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
1020 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
1022 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
1024 if( mTextInputHandles.GetGrabHandle() )
1026 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1027 PositionGrabHandle( cursorTextPosition );
1032 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1033 DrawCursor( cursorTextPosition );
1034 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
1038 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1039 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1041 // Updates the selection handles and highlighted text position and visibility.
1042 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
1044 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
1045 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
1047 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
1048 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
1050 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
1051 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
1053 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1054 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1056 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1057 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1059 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
1060 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
1062 if( mHighlightMeshActor )
1064 mHighlightMeshActor.SetVisible( true );
1065 ShowUpdatedHighlight();
1070 void Decorator::StartScrollTimer()
1074 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1075 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
1078 if( !mScrollTimer.IsRunning() )
1080 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
1081 mScrollTimer.Start();
1085 void Decorator::StopScrollTimer()
1089 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
1091 mScrollTimer.Stop();
1092 mScrollTimer.Reset();
1096 bool Decorator::OnScrollTimerTick()
1098 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
1100 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
1102 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
1103 if ( mGrabHandlePosition != newGrabHandlePosition )
1105 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1106 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
1107 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
1108 scrollPosition += scrollDelta;
1109 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1111 // If scroll position goes too far TextView will trim it to fit.
1112 if ( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1117 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1121 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1122 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1124 if ( selectionHandleOne && selectionHandleTwo )
1126 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1128 // todo duplicated code should be a function
1130 if ( mSelectionHandleOnePosition != newHandleOnePosition )
1132 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1134 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1136 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1137 scrollPosition += scrollDelta;
1138 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1140 if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1145 mSelectionHandleOnePosition = newHandleOnePosition;
1146 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1150 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1151 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1154 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1156 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1158 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1160 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1162 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1163 scrollPosition += scrollDelta;
1164 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1166 if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1171 mSelectionHandleTwoPosition = newHandleTwoPosition;
1172 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1177 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1178 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1188 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1190 MarkupProcessor::StyledTextArray currentSelectedText;
1192 if ( mHighlightMeshActor ) // Text Selected
1194 MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1195 MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1197 for(; it != end; ++it)
1199 MarkupProcessor::StyledText& styledText( *it );
1200 currentSelectedText.push_back( styledText );
1203 return currentSelectedText;
1208 } // namespace Toolkit