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);
76 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
77 mTextViewCharacterPositioning( textViewManager ),
78 mTextStyle( textStyle ),
80 mTextHighlight( textViewManager ),
81 mCursorBlinkStatus( true ),
82 mCursorVisibility( true ),
83 mGrabHandleEnabled( true )
87 Decorator::~Decorator()
94 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
96 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
97 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
99 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
100 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
102 const Vector4 boundary( originX,
104 originX + boundingRectangle.width,
105 originY + boundingRectangle.height );
107 mBoundingRectangleWorldCoordinates = boundary;
110 Vector4 Decorator::GetBoundingBox() const
112 return mBoundingRectangleWorldCoordinates;
118 void Decorator::OnHandlePan(Actor actor, PanGesture gesture)
120 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
121 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
123 switch (gesture.state)
125 case Gesture::Started:
126 // fall through so code not duplicated
127 case Gesture::Continuing:
129 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
131 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
134 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
136 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
139 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
141 SetCursorVisibility( true );
142 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
143 MoveGrabHandle( gesture.displacement );
144 HidePopUp(); // Do not show popup while handle is moving
149 case Gesture::Finished:
151 // Revert back to non-pressed selection handle images
152 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
154 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
155 ShowPopupCutCopyPaste();
157 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
159 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
160 ShowPopupCutCopyPaste();
162 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
164 MoveGrabHandle( gesture.displacement );
165 SetCursorVisibility( true );
166 ShowPopupCutCopyPaste();
175 void Decorator::CreateSelectionHandles( Actor targetParent )
177 if ( !mPanGestureDetector )
179 mPanGestureDetector = PanGestureDetector::New();
180 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
183 if ( !mTextInputHandles.GetSelectionHandleOne() )
185 mTextInputHandles.CreateSelectionHandles();
187 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
189 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
190 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
192 SetUpHandlePropertyNotifications();
196 void Decorator::RemoveSelectionHandles()
198 mTextInputHandles.DestorySelectionHandles();
201 Vector3 Decorator::GetSelectionHandleSize()
203 return DEFAULT_SELECTION_HANDLE_SIZE;
206 std::size_t Decorator::GetHandleOnePosition() const
208 return mSelectionHandleOnePosition;
211 std::size_t Decorator::GetHandleTwoPosition() const
213 return mSelectionHandleTwoPosition;
216 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
218 bool direction(false);
219 Vector3 alternatePosition;
220 bool alternatePositionValid(false);
222 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
224 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
228 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
230 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
232 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
234 if( mTextViewCharacterPositioning.IsScrollEnabled() )
236 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
237 const Size cursorSize( GetCursorSizeAt( position ) );
238 bool handleVisible = IsPositionWithinControl( actualPosition, Vector2(DEFAULT_HANDLE_OFFSET.width, DEFAULT_HANDLE_OFFSET.height), controlSize );
240 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::PositionSelectionHandle controlSize[%f,%f] cursorSize[%f,%f] actualPos[%f,%f] visible[%s] \n",
241 controlSize.x, controlSize.y, cursorSize.x, cursorSize.y, actualPosition.x, actualPosition.y, ( handleVisible )?"true":"false" );
243 selectionHandle.SetVisible( handleVisible );
245 return actualPosition;
248 void Decorator::SetSelectionHandlesVisibility(bool visible )
250 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
251 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
254 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
256 mSelectionHandleOnePosition = start;
257 mSelectionHandleTwoPosition = end;
259 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
261 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
262 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
265 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
266 Vector3& actualSelectionHandlePosition,
267 std::size_t& currentSelectionHandlePosition,
268 const Vector2& displacement )
270 Vector3 actualHandlePosition;
271 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
272 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;;
274 // Selection handles should jump to the nearest character
275 std::size_t newHandlePosition = 0;
276 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
278 bool direction(false);
279 Vector3 alternatePosition;
280 bool alternatePositionValid(false);
281 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
283 bool handleVisible = true;
286 if( mTextViewCharacterPositioning.IsScrollEnabled() )
288 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
289 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
291 handleVisible = IsPositionWithinControl( actualHandlePosition,
298 mCurrentHandlePosition = actualHandlePosition;
299 mScrollDisplacement = Vector2::ZERO;
304 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
306 mScrollDisplacement.x = -SCROLL_SPEED;
308 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
310 mScrollDisplacement.x = SCROLL_SPEED;
312 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
314 mScrollDisplacement.y = -SCROLL_SPEED;
316 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
318 mScrollDisplacement.y = SCROLL_SPEED;
320 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle not visible scroll displacement [%f]\n", mScrollDisplacement.x);
326 if ( handleVisible && // Ensure the handle is visible.
327 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
328 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
329 ( newHandlePosition != mSelectionHandleOnePosition ) )
331 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
333 currentSelectionHandlePosition = newHandlePosition;
335 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
337 ShowUpdatedHighlight();
339 // Set Active Style to that of first character in selection
340 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
342 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
343 mTextStyle.SetInputStyle( inputStyle );
345 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
351 void Decorator::PositionGrabHandle( std::size_t positionInText )
353 bool direction(false);
354 Vector3 alternatePosition;
355 bool alternatePositionValid(false);
357 mGrabHandlePosition = positionInText;
359 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
360 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
362 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
365 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
367 mActualGrabHandlePosition.x += displacement.x;
368 mActualGrabHandlePosition.y += displacement.y;
370 // Grab handle should jump to the nearest character and take cursor with it
371 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
373 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
375 bool handleVisible = true;
377 if( mTextViewCharacterPositioning.IsScrollEnabled() )
379 const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
380 const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
381 // Scrolls the text if the handle is not in a visible position
382 handleVisible = IsPositionWithinControl( actualHandlePosition,
386 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveGrabHandle handleVisible[%s]\n", ( handleVisible )?"true":"false");
391 mCurrentHandlePosition = actualHandlePosition;
392 mScrollDisplacement = Vector2::ZERO;
396 if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
398 mScrollDisplacement.x = -SCROLL_SPEED;
400 else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
402 mScrollDisplacement.x = SCROLL_SPEED;
404 if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
406 mScrollDisplacement.y = -SCROLL_SPEED;
408 else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
410 mScrollDisplacement.y = SCROLL_SPEED;
416 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
417 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
419 mActualGrabHandlePosition = actualHandlePosition;
420 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
422 //PositionGrabHandle( newHandlePosition );
423 mGrabHandlePosition = newHandlePosition;
424 SetCurrentCursorPosition( mGrabHandlePosition );
425 DrawCursor( mGrabHandlePosition );
427 const std::size_t cursorPosition = GetCurrentCursorPosition();
429 // Let keyboard know the new cursor position so can 're-capture' for prediction.
430 mCursorRePositionedSignal.Emit();
432 // Set Input Style to that of cursor position
433 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
435 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
436 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
437 mTextStyle.SetInputStyle( inputStyle );
442 void Decorator::ShowGrabHandle( bool visible )
444 mGrabHandleVisibility = visible;
445 mTextInputHandles.SetGrabHandleVisibility( visible );
448 void Decorator::CreateGrabHandle( Actor targetParent )
450 if ( !mPanGestureDetector )
452 mPanGestureDetector = PanGestureDetector::New();
453 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
456 if ( !mTextInputHandles.GetGrabHandle() )
458 mTextInputHandles.CreateGrabHandle();
459 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
460 targetParent.Add( mTextInputHandles.GetGrabHandle() );
464 void Decorator::SetGrabHandleImage( Image image )
466 mTextInputHandles.SetGrabHandleImage( image );
469 void Decorator::EnableGrabHandle( bool toggle)
471 // enables grab handle with will in turn de-activate magnifier
472 mGrabHandleEnabled = toggle;
475 bool Decorator::IsGrabHandleEnabled()
477 // if false then magnifier will be shown instead.
478 return mGrabHandleEnabled;
484 std::size_t Decorator::GetCurrentCursorPosition() const
486 return mCursorPosition;
489 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
491 mCursorPosition = newCursorPosition;
494 void Decorator::SetCursorVisibility( bool visible )
496 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
498 mCursorVisibility = visible;
499 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
500 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
503 void Decorator::DrawCursor(const std::size_t nthChar)
505 std::size_t cursorPosition = GetCurrentCursorPosition();
507 // Get height of cursor and set its size
508 Size size( CURSOR_THICKNESS, 0.0f );
509 if ( !mTextViewCharacterPositioning.IsTextEmpty() )
511 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
512 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
516 // Measure Font so know how big text will be if no initial text to measure.
517 size.height = mTextViewCharacterPositioning.GetLineHeight( nthChar );
520 mCursor.SetSize(size);
522 // If the character is italic then the cursor also tilts.
523 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
525 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
526 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
527 mCursor.SetRotation( styleAtCursor.GetItalics() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
530 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
531 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
533 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
534 bool altPositionValid( false ); // Alternate cursor validity flag.
535 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
536 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
538 SetAltCursorEnabled( altPositionValid );
540 if(!altPositionValid)
542 mCursor.SetPosition( position + UI_OFFSET );
547 mCursor.SetSize(size);
548 mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
549 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
550 Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max );
551 size.height = rowSize.height * 0.5f;
552 mCursorRTL.SetSize(size);
553 mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
556 if( mTextViewCharacterPositioning.IsScrollEnabled() )
558 // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
559 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
560 mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionWithinControl( position, size, controlSize );
565 void Decorator::SetAltCursorEnabled( bool enabled )
567 mCursorRTLEnabled = enabled;
568 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
571 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
573 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
577 mCursor.SetImage( image );
578 mCursor.SetNinePatchBorder( border );
582 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
584 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
588 mCursorRTL.SetImage( image );
589 mCursorRTL.SetNinePatchBorder( border );
593 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
599 cursor = ImageActor::New( cursorImage );
603 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
606 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
607 cursor.SetNinePatchBorder( border );
608 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
609 cursor.SetVisible(false);
610 cursor.SetName( cursorName );
614 void Decorator::CreateCursors( Actor targetParent )
616 Image mCursorImage = Image::New( DEFAULT_CURSOR );
617 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
618 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
619 targetParent.Add( mCursor );
620 targetParent.Add( mCursorRTL );
623 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
625 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
629 const Size cursorSize( CURSOR_THICKNESS,
630 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
635 void Decorator::StartCursorBlinkTimer()
637 if ( !mCursorBlinkTimer )
639 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
640 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
643 if ( !mCursorBlinkTimer.IsRunning() )
645 mCursorBlinkTimer.Start();
649 void Decorator::StopCursorBlinkTimer()
651 if ( mCursorBlinkTimer )
653 mCursorBlinkTimer.Stop();
657 bool Decorator::OnCursorBlinkTimerTick()
660 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
661 if ( mCursorRTLEnabled )
663 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
665 mCursorBlinkStatus = !mCursorBlinkStatus;
673 void Decorator::ShowUpdatedHighlight()
675 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
676 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
678 // Clamp highlightInfo so they don't exceed the boundary of the control.
679 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
680 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
682 mTextHighlight.UpdateHighlight( highlightInfo );
685 void Decorator::CreateHighlight( Actor parent )
687 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
689 if ( !mHighlightMeshActor )
691 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
692 mHighlightMeshActor.SetName( "HighlightMeshActor" );
693 mHighlightMeshActor.SetInheritShaderEffect( false );
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