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>
27 #include <dali/public-api/images/resource-image.h>
30 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
40 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
41 const float TOP_HANDLE_TOP_OFFSET(-1.5f); // Offset between top handle and cutCopyPaste pop-up
42 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); // Offset between bottom handle and cutCopyPaste pop-up
43 const float UI_Z_OFFSET( 0.2f ); // Text Selection Handles/Cursor z-offset.
44 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); // Text Selection Handles/Cursor offset.
45 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
46 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
47 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
48 const float CURSOR_THICKNESS(6.0f);
49 const Degree CURSOR_ANGLE_OFFSET(2.0f); // Offset from the angle
51 const unsigned int SCROLL_TICK_INTERVAL = 50u;
52 const float SCROLL_THRESHOLD = 10.f;
53 const float SCROLL_SPEED = 15.f;
56 * Whether the given position plus the cursor size offset is inside the given boundary.
58 * @param[in] position The given position.
59 * @param[in] cursorSize The cursor size.
60 * @param[in] controlSize The given boundary.
61 * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
63 * @return whether the given position is inside the given boundary.
65 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
67 return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
68 ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
69 ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
70 ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
84 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
85 mTextViewCharacterPositioning( textViewManager ),
86 mTextStyle( textStyle ),
87 mSelectionHandleOnePosition(0),
88 mSelectionHandleTwoPosition(0),
89 mGrabHandlePosition(0),
91 mTextHighlight( textViewManager ),
92 mCursorBlinkStatus( true ),
93 mCursorVisibility( true ),
94 mCursorRTLEnabled( false ),
95 mIsGrabHandleInScrollArea( false ),
96 mIsCursorInScrollArea( false ),
97 mGrabHandleVisibility( false ),
98 mGrabHandleEnabled( true )
102 Decorator::~Decorator()
109 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
111 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
112 Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
114 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
115 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
117 const Vector4 boundary( originX,
119 originX + boundingRectangle.width,
120 originY + boundingRectangle.height );
122 mBoundingRectangleWorldCoordinates = boundary;
125 Vector4 Decorator::GetBoundingBox() const
127 return mBoundingRectangleWorldCoordinates;
133 void Decorator::OnHandlePan(Actor actor, const PanGesture& gesture)
135 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
136 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
138 switch (gesture.state)
140 case Gesture::Started:
141 // fall through so code not duplicated
142 case Gesture::Continuing:
144 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
146 MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
149 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
151 MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
154 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
156 SetCursorVisibility( true );
157 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
158 MoveGrabHandle( gesture.displacement );
159 HidePopUp(); // Do not show popup while handle is moving
164 case Gesture::Finished:
166 // Revert back to non-pressed selection handle images
167 if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
169 mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
170 ShowPopupCutCopyPaste();
172 else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
174 mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
175 ShowPopupCutCopyPaste();
177 else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
179 MoveGrabHandle( gesture.displacement );
180 SetCursorVisibility( true );
181 ShowPopupCutCopyPaste();
190 void Decorator::CreateSelectionHandles( Actor targetParent )
192 if ( !mPanGestureDetector )
194 mPanGestureDetector = PanGestureDetector::New();
195 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
198 if ( !mTextInputHandles.GetSelectionHandleOne() )
200 mTextInputHandles.CreateSelectionHandles();
202 mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
204 targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
205 targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
207 SetUpHandlePropertyNotifications();
211 void Decorator::RemoveSelectionHandles()
213 mTextInputHandles.DestorySelectionHandles();
216 Vector3 Decorator::GetSelectionHandleSize()
218 return DEFAULT_SELECTION_HANDLE_SIZE;
221 std::size_t Decorator::GetHandleOnePosition() const
223 return mSelectionHandleOnePosition;
226 std::size_t Decorator::GetHandleTwoPosition() const
228 return mSelectionHandleTwoPosition;
231 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
233 bool direction(false);
234 Vector3 alternatePosition;
235 bool alternatePositionValid(false);
237 Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
239 return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
243 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
245 const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
247 selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
249 return actualPosition;
252 void Decorator::SetSelectionHandlesVisibility(bool visible )
254 mTextInputHandles.SetSelectionHandleOneVisibility( visible );
255 mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
258 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
260 mSelectionHandleOnePosition = start;
261 mSelectionHandleTwoPosition = end;
263 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
265 mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
266 mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
269 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
270 Vector3& actualSelectionHandlePosition,
271 std::size_t& currentSelectionHandlePosition,
272 const Vector2& displacement )
274 Vector3 actualHandlePosition;
275 actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
276 actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;
278 // Selection handles should jump to the nearest character
279 std::size_t newHandlePosition = 0;
280 newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
282 bool direction(false);
283 Vector3 alternatePosition;
284 bool alternatePositionValid(false);
285 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid );
287 bool handleVisible = true;
289 if ( handleVisible && // Ensure the handle is visible.
290 ( newHandlePosition != currentSelectionHandlePosition ) && // Ensure the handle has moved.
291 ( newHandlePosition != mSelectionHandleTwoPosition ) && // Ensure new handle position not the same position as an existing handle.
292 ( newHandlePosition != mSelectionHandleOnePosition ) )
294 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
296 currentSelectionHandlePosition = newHandlePosition;
298 PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
300 ShowUpdatedHighlight();
302 // Set Active Style to that of first character in selection
303 std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
305 const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
306 mTextStyle.SetInputStyle( inputStyle );
308 return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
314 void Decorator::PositionGrabHandle( std::size_t positionInText )
316 bool direction(false);
317 Vector3 alternatePosition;
318 bool alternatePositionValid(false);
320 mGrabHandlePosition = positionInText;
322 mTextViewCharacterPositioning.UpdateTextLayoutInfo();
323 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
325 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
328 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
330 mActualGrabHandlePosition.x += displacement.x;
331 mActualGrabHandlePosition.y += displacement.y;
333 // Grab handle should jump to the nearest character and take cursor with it
334 std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
336 Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
338 bool handleVisible = true;
340 if( ( newHandlePosition != mGrabHandlePosition ) && // Only redraw cursor and do updates if position changed
341 ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
343 mActualGrabHandlePosition = actualHandlePosition;
344 mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
346 //PositionGrabHandle( newHandlePosition );
347 mGrabHandlePosition = newHandlePosition;
348 SetCurrentCursorPosition( mGrabHandlePosition );
349 DrawCursor( mGrabHandlePosition );
351 const std::size_t cursorPosition = GetCurrentCursorPosition();
353 // Let keyboard know the new cursor position so can 're-capture' for prediction.
354 mCursorRePositionedSignal.Emit();
356 // Set Input Style to that of cursor position
357 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
359 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
364 void Decorator::ShowGrabHandle( bool visible )
366 mGrabHandleVisibility = visible;
367 mTextInputHandles.SetGrabHandleVisibility( visible );
370 void Decorator::CreateGrabHandle( Actor targetParent )
372 if ( !mPanGestureDetector )
374 mPanGestureDetector = PanGestureDetector::New();
375 mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
378 if ( !mTextInputHandles.GetGrabHandle() )
380 mTextInputHandles.CreateGrabHandle();
381 mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
382 targetParent.Add( mTextInputHandles.GetGrabHandle() );
386 void Decorator::SetGrabHandleImage( Image image )
388 mTextInputHandles.SetGrabHandleImage( image );
391 void Decorator::EnableGrabHandle( bool toggle)
393 // enables grab handle with will in turn de-activate magnifier
394 mGrabHandleEnabled = toggle;
397 bool Decorator::IsGrabHandleEnabled()
399 // if false then magnifier will be shown instead.
400 return mGrabHandleEnabled;
406 std::size_t Decorator::GetCurrentCursorPosition() const
408 return mCursorPosition;
411 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
413 mCursorPosition = newCursorPosition;
416 void Decorator::SetCursorVisibility( bool visible )
418 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
420 mCursorVisibility = visible;
421 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
422 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
425 void Decorator::DrawCursor(const std::size_t nthChar)
427 std::size_t cursorPosition = GetCurrentCursorPosition();
429 // Get height of cursor and set its size
430 Size size( CURSOR_THICKNESS, 0.0f );
432 Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
433 size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max ).height;
435 mCursor.SetSize(size);
437 // If the character is italic then the cursor also tilts.
438 if ( !mTextViewCharacterPositioning.IsStyledTextEmpty() && ( cursorPosition > 0 ) )
440 DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
441 const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
442 mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
445 DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
446 if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
448 Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position.
449 bool altPositionValid( false ); // Alternate cursor validity flag.
450 bool directionRTL( false ); // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
451 Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
453 SetAltCursorEnabled( altPositionValid );
455 mCursor.SetPosition( position + UI_OFFSET );
459 void Decorator::SetAltCursorEnabled( bool enabled )
461 mCursorRTLEnabled = enabled;
462 mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
465 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
467 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
471 mCursor.SetImage( image );
472 mCursor.SetNinePatchBorder( border );
476 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
478 DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
482 mCursorRTL.SetImage( image );
483 mCursorRTL.SetNinePatchBorder( border );
487 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
493 cursor = ImageActor::New( cursorImage );
497 cursor = ImageActor::New( ResourceImage::New( DEFAULT_CURSOR ) );
500 cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
501 cursor.SetNinePatchBorder( border );
502 cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
503 cursor.SetVisible(false);
504 cursor.SetName( cursorName );
508 void Decorator::CreateCursors( Actor targetParent )
510 Image mCursorImage = ResourceImage::New( DEFAULT_CURSOR );
511 mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
512 mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
513 targetParent.Add( mCursor );
514 targetParent.Add( mCursorRTL );
517 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
519 std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
523 const Size cursorSize( CURSOR_THICKNESS,
524 mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
529 void Decorator::StartCursorBlinkTimer()
531 if ( !mCursorBlinkTimer )
533 mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
534 mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
537 if ( !mCursorBlinkTimer.IsRunning() )
539 mCursorBlinkTimer.Start();
543 void Decorator::StopCursorBlinkTimer()
545 if ( mCursorBlinkTimer )
547 mCursorBlinkTimer.Stop();
551 bool Decorator::OnCursorBlinkTimerTick()
554 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
555 if ( mCursorRTLEnabled )
557 mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
559 mCursorBlinkStatus = !mCursorBlinkStatus;
567 void Decorator::ShowUpdatedHighlight()
569 Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
570 TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
572 // Clamp highlightInfo so they don't exceed the boundary of the control.
573 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
574 highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
576 mTextHighlight.UpdateHighlight( highlightInfo );
579 void Decorator::CreateHighlight( Actor parent )
581 DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
583 if ( !mHighlightMeshActor )
585 mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
586 mHighlightMeshActor.SetName( "HighlightMeshActor" );
587 mHighlightMeshActor.SetAffectedByLighting(false);
588 parent.Add( mHighlightMeshActor );
592 void Decorator::RemoveHighlight()
594 if ( mHighlightMeshActor )
596 mHighlightMeshActor.Unparent();
597 mHighlightMeshActor.Reset();
598 // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
602 void Decorator::HighlightVisibility( bool visiblility )
604 if ( mHighlightMeshActor )
606 mHighlightMeshActor.SetVisible( visiblility );
611 * Callbacks connected to be Property notifications for Boundary checking.
613 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
614 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
616 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
619 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
621 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
624 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
626 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
629 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
631 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
634 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
636 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
637 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
638 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
639 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
642 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
644 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
645 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
646 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
647 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
650 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
652 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
653 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
654 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
657 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
659 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
660 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
661 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
664 void Decorator::SetUpHandlePropertyNotifications()
666 /* Property notifications for handles exceeding the boundary and returning back within boundary */
668 Vector3 handlesize = GetSelectionHandleSize();
670 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
671 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
673 // Exceeding horizontal boundary
674 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
675 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
677 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
678 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
680 // Within horizontal boundary
681 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
682 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
684 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
685 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
687 // Exceeding vertical boundary
688 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
689 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
690 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
691 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
693 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
694 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
695 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
696 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
698 // Within vertical boundary
699 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
700 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
701 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
702 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
704 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
705 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
706 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
707 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
713 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
720 // When text is selected, show popup above top handle (and text), or below bottom handle.
722 // topHandle: referring to the top most point of the handle or the top line of selection.
723 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
725 topHandle = mSelectionHandleOneActualPosition;
726 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
730 topHandle = mSelectionHandleTwoActualPosition;
731 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
733 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
734 position = Vector3(topHandle.x, topHandle.y, 0.0f);
739 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
741 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
742 Vector3 alternativePosition;
743 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
744 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
746 return alternativePosition;
749 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
751 // When no text is selected, show PopUp at position of cursor
754 std::size_t cursorPosition = GetCurrentCursorPosition();
755 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
756 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
757 position.y -= rowSize.height;
762 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
764 std::size_t cursorPosition = GetCurrentCursorPosition();
765 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
767 if ( mTextInputHandles.GetGrabHandle() )
769 // If grab handle enabled then position pop-up below the grab handle.
770 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
774 alternativePosition.y += mPopUpPanel.GetSize().y;
777 return alternativePosition;
781 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
783 return Vector3::ZERO;
786 void Decorator::ShowPopUp()
789 Vector3 alternativePosition;
792 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
794 if( mHighlightMeshActor ) // Text Selection mode
796 position = PositionOfPopUpRelativeToSelectionHandles();
798 else // Not in Text Selection mode so position relative to cursor.
800 position = PositionOfPopUpRelativeToCursor();
803 // reposition popup above the desired cursor position.
804 mPopUpPanel.Show( mPopUpTarget, true );
805 mPopUpPanel.Self().SetPosition( position );
806 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
808 SetUpPopUpPositionNotifications();
809 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
812 void Decorator::ShowPopUp( Actor target )
814 mPopUpTarget = target;
815 ShowPopupCutCopyPaste();
818 void Decorator::ShowPopupCutCopyPaste()
820 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
821 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
822 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
824 Clipboard clipboard = Clipboard::Get();
825 bool hasClipboardGotContent = clipboard.NumberOfItems();
827 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
831 void Decorator::HidePopUp( bool animate, bool signalFinished )
835 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
837 mPopUpPanel.AddButton(name, caption, icon, finalOption);
840 void Decorator::ClearPopup()
845 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
847 Vector3 position, alternativePosition;
849 if( mHighlightMeshActor ) // Text Selection mode
851 alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
853 else // Not in Text Selection mode
855 alternativePosition = AlternatePopUpPositionRelativeToCursor();
856 // if can't be positioned above, then position below row.
858 // reposition popup above the desired cursor position.
859 mPopUpPanel.Self().SetPosition( alternativePosition );
862 void Decorator::SetUpPopUpPositionNotifications( )
864 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
866 // Exceeding vertical boundary
867 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
868 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
869 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
870 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
873 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
875 mPopUpButtonPressedSignal.Emit( button );
879 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
881 return mPopUpButtonPressedSignal;
884 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
886 return mCursorRePositionedSignal;
890 * Decoration Positioning during Scrolling
892 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
894 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
896 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
897 Size cursorSize( CURSOR_THICKNESS, 0.f );
899 // Updates the cursor and grab handle position and visibility.
900 if( mTextInputHandles.GetGrabHandle() || mCursor )
903 size_t cursorTextPosition = GetCurrentCursorPosition();
904 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
906 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
908 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
909 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
911 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
913 if( mTextInputHandles.GetGrabHandle() )
915 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
916 PositionGrabHandle( cursorTextPosition );
921 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
922 DrawCursor( cursorTextPosition );
923 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
927 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
928 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
930 // Updates the selection handles and highlighted text position and visibility.
931 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
933 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
934 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
936 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
937 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
939 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
940 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
942 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
943 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
945 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
946 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
948 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
949 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
951 if( mHighlightMeshActor )
953 mHighlightMeshActor.SetVisible( true );
954 ShowUpdatedHighlight();
959 void Decorator::StartScrollTimer()
963 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
964 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
967 if( !mScrollTimer.IsRunning() )
969 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
970 mScrollTimer.Start();
974 void Decorator::StopScrollTimer()
978 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
981 mScrollTimer.Reset();
985 bool Decorator::OnScrollTimerTick()
987 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
989 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
991 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
992 if ( mGrabHandlePosition != newGrabHandlePosition )
994 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
995 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
996 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
997 scrollPosition += scrollDelta;
998 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1000 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1004 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1005 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1007 if ( selectionHandleOne && selectionHandleTwo )
1009 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1011 // todo duplicated code should be a function
1013 if ( mSelectionHandleOnePosition != newHandleOnePosition )
1015 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1017 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1019 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1020 scrollPosition += scrollDelta;
1021 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1023 mSelectionHandleOnePosition = newHandleOnePosition;
1024 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1028 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1029 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1032 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1034 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1036 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1038 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1040 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1041 scrollPosition += scrollDelta;
1042 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1044 mSelectionHandleTwoPosition = newHandleTwoPosition;
1045 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1050 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1051 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1061 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1063 MarkupProcessor::StyledTextArray currentSelectedText;
1065 if ( mHighlightMeshActor ) // Text Selected
1067 MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1068 MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1070 for(; it != end; ++it)
1072 MarkupProcessor::StyledText& styledText( *it );
1073 currentSelectedText.push_back( styledText );
1076 return currentSelectedText;
1081 } // namespace Toolkit