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.SetOrientation( 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" );
585 void Decorator::RemoveHighlight()
589 void Decorator::HighlightVisibility( bool visiblility )
594 * Callbacks connected to be Property notifications for Boundary checking.
596 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
597 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
599 mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
602 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
604 mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
607 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
609 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
612 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
614 mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
617 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
619 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
620 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
621 selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
622 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
625 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
627 DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
628 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
629 selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
630 selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
633 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
635 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
636 selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
637 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
640 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
642 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
643 selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
644 selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
647 void Decorator::SetUpHandlePropertyNotifications()
649 /* Property notifications for handles exceeding the boundary and returning back within boundary */
651 Vector3 handlesize = GetSelectionHandleSize();
653 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
654 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
656 // Exceeding horizontal boundary
657 PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
658 leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
660 PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
661 rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
663 // Within horizontal boundary
664 PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
665 leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
667 PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
668 rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
670 // Exceeding vertical boundary
671 PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
672 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
673 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
674 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
676 PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
677 OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
678 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
679 verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
681 // Within vertical boundary
682 PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
683 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
684 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
685 verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
687 PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
688 InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
689 mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
690 verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
696 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
703 // When text is selected, show popup above top handle (and text), or below bottom handle.
705 // topHandle: referring to the top most point of the handle or the top line of selection.
706 if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
708 topHandle = mSelectionHandleOneActualPosition;
709 rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
713 topHandle = mSelectionHandleTwoActualPosition;
714 rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
716 topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
717 position = Vector3(topHandle.x, topHandle.y, 0.0f);
722 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
724 // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
725 Vector3 alternativePosition;
726 alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
727 alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
729 return alternativePosition;
732 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
734 // When no text is selected, show PopUp at position of cursor
737 std::size_t cursorPosition = GetCurrentCursorPosition();
738 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
739 const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
740 position.y -= rowSize.height;
745 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
747 std::size_t cursorPosition = GetCurrentCursorPosition();
748 Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
750 if ( mTextInputHandles.GetGrabHandle() )
752 // If grab handle enabled then position pop-up below the grab handle.
753 alternativePosition.y += mTextInputHandles.GetGrabHandle().GetCurrentSize().height + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
757 alternativePosition.y += mPopUpPanel.GetSize().y;
760 return alternativePosition;
764 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
766 return Vector3::ZERO;
769 void Decorator::ShowPopUp()
772 Vector3 alternativePosition;
775 DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
778 position = PositionOfPopUpRelativeToCursor();
781 // reposition popup above the desired cursor position.
782 mPopUpPanel.Show( mPopUpTarget, true );
783 mPopUpPanel.Self().SetPosition( position );
784 mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
786 SetUpPopUpPositionNotifications();
787 mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
790 void Decorator::ShowPopUp( Actor target )
792 mPopUpTarget = target;
793 ShowPopupCutCopyPaste();
796 void Decorator::ShowPopupCutCopyPaste()
798 bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
799 bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
800 bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) &&false;
802 Clipboard clipboard = Clipboard::Get();
803 bool hasClipboardGotContent = clipboard.NumberOfItems();
805 mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
809 void Decorator::HidePopUp( bool animate, bool signalFinished )
813 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
815 mPopUpPanel.AddButton(name, caption, icon, finalOption);
818 void Decorator::ClearPopup()
823 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
825 Vector3 position, alternativePosition;
828 alternativePosition = AlternatePopUpPositionRelativeToCursor();
829 // if can't be positioned above, then position below row.
831 // reposition popup above the desired cursor position.
832 mPopUpPanel.Self().SetPosition( alternativePosition );
835 void Decorator::SetUpPopUpPositionNotifications( )
837 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
839 // Exceeding vertical boundary
840 PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
841 OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
842 mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
843 verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
846 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
848 mPopUpButtonPressedSignal.Emit( button );
852 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
854 return mPopUpButtonPressedSignal;
857 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
859 return mCursorRePositionedSignal;
863 * Decoration Positioning during Scrolling
865 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
867 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
869 const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
870 Size cursorSize( CURSOR_THICKNESS, 0.f );
872 // Updates the cursor and grab handle position and visibility.
873 if( mTextInputHandles.GetGrabHandle() || mCursor )
876 size_t cursorTextPosition = GetCurrentCursorPosition();
877 cursorSize.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
879 const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
881 bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
882 bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
884 Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
886 if( mTextInputHandles.GetGrabHandle() )
888 ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
889 PositionGrabHandle( cursorTextPosition );
894 mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
895 DrawCursor( cursorTextPosition );
896 mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
900 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
901 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
903 // Updates the selection handles and highlighted text position and visibility.
904 if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
906 const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
907 const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
909 Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
910 const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
912 cursorSize = GetCursorSizeAt( mSelectionHandleTwoPosition );
913 const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
915 mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
916 mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
918 selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
919 selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
921 PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
922 PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
927 void Decorator::StartScrollTimer()
931 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
932 mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
935 if( !mScrollTimer.IsRunning() )
937 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
938 mScrollTimer.Start();
942 void Decorator::StopScrollTimer()
946 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
949 mScrollTimer.Reset();
953 bool Decorator::OnScrollTimerTick()
955 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
957 if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
959 std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
960 if ( mGrabHandlePosition != newGrabHandlePosition )
962 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
963 Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
964 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
965 scrollPosition += scrollDelta;
966 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
968 mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
972 Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
973 Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
975 if ( selectionHandleOne && selectionHandleTwo )
977 std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
979 // todo duplicated code should be a function
981 if ( mSelectionHandleOnePosition != newHandleOnePosition )
983 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
985 Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
987 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
988 scrollPosition += scrollDelta;
989 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
991 mSelectionHandleOnePosition = newHandleOnePosition;
992 mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
996 mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
997 mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1000 std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1002 if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1004 const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1006 Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1008 Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1009 scrollPosition += scrollDelta;
1010 mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1012 mSelectionHandleTwoPosition = newHandleTwoPosition;
1013 mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1018 mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1019 mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1029 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1031 MarkupProcessor::StyledTextArray currentSelectedText;
1033 return currentSelectedText;
1038 } // namespace Toolkit