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 mGrabHandleVisibility( false ),
97 mGrabHandleEnabled( true )
101 Decorator::~Decorator()
108 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
110 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
111 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
113 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
114 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
116 const Vector4 boundary( originX,
118 originX + boundingRectangle.width,
119 originY + boundingRectangle.height );
121 mBoundingRectangleWorldCoordinates = boundary;
124 Vector4 Decorator::GetBoundingBox() const
126 return mBoundingRectangleWorldCoordinates;
132 void Decorator::OnHandlePan(Actor actor, const PanGesture& gesture)
134 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
135 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
137 switch (gesture.state)
139 case Gesture::Started:
140 // fall through so code not duplicated
141 case Gesture::Continuing:
143 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
145 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
148 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
150 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
153 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
155 SetCursorVisibility( true );
156 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
157 MoveGrabHandle( gesture.displacement );
158 HidePopUp(); // Do not show popup while handle is moving
163 case Gesture::Finished:
165 // Revert back to non-pressed selection handle images
166 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
168 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
169 ShowPopupCutCopyPaste();
171 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
173 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
174 ShowPopupCutCopyPaste();
176 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
178 MoveGrabHandle( gesture.displacement );
179 SetCursorVisibility( true );
180 ShowPopupCutCopyPaste();
189 void Decorator::CreateSelectionHandles( Actor targetParent )
191 if ( !mPanGestureDetector )
193 mPanGestureDetector = PanGestureDetector::New();
194 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
197 if ( !mTextInputHandles.GetSelectionHandleOne() )
199 mTextInputHandles.CreateSelectionHandles();
201 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
203 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
204 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
206 SetUpHandlePropertyNotifications();
210 void Decorator::RemoveSelectionHandles()
212 mTextInputHandles.DestorySelectionHandles();
215 Vector3 Decorator::GetSelectionHandleSize()
217 return DEFAULT_SELECTION_HANDLE_SIZE;
220 std::size_t Decorator::GetHandleOnePosition() const
222 return mSelectionHandleOnePosition;
225 std::size_t Decorator::GetHandleTwoPosition() const
227 return mSelectionHandleTwoPosition;
230 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
232 bool direction(false);
233 Vector3 alternatePosition;
234 bool alternatePositionValid(false);
236 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
238 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
242 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
244 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
246 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
248 return actualPosition;
251 void Decorator::SetSelectionHandlesVisibility(bool visible )
253 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
254 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
257 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
259 mSelectionHandleOnePosition = start;
260 mSelectionHandleTwoPosition = end;
262 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
264 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
265 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
268 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
269 Vector3& actualSelectionHandlePosition,
270 std::size_t& currentSelectionHandlePosition,
271 const Vector2& displacement )
273 Vector3 actualHandlePosition;
274 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
275 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;
277 // Selection handles should jump to the nearest character
278 std::size_t newHandlePosition = 0;
279 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
281 bool direction(false);
282 Vector3 alternatePosition;
283 bool alternatePositionValid(false);
284 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
286 bool handleVisible = true;
288 if ( handleVisible && // Ensure the handle is visible.
289 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
290 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
291 ( newHandlePosition != mSelectionHandleOnePosition ) )
293 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
295 currentSelectionHandlePosition = newHandlePosition;
297 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
299 ShowUpdatedHighlight();
301 // Set Active Style to that of first character in selection
302 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
304 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
305 mTextStyle.SetInputStyle( inputStyle );
307 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
313 void Decorator::PositionGrabHandle( std::size_t positionInText )
315 bool direction(false);
316 Vector3 alternatePosition;
317 bool alternatePositionValid(false);
319 mGrabHandlePosition = positionInText;
321 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
322 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
324 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
327 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
329 mActualGrabHandlePosition.x += displacement.x;
330 mActualGrabHandlePosition.y += displacement.y;
332 // Grab handle should jump to the nearest character and take cursor with it
333 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
335 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
337 bool handleVisible = true;
339 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
340 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
342 mActualGrabHandlePosition = actualHandlePosition;
343 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
345 //PositionGrabHandle( newHandlePosition );
346 mGrabHandlePosition = newHandlePosition;
347 SetCurrentCursorPosition( mGrabHandlePosition );
348 DrawCursor( mGrabHandlePosition );
350 const std::size_t cursorPosition = GetCurrentCursorPosition();
352 // Let keyboard know the new cursor position so can 're-capture' for prediction.
353 mCursorRePositionedSignal.Emit();
355 // Set Input Style to that of cursor position
356 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
358 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
363 void Decorator::ShowGrabHandle( bool visible )
365 mGrabHandleVisibility = visible;
366 mTextInputHandles.SetGrabHandleVisibility( visible );
369 void Decorator::CreateGrabHandle( Actor targetParent )
371 if ( !mPanGestureDetector )
373 mPanGestureDetector = PanGestureDetector::New();
374 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
377 if ( !mTextInputHandles.GetGrabHandle() )
379 mTextInputHandles.CreateGrabHandle();
380 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
381 targetParent.Add( mTextInputHandles.GetGrabHandle() );
385 void Decorator::SetGrabHandleImage( Image image )
387 mTextInputHandles.SetGrabHandleImage( image );
390 void Decorator::EnableGrabHandle( bool toggle)
392 // enables grab handle with will in turn de-activate magnifier
393 mGrabHandleEnabled = toggle;
396 bool Decorator::IsGrabHandleEnabled()
398 // if false then magnifier will be shown instead.
399 return mGrabHandleEnabled;
405 std::size_t Decorator::GetCurrentCursorPosition() const
407 return mCursorPosition;
410 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
412 mCursorPosition = newCursorPosition;
415 void Decorator::SetCursorVisibility( bool visible )
417 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
419 mCursorVisibility = visible;
420 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
421 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
424 void Decorator::DrawCursor(const std::size_t nthChar)
426 std::size_t cursorPosition = GetCurrentCursorPosition();
428 // Get height of cursor and set its size
429 Size size( CURSOR_THICKNESS, 0.0f );
431 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
432 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
434 mCursor.SetSize(size);
436 // If the character is italic then the cursor also tilts.
437 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
439 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
440 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
441 mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
444 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
445 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
447 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
448 bool altPositionValid( false ); // Alternate cursor validity flag.
449 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
450 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
452 SetAltCursorEnabled( altPositionValid );
454 mCursor.SetPosition( position + UI_OFFSET );
458 void Decorator::SetAltCursorEnabled( bool enabled )
460 mCursorRTLEnabled = enabled;
461 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
464 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
466 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
470 mCursor.SetImage( image );
471 mCursor.SetNinePatchBorder( border );
475 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
477 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
481 mCursorRTL.SetImage( image );
482 mCursorRTL.SetNinePatchBorder( border );
486 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
492 cursor = ImageActor::New( cursorImage );
496 cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
499 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
500 cursor.SetNinePatchBorder( border );
501 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
502 cursor.SetVisible(false);
503 cursor.SetName( cursorName );
507 void Decorator::CreateCursors( Actor targetParent )
509 Image mCursorImage = Image::New( DEFAULT_CURSOR );
510 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
511 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
512 targetParent.Add( mCursor );
513 targetParent.Add( mCursorRTL );
516 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
518 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
522 const Size cursorSize( CURSOR_THICKNESS,
523 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
528 void Decorator::StartCursorBlinkTimer()
530 if ( !mCursorBlinkTimer )
532 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
533 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
536 if ( !mCursorBlinkTimer.IsRunning() )
538 mCursorBlinkTimer.Start();
542 void Decorator::StopCursorBlinkTimer()
544 if ( mCursorBlinkTimer )
546 mCursorBlinkTimer.Stop();
550 bool Decorator::OnCursorBlinkTimerTick()
553 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
554 if ( mCursorRTLEnabled )
556 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
558 mCursorBlinkStatus = !mCursorBlinkStatus;
566 void Decorator::ShowUpdatedHighlight()
568 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
569 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
571 // Clamp highlightInfo so they don't exceed the boundary of the control.
572 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
573 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
575 mTextHighlight.UpdateHighlight( highlightInfo );
578 void Decorator::CreateHighlight( Actor parent )
580 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
582 if ( !mHighlightMeshActor )
584 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
585 mHighlightMeshActor.SetName( "HighlightMeshActor" );
586 mHighlightMeshActor.SetAffectedByLighting(false);
587 parent.Add( mHighlightMeshActor );
591 void Decorator::RemoveHighlight()
593 if ( mHighlightMeshActor )
595 mHighlightMeshActor.Unparent();
596 mHighlightMeshActor.Reset();
597 // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
601 void Decorator::HighlightVisibility( bool visiblility )
603 if ( mHighlightMeshActor )
605 mHighlightMeshActor.SetVisible( visiblility );
610 * Callbacks connected to be Property notifications for Boundary checking.
612 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
613 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
615 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
618 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
620 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
623 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
625 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
628 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
630 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
633 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
635 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
636 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
637 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
638 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
641 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
643 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
644 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
645 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
646 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
649 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
651 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
652 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
653 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
656 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
658 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
659 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
660 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
663 void Decorator::SetUpHandlePropertyNotifications()
665 /* Property notifications for handles exceeding the boundary and returning back within boundary */
667 Vector3 handlesize = GetSelectionHandleSize();
669 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
670 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
672 // Exceeding horizontal boundary
673 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
674 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
676 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
677 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
679 // Within horizontal boundary
680 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
681 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
683 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
684 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
686 // Exceeding vertical boundary
687 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
688 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
689 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
690 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
692 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
693 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
694 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
695 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
697 // Within vertical boundary
698 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
699 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
700 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
701 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
703 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
704 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
705 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
706 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
712 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
719 // When text is selected, show popup above top handle (and text), or below bottom handle.
721 // topHandle: referring to the top most point of the handle or the top line of selection.
722 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
724 topHandle = mSelectionHandleOneActualPosition;
725 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
729 topHandle = mSelectionHandleTwoActualPosition;
730 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
732 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
733 position = Vector3(topHandle.x, topHandle.y, 0.0f);
738 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
740 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
741 Vector3 alternativePosition;
742 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
743 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
745 return alternativePosition;
748 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
750 // When no text is selected, show PopUp at position of cursor
753 std::size_t cursorPosition = GetCurrentCursorPosition();
754 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
755 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
756 position.y -= rowSize.height;
761 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
763 std::size_t cursorPosition = GetCurrentCursorPosition();
764 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
766 if ( mTextInputHandles.GetGrabHandle() )
768 // If grab handle enabled then position pop-up below the grab handle.
769 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
773 alternativePosition.y += mPopUpPanel.GetSize().y;
776 return alternativePosition;
780 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
782 return Vector3::ZERO;
785 void Decorator::ShowPopUp()
788 Vector3 alternativePosition;
791 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
793 if( mHighlightMeshActor ) // Text Selection mode
795 position = PositionOfPopUpRelativeToSelectionHandles();
797 else // Not in Text Selection mode so position relative to cursor.
799 position = PositionOfPopUpRelativeToCursor();
802 // reposition popup above the desired cursor position.
803 mPopUpPanel.Show( mPopUpTarget, true );
804 mPopUpPanel.Self().SetPosition( position );
805 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
807 SetUpPopUpPositionNotifications();
808 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
811 void Decorator::ShowPopUp( Actor target )
813 mPopUpTarget = target;
814 ShowPopupCutCopyPaste();
817 void Decorator::ShowPopupCutCopyPaste()
819 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
820 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
821 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
823 Clipboard clipboard = Clipboard::Get();
824 bool hasClipboardGotContent = clipboard.NumberOfItems();
826 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
830 void Decorator::HidePopUp( bool animate, bool signalFinished )
834 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
836 mPopUpPanel.AddButton(name, caption, icon, finalOption);
839 void Decorator::ClearPopup()
844 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
846 Vector3 position, alternativePosition;
848 if( mHighlightMeshActor ) // Text Selection mode
850 alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
852 else // Not in Text Selection mode
854 alternativePosition = AlternatePopUpPositionRelativeToCursor();
855 // if can't be positioned above, then position below row.
857 // reposition popup above the desired cursor position.
858 mPopUpPanel.Self().SetPosition( alternativePosition );
861 void Decorator::SetUpPopUpPositionNotifications( )
863 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
865 // Exceeding vertical boundary
866 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
867 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
868 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
869 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
872 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
874 mPopUpButtonPressedSignal.Emit( button );
878 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
880 return mPopUpButtonPressedSignal;
883 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
885 return mCursorRePositionedSignal;
889 * Decoration Positioning during Scrolling
891 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
893 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
895 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
896 Size cursorSize( CURSOR_THICKNESS, 0.f );
898 // Updates the cursor and grab handle position and visibility.
899 if( mTextInputHandles.GetGrabHandle() || mCursor )
902 size_t cursorTextPosition = GetCurrentCursorPosition();
903 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
905 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
907 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
908 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
910 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
912 if( mTextInputHandles.GetGrabHandle() )
914 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
915 PositionGrabHandle( cursorTextPosition );
920 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
921 DrawCursor( cursorTextPosition );
922 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
926 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
927 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
929 // Updates the selection handles and highlighted text position and visibility.
930 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
932 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
933 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
935 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
936 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
938 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
939 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
941 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
942 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
944 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
945 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
947 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
948 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
950 if( mHighlightMeshActor )
952 mHighlightMeshActor.SetVisible( true );
953 ShowUpdatedHighlight();
958 void Decorator::StartScrollTimer()
962 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
963 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
966 if( !mScrollTimer.IsRunning() )
968 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
969 mScrollTimer.Start();
973 void Decorator::StopScrollTimer()
977 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
980 mScrollTimer.Reset();
984 bool Decorator::OnScrollTimerTick()
986 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
988 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
990 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
991 if ( mGrabHandlePosition != newGrabHandlePosition )
993 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
994 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
995 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
996 scrollPosition += scrollDelta;
997 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
999 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1003 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1004 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1006 if ( selectionHandleOne && selectionHandleTwo )
1008 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1010 // todo duplicated code should be a function
1012 if ( mSelectionHandleOnePosition != newHandleOnePosition )
1014 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1016 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1018 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1019 scrollPosition += scrollDelta;
1020 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1022 mSelectionHandleOnePosition = newHandleOnePosition;
1023 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1027 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1028 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1031 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1033 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1035 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1037 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1039 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1040 scrollPosition += scrollDelta;
1041 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1043 mSelectionHandleTwoPosition = newHandleTwoPosition;
1044 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1049 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1050 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1060 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1062 MarkupProcessor::StyledTextArray currentSelectedText;
1064 if ( mHighlightMeshActor ) // Text Selected
1066 MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1067 MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1069 for(; it != end; ++it)
1071 MarkupProcessor::StyledText& styledText( *it );
1072 currentSelectedText.push_back( styledText );
1075 return currentSelectedText;
1080 } // namespace Toolkit