2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/text-input/text-input-decorator-impl.h>
22 #include <dali/public-api/adaptor-framework/clipboard.h>
23 #include <dali/public-api/common/stage.h>
24 #include <dali/public-api/events/pan-gesture.h>
25 #include <dali/public-api/object/property-notification.h>
26 #include <dali/integration-api/debug.h>
29 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
35 #if defined(DEBUG_ENABLED)
36 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
39 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
40 const float TOP_HANDLE_TOP_OFFSET(-1.5f); // Offset between top handle and cutCopyPaste pop-up
41 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); // Offset between bottom handle and cutCopyPaste pop-up
42 const float UI_Z_OFFSET( 0.2f ); // Text Selection Handles/Cursor z-offset.
43 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); // Text Selection Handles/Cursor offset.
44 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
45 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
46 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
47 const float CURSOR_THICKNESS(6.0f);
48 const Degree CURSOR_ANGLE_OFFSET(2.0f); // Offset from the angle
50 const unsigned int SCROLL_TICK_INTERVAL = 50u;
51 const float SCROLL_THRESHOLD = 10.f;
52 const float SCROLL_SPEED = 15.f;
55 * Whether the given position plus the cursor size offset is inside the given boundary.
57 * @param[in] position The given position.
58 * @param[in] cursorSize The cursor size.
59 * @param[in] controlSize The given boundary.
60 * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
62 * @return whether the given position is inside the given boundary.
64 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
66 return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
67 ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
68 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
69 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
83 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
84 mTextViewCharacterPositioning( textViewManager ),
85 mTextStyle( textStyle ),
86 mSelectionHandleOnePosition(0),
87 mSelectionHandleTwoPosition(0),
88 mGrabHandlePosition(0),
90 mTextHighlight( textViewManager ),
91 mCursorBlinkStatus( true ),
92 mCursorVisibility( true ),
93 mCursorRTLEnabled( false ),
94 mIsGrabHandleInScrollArea( false ),
95 mIsCursorInScrollArea( false ),
96 mGrabHandleEnabled( true )
100 Decorator::~Decorator()
107 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
109 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
110 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
112 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
113 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
115 const Vector4 boundary( originX,
117 originX + boundingRectangle.width,
118 originY + boundingRectangle.height );
120 mBoundingRectangleWorldCoordinates = boundary;
123 Vector4 Decorator::GetBoundingBox() const
125 return mBoundingRectangleWorldCoordinates;
131 void Decorator::OnHandlePan(Actor actor, PanGesture gesture)
133 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
134 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
136 switch (gesture.state)
138 case Gesture::Started:
139 // fall through so code not duplicated
140 case Gesture::Continuing:
142 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
144 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
147 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
149 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
152 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
154 SetCursorVisibility( true );
155 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
156 MoveGrabHandle( gesture.displacement );
157 HidePopUp(); // Do not show popup while handle is moving
162 case Gesture::Finished:
164 // Revert back to non-pressed selection handle images
165 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
167 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
168 ShowPopupCutCopyPaste();
170 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
172 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
173 ShowPopupCutCopyPaste();
175 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
177 MoveGrabHandle( gesture.displacement );
178 SetCursorVisibility( true );
179 ShowPopupCutCopyPaste();
188 void Decorator::CreateSelectionHandles( Actor targetParent )
190 if ( !mPanGestureDetector )
192 mPanGestureDetector = PanGestureDetector::New();
193 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
196 if ( !mTextInputHandles.GetSelectionHandleOne() )
198 mTextInputHandles.CreateSelectionHandles();
200 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
202 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
203 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
205 SetUpHandlePropertyNotifications();
209 void Decorator::RemoveSelectionHandles()
211 mTextInputHandles.DestorySelectionHandles();
214 Vector3 Decorator::GetSelectionHandleSize()
216 return DEFAULT_SELECTION_HANDLE_SIZE;
219 std::size_t Decorator::GetHandleOnePosition() const
221 return mSelectionHandleOnePosition;
224 std::size_t Decorator::GetHandleTwoPosition() const
226 return mSelectionHandleTwoPosition;
229 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
231 bool direction(false);
232 Vector3 alternatePosition;
233 bool alternatePositionValid(false);
235 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
237 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
241 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
243 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
245 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
247 return actualPosition;
250 void Decorator::SetSelectionHandlesVisibility(bool visible )
252 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
253 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
256 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
258 mSelectionHandleOnePosition = start;
259 mSelectionHandleTwoPosition = end;
261 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
263 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
264 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
267 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
268 Vector3& actualSelectionHandlePosition,
269 std::size_t& currentSelectionHandlePosition,
270 const Vector2& displacement )
272 Vector3 actualHandlePosition;
273 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
274 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;
276 // Selection handles should jump to the nearest character
277 std::size_t newHandlePosition = 0;
278 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
280 bool direction(false);
281 Vector3 alternatePosition;
282 bool alternatePositionValid(false);
283 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
285 bool handleVisible = true;
287 if ( handleVisible && // Ensure the handle is visible.
288 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
289 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
290 ( newHandlePosition != mSelectionHandleOnePosition ) )
292 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
294 currentSelectionHandlePosition = newHandlePosition;
296 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
298 ShowUpdatedHighlight();
300 // Set Active Style to that of first character in selection
301 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
303 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
304 mTextStyle.SetInputStyle( inputStyle );
306 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
312 void Decorator::PositionGrabHandle( std::size_t positionInText )
314 bool direction(false);
315 Vector3 alternatePosition;
316 bool alternatePositionValid(false);
318 mGrabHandlePosition = positionInText;
320 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
321 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
323 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
326 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
328 mActualGrabHandlePosition.x += displacement.x;
329 mActualGrabHandlePosition.y += displacement.y;
331 // Grab handle should jump to the nearest character and take cursor with it
332 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
334 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
336 bool handleVisible = true;
338 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
339 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
341 mActualGrabHandlePosition = actualHandlePosition;
342 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
344 //PositionGrabHandle( newHandlePosition );
345 mGrabHandlePosition = newHandlePosition;
346 SetCurrentCursorPosition( mGrabHandlePosition );
347 DrawCursor( mGrabHandlePosition );
349 const std::size_t cursorPosition = GetCurrentCursorPosition();
351 // Let keyboard know the new cursor position so can 're-capture' for prediction.
352 mCursorRePositionedSignal.Emit();
354 // Set Input Style to that of cursor position
355 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
357 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
362 void Decorator::ShowGrabHandle( bool visible )
364 mGrabHandleVisibility = visible;
365 mTextInputHandles.SetGrabHandleVisibility( visible );
368 void Decorator::CreateGrabHandle( Actor targetParent )
370 if ( !mPanGestureDetector )
372 mPanGestureDetector = PanGestureDetector::New();
373 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
376 if ( !mTextInputHandles.GetGrabHandle() )
378 mTextInputHandles.CreateGrabHandle();
379 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
380 targetParent.Add( mTextInputHandles.GetGrabHandle() );
384 void Decorator::SetGrabHandleImage( Image image )
386 mTextInputHandles.SetGrabHandleImage( image );
389 void Decorator::EnableGrabHandle( bool toggle)
391 // enables grab handle with will in turn de-activate magnifier
392 mGrabHandleEnabled = toggle;
395 bool Decorator::IsGrabHandleEnabled()
397 // if false then magnifier will be shown instead.
398 return mGrabHandleEnabled;
404 std::size_t Decorator::GetCurrentCursorPosition() const
406 return mCursorPosition;
409 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
411 mCursorPosition = newCursorPosition;
414 void Decorator::SetCursorVisibility( bool visible )
416 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
418 mCursorVisibility = visible;
419 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
420 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
423 void Decorator::DrawCursor(const std::size_t nthChar)
425 std::size_t cursorPosition = GetCurrentCursorPosition();
427 // Get height of cursor and set its size
428 Size size( CURSOR_THICKNESS, 0.0f );
430 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
431 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
433 mCursor.SetSize(size);
435 // If the character is italic then the cursor also tilts.
436 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
438 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
439 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
440 mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
443 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
444 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
446 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
447 bool altPositionValid( false ); // Alternate cursor validity flag.
448 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
449 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
451 SetAltCursorEnabled( altPositionValid );
453 mCursor.SetPosition( position + UI_OFFSET );
457 void Decorator::SetAltCursorEnabled( bool enabled )
459 mCursorRTLEnabled = enabled;
460 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
463 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
465 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
469 mCursor.SetImage( image );
470 mCursor.SetNinePatchBorder( border );
474 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
476 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
480 mCursorRTL.SetImage( image );
481 mCursorRTL.SetNinePatchBorder( border );
485 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
491 cursor = ImageActor::New( cursorImage );
495 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
498 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
499 cursor.SetNinePatchBorder( border );
500 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
501 cursor.SetVisible(false);
502 cursor.SetName( cursorName );
506 void Decorator::CreateCursors( Actor targetParent )
508 Image mCursorImage = Image::New( DEFAULT_CURSOR );
509 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
510 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
511 targetParent.Add( mCursor );
512 targetParent.Add( mCursorRTL );
515 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
517 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
521 const Size cursorSize( CURSOR_THICKNESS,
522 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
527 void Decorator::StartCursorBlinkTimer()
529 if ( !mCursorBlinkTimer )
531 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
532 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
535 if ( !mCursorBlinkTimer.IsRunning() )
537 mCursorBlinkTimer.Start();
541 void Decorator::StopCursorBlinkTimer()
543 if ( mCursorBlinkTimer )
545 mCursorBlinkTimer.Stop();
549 bool Decorator::OnCursorBlinkTimerTick()
552 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
553 if ( mCursorRTLEnabled )
555 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
557 mCursorBlinkStatus = !mCursorBlinkStatus;
565 void Decorator::ShowUpdatedHighlight()
567 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
568 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
570 // Clamp highlightInfo so they don't exceed the boundary of the control.
571 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
572 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
574 mTextHighlight.UpdateHighlight( highlightInfo );
577 void Decorator::CreateHighlight( Actor parent )
579 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
581 if ( !mHighlightMeshActor )
583 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
584 mHighlightMeshActor.SetName( "HighlightMeshActor" );
585 mHighlightMeshActor.SetAffectedByLighting(false);
586 parent.Add( mHighlightMeshActor );
590 void Decorator::RemoveHighlight()
592 if ( mHighlightMeshActor )
594 mHighlightMeshActor.Unparent();
595 mHighlightMeshActor.Reset();
596 // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
600 void Decorator::HighlightVisibility( bool visiblility )
602 if ( mHighlightMeshActor )
604 mHighlightMeshActor.SetVisible( visiblility );
609 * Callbacks connected to be Property notifications for Boundary checking.
611 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
612 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
614 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
617 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
619 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
622 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
624 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
627 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
629 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
632 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
634 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
635 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
636 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
637 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
640 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
642 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
643 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
644 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
645 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
648 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
650 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
651 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
652 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
655 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
657 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
658 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
659 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
662 void Decorator::SetUpHandlePropertyNotifications()
664 /* Property notifications for handles exceeding the boundary and returning back within boundary */
666 Vector3 handlesize = GetSelectionHandleSize();
668 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
669 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
671 // Exceeding horizontal boundary
672 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
673 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
675 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
676 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
678 // Within horizontal boundary
679 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
680 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
682 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
683 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
685 // Exceeding vertical boundary
686 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
687 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
688 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
689 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
691 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
692 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
693 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
694 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
696 // Within vertical boundary
697 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
698 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
699 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
700 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
702 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
703 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
704 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
705 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
711 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
718 // When text is selected, show popup above top handle (and text), or below bottom handle.
720 // topHandle: referring to the top most point of the handle or the top line of selection.
721 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
723 topHandle = mSelectionHandleOneActualPosition;
724 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
728 topHandle = mSelectionHandleTwoActualPosition;
729 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
731 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
732 position = Vector3(topHandle.x, topHandle.y, 0.0f);
737 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
739 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
740 Vector3 alternativePosition;
741 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
742 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
744 return alternativePosition;
747 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
749 // When no text is selected, show PopUp at position of cursor
752 std::size_t cursorPosition = GetCurrentCursorPosition();
753 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
754 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
755 position.y -= rowSize.height;
760 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
762 std::size_t cursorPosition = GetCurrentCursorPosition();
763 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
765 if ( mTextInputHandles.GetGrabHandle() )
767 // If grab handle enabled then position pop-up below the grab handle.
768 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
772 alternativePosition.y += mPopUpPanel.GetSize().y;
775 return alternativePosition;
779 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
781 return Vector3::ZERO;
784 void Decorator::ShowPopUp()
787 Vector3 alternativePosition;
790 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
792 if( mHighlightMeshActor ) // Text Selection mode
794 position = PositionOfPopUpRelativeToSelectionHandles();
796 else // Not in Text Selection mode so position relative to cursor.
798 position = PositionOfPopUpRelativeToCursor();
801 // reposition popup above the desired cursor position.
802 mPopUpPanel.Show( mPopUpTarget, true );
803 mPopUpPanel.Self().SetPosition( position );
804 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
806 SetUpPopUpPositionNotifications();
807 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
810 void Decorator::ShowPopUp( Actor target )
812 mPopUpTarget = target;
813 ShowPopupCutCopyPaste();
816 void Decorator::ShowPopupCutCopyPaste()
818 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
819 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
820 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
822 Clipboard clipboard = Clipboard::Get();
823 bool hasClipboardGotContent = clipboard.NumberOfItems();
825 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
829 void Decorator::HidePopUp( bool animate, bool signalFinished )
833 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
835 mPopUpPanel.AddButton(name, caption, icon, finalOption);
838 void Decorator::ClearPopup()
843 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
845 Vector3 position, alternativePosition;
847 if( mHighlightMeshActor ) // Text Selection mode
849 alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
851 else // Not in Text Selection mode
853 alternativePosition = AlternatePopUpPositionRelativeToCursor();
854 // if can't be positioned above, then position below row.
856 // reposition popup above the desired cursor position.
857 mPopUpPanel.Self().SetPosition( alternativePosition );
860 void Decorator::SetUpPopUpPositionNotifications( )
862 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
864 // Exceeding vertical boundary
865 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
866 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
867 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
868 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
871 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
873 mPopUpButtonPressedSignal.Emit( button );
877 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
879 return mPopUpButtonPressedSignal;
882 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
884 return mCursorRePositionedSignal;
888 * Decoration Positioning during Scrolling
890 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
892 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
894 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
895 Size cursorSize( CURSOR_THICKNESS, 0.f );
897 // Updates the cursor and grab handle position and visibility.
898 if( mTextInputHandles.GetGrabHandle() || mCursor )
901 size_t cursorTextPosition = GetCurrentCursorPosition();
902 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
904 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
906 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
907 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
909 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
911 if( mTextInputHandles.GetGrabHandle() )
913 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
914 PositionGrabHandle( cursorTextPosition );
919 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
920 DrawCursor( cursorTextPosition );
921 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
925 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
926 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
928 // Updates the selection handles and highlighted text position and visibility.
929 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
931 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
932 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
934 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
935 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
937 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
938 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
940 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
941 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
943 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
944 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
946 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
947 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
949 if( mHighlightMeshActor )
951 mHighlightMeshActor.SetVisible( true );
952 ShowUpdatedHighlight();
957 void Decorator::StartScrollTimer()
961 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
962 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
965 if( !mScrollTimer.IsRunning() )
967 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
968 mScrollTimer.Start();
972 void Decorator::StopScrollTimer()
976 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
979 mScrollTimer.Reset();
983 bool Decorator::OnScrollTimerTick()
985 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
987 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
989 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
990 if ( mGrabHandlePosition != newGrabHandlePosition )
992 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
993 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
994 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
995 scrollPosition += scrollDelta;
996 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
998 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1002 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1003 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1005 if ( selectionHandleOne && selectionHandleTwo )
1007 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1009 // todo duplicated code should be a function
1011 if ( mSelectionHandleOnePosition != newHandleOnePosition )
1013 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1015 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1017 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1018 scrollPosition += scrollDelta;
1019 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1021 mSelectionHandleOnePosition = newHandleOnePosition;
1022 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1026 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1027 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1030 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1032 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1034 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1036 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1038 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1039 scrollPosition += scrollDelta;
1040 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1042 mSelectionHandleTwoPosition = newHandleTwoPosition;
1043 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1048 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1049 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1059 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1061 MarkupProcessor::StyledTextArray currentSelectedText;
1063 if ( mHighlightMeshActor ) // Text Selected
1065 MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1066 MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1068 for(; it != end; ++it)
1070 MarkupProcessor::StyledText& styledText( *it );
1071 currentSelectedText.push_back( styledText );
1074 return currentSelectedText;
1079 } // namespace Toolkit