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 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::Property::WorldPositionX, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
674 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
676 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionX, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
677 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
679 // Within horizontal boundary
680 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionX, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
681 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
683 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionX, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
684 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
686 // Exceeding vertical boundary
687 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::Property::WorldPositionY,
688 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
689 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
690 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
692 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionY,
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::Property::WorldPositionY,
699 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
700 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
701 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
703 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::Property::WorldPositionY,
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::Property::WorldPositionY,
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