2 * Copyright (c) 2015 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/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-event.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
36 #include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
37 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
40 #define DECORATOR_DEBUG
44 #define MAKE_SHADER(A)#A
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2 aPosition;
50 uniform mediump mat4 uMvpMatrix;
51 uniform mediump vec3 uSize;
55 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
56 position.xyz *= uSize;
57 gl_Position = uMvpMatrix * position;
61 const char* FRAGMENT_SHADER = MAKE_SHADER(
62 uniform lowp vec4 uColor;
66 gl_FragColor = uColor;
77 #ifdef DECORATOR_DEBUG
78 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
89 const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "cursor_handler_center.png" );
91 const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position.
93 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
94 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
96 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
98 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
100 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
101 const float TO_MILLISECONDS = 1000.f;
102 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
104 const unsigned int SCROLL_TICK_INTERVAL = 50u;
106 const float SCROLL_THRESHOLD = 10.f;
107 const float SCROLL_SPEED = 300.f;
108 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
110 const float CURSOR_WIDTH = 1.f;
113 * structure to hold coordinates of each quad, which will make up the mesh.
115 struct QuadCoordinates
118 * Default constructor
126 * @param[in] x1 left co-ordinate
127 * @param[in] y1 top co-ordinate
128 * @param[in] x2 right co-ordinate
129 * @param[in] y2 bottom co-ordinate
131 QuadCoordinates(float x1, float y1, float x2, float y2)
137 Dali::Vector2 min; ///< top-left (minimum) position of quad
138 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
141 typedef std::vector<QuadCoordinates> QuadContainer;
144 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
145 * @param[in] boundingRectangle local bounding
146 * @param[out] Vector4 World coordinate bounding Box.
148 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
150 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
151 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
153 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
154 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
156 boundingBox = Dali::Vector4( originX,
158 originX + boundingRectangle.width,
159 originY + boundingRectangle.height );
162 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
164 // Convert to local coordinates and store as a Dali::Rect.
165 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
167 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
168 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
169 boundingRectangle.width = boundingBox.z - boundingBox.x;
170 boundingRectangle.height = boundingBox.w - boundingBox.y;
173 } // end of namespace
184 struct Decorator::Impl : public ConnectionTracker
198 : color( Dali::Color::BLACK ),
200 cursorHeight( 0.0f ),
217 grabDisplacementX( 0.f ),
218 grabDisplacementY( 0.f ),
222 horizontallyFlipped( false ),
223 verticallyFlipped( false )
229 ImageActor markerActor;
233 float lineHeight; ///< Not the handle height
234 float grabDisplacementX;
235 float grabDisplacementY;
239 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
240 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
247 offset( DEFAULT_POPUP_OFFSET )
251 TextSelectionPopup actor;
256 Impl( ControllerInterface& controller,
257 TextSelectionPopupCallbackInterface& callbackInterface )
258 : mController( controller ),
259 mEnabledPopupButtons( TextSelectionPopup::NONE ),
260 mTextSelectionPopupCallbackInterface( callbackInterface ),
261 mHandleColor( HANDLE_COLOR ),
263 mHighlightColor( LIGHT_BLUE ),
264 mHighlightPosition( Vector2::ZERO ),
265 mActiveCursor( ACTIVE_CURSOR_NONE ),
266 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
267 mCursorBlinkDuration( 0.0f ),
268 mCursorWidth( CURSOR_WIDTH ),
269 mHandleScrolling( HANDLE_TYPE_COUNT ),
270 mScrollDirection( SCROLL_NONE ),
271 mScrollThreshold( SCROLL_THRESHOLD ),
272 mScrollSpeed( SCROLL_SPEED ),
273 mScrollDistance( SCROLL_DISTANCE ),
275 mActiveCopyPastePopup( false ),
276 mPopupSetNewPosition( true ),
277 mCursorBlinkStatus( true ),
278 mDelayCursorBlink( false ),
279 mPrimaryCursorVisible( false ),
280 mSecondaryCursorVisible( false ),
281 mFlipSelectionHandlesOnCross( false ),
282 mFlipLeftSelectionHandleDirection( false ),
283 mFlipRightSelectionHandleDirection( false ),
284 mHandlePanning( false ),
285 mHandleCurrentCrossed( false ),
286 mHandlePreviousCrossed( false ),
287 mNotifyEndOfScroll( false )
289 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
290 mQuadIndexFormat[ "indices" ] = Property::INTEGER;
291 mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
297 * Relayout of the decorations owned by the decorator.
298 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
300 void Relayout( const Vector2& size )
302 // TODO - Remove this if nothing is active
305 // Show or hide the cursors
310 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
311 mPrimaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
312 if( mPrimaryCursorVisible )
314 const Vector2& position = cursor.position;
316 mPrimaryCursor.SetPosition( position.x,
318 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
320 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
322 if( mSecondaryCursor )
324 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
325 mSecondaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
326 if( mSecondaryCursorVisible )
328 mSecondaryCursor.SetPosition( cursor.position.x,
330 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
332 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
335 // Show or hide the grab handle
336 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
337 if( grabHandle.active )
339 const Vector2& position = grabHandle.position;
341 const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f );
347 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
348 SetGrabHandlePosition();
350 // Sets the grab handle image according if it's pressed, flipped, etc.
351 SetHandleImage( GRAB_HANDLE );
353 grabHandle.actor.SetVisible( isVisible );
355 else if( grabHandle.actor )
357 grabHandle.actor.Unparent();
360 // Show or hide the selection handles/highlight
361 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
362 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
363 if( primary.active || secondary.active )
365 const Vector2& primaryPosition = primary.position;
366 const Vector2& secondaryPosition = secondary.position;
368 const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
369 const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
371 if( isPrimaryVisible || isSecondaryVisible )
373 CreateSelectionHandles();
375 if( isPrimaryVisible )
377 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
379 // Sets the primary handle image according if it's pressed, flipped, etc.
380 SetHandleImage( LEFT_SELECTION_HANDLE );
382 SetSelectionHandleMarkerSize( primary );
385 if( isSecondaryVisible )
387 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
389 // Sets the secondary handle image according if it's pressed, flipped, etc.
390 SetHandleImage( RIGHT_SELECTION_HANDLE );
392 SetSelectionHandleMarkerSize( secondary );
395 primary.actor.SetVisible( isPrimaryVisible );
396 secondary.actor.SetVisible( isSecondaryVisible );
405 primary.actor.Unparent();
407 if( secondary.actor )
409 secondary.actor.Unparent();
411 if( mHighlightActor )
413 mHighlightActor.Unparent();
417 if( mActiveCopyPastePopup )
423 if( mCopyPastePopup.actor )
425 mCopyPastePopup.actor.HidePopup();
426 mPopupSetNewPosition = true;
431 void UpdatePositions( const Vector2& scrollOffset )
433 mCursor[PRIMARY_CURSOR].position += scrollOffset;
434 mCursor[SECONDARY_CURSOR].position += scrollOffset;
435 mHandle[ GRAB_HANDLE ].position += scrollOffset;
436 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
437 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
438 mHighlightPosition += scrollOffset;
443 if ( !mCopyPastePopup.actor )
448 if( !mCopyPastePopup.actor.GetParent() )
450 mActiveLayer.Add( mCopyPastePopup.actor );
453 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
454 mCopyPastePopup.actor.ShowPopup();
457 void DeterminePositionPopup()
459 if ( !mActiveCopyPastePopup )
464 if( mPopupSetNewPosition )
466 if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
468 float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
469 float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
471 float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
473 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
474 mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
478 mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
482 Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
484 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
486 SetUpPopupPositionNotifications();
488 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
489 mPopupSetNewPosition = false;
492 void PopupRelayoutComplete( Actor actor )
494 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
496 DeterminePositionPopup();
499 void CreateCursor( ImageActor& cursor, const Vector4& color )
501 cursor = CreateSolidColorActor( color );
502 cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
503 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
504 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
507 // Add or Remove cursor(s) from parent
510 if( mActiveCursor == ACTIVE_CURSOR_NONE )
514 mPrimaryCursor.Unparent();
516 if( mSecondaryCursor )
518 mSecondaryCursor.Unparent();
523 // Create Primary and or Secondary Cursor(s) if active and add to parent
524 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
525 mActiveCursor == ACTIVE_CURSOR_BOTH )
527 if ( !mPrimaryCursor )
529 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
530 #ifdef DECORATOR_DEBUG
531 mPrimaryCursor.SetName( "PrimaryCursorActor" );
535 if( !mPrimaryCursor.GetParent() )
537 mActiveLayer.Add( mPrimaryCursor );
541 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
543 if ( !mSecondaryCursor )
545 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
546 #ifdef DECORATOR_DEBUG
547 mSecondaryCursor.SetName( "SecondaryCursorActor" );
551 if( !mSecondaryCursor.GetParent() )
553 mActiveLayer.Add( mSecondaryCursor );
558 if( mSecondaryCursor )
560 mSecondaryCursor.Unparent();
566 bool OnCursorBlinkTimerTick()
568 if( !mDelayCursorBlink )
571 if ( mPrimaryCursor )
573 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
575 if ( mSecondaryCursor )
577 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
580 mCursorBlinkStatus = !mCursorBlinkStatus;
585 mDelayCursorBlink = false;
591 void SetupTouchEvents()
593 mTapDetector = TapGestureDetector::New();
594 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
596 mPanGestureDetector = PanGestureDetector::New();
597 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
600 void CreateActiveLayer()
604 mActiveLayer = Layer::New();
605 #ifdef DECORATOR_DEBUG
606 mActiveLayer.SetName ( "ActiveLayerActor" );
609 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
610 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
611 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
613 // Add the active layer telling the controller it doesn't need clipping.
614 mController.AddDecoration( mActiveLayer, false );
617 mActiveLayer.RaiseToTop();
620 void SetSelectionHandleMarkerSize( HandleImpl& handle )
622 if ( handle.markerActor )
624 handle.markerActor.SetSize( 0, handle.lineHeight );
628 void CreateGrabHandle()
630 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
631 if( !grabHandle.actor )
633 if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
635 SetHandleImage( GRAB_HANDLE, HANDLE_IMAGE_RELEASED, ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED ) );
638 grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
639 grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
640 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
641 // Area that Grab handle responds to, larger than actual handle so easier to move
642 #ifdef DECORATOR_DEBUG
643 grabHandle.actor.SetName( "GrabHandleActor" );
644 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
646 grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
647 grabHandle.grabArea.SetName( "GrabArea" );
651 grabHandle.grabArea = Actor::New();
652 grabHandle.grabArea.SetName( "GrabArea" );
655 grabHandle.grabArea = Actor::New();
658 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
659 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
660 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
661 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
662 grabHandle.actor.Add( grabHandle.grabArea );
663 grabHandle.actor.SetColor( mHandleColor );
665 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
666 mTapDetector.Attach( grabHandle.grabArea );
667 mPanGestureDetector.Attach( grabHandle.grabArea );
669 mActiveLayer.Add( grabHandle.actor );
672 if( !grabHandle.actor.GetParent() )
674 mActiveLayer.Add( grabHandle.actor );
678 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
682 handle.markerActor = ImageActor::New( image );
683 handle.markerActor.SetColor( mHandleColor );
684 handle.actor.Add( handle.markerActor );
686 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
688 if ( LEFT_SELECTION_HANDLE == handleType )
690 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
691 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
693 else if ( RIGHT_SELECTION_HANDLE == handleType )
695 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
696 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
701 void CreateSelectionHandles()
703 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
706 primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
707 #ifdef DECORATOR_DEBUG
708 primary.actor.SetName("SelectionHandleOne");
710 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
711 primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
712 primary.actor.SetColor( mHandleColor );
714 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
715 #ifdef DECORATOR_DEBUG
716 primary.grabArea.SetName("SelectionHandleOneGrabArea");
718 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
719 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
720 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
721 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
723 mTapDetector.Attach( primary.grabArea );
724 mPanGestureDetector.Attach( primary.grabArea );
725 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
727 primary.actor.Add( primary.grabArea );
729 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
732 if( !primary.actor.GetParent() )
734 mActiveLayer.Add( primary.actor );
737 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
738 if( !secondary.actor )
740 secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
741 #ifdef DECORATOR_DEBUG
742 secondary.actor.SetName("SelectionHandleTwo");
744 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
745 secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
746 secondary.actor.SetColor( mHandleColor );
748 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
749 #ifdef DECORATOR_DEBUG
750 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
752 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
753 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
754 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
755 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
757 mTapDetector.Attach( secondary.grabArea );
758 mPanGestureDetector.Attach( secondary.grabArea );
759 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
761 secondary.actor.Add( secondary.grabArea );
763 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
766 if( !secondary.actor.GetParent() )
768 mActiveLayer.Add( secondary.actor );
772 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
774 // Get the world position of the active layer
775 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
777 // Get the size of the UI control.
779 mController.GetTargetSize( targetSize );
781 // The grab handle position in world coords.
782 position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x;
783 position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight;
786 void SetGrabHandlePosition()
788 // Reference to the grab handle.
789 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
791 // The grab handle position in world coords.
792 Vector2 grabHandleWorldPosition;
793 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
795 // Check if the grab handle exceeds the boundaries of the decoration box.
796 // At the moment only the height is checked for the grab handle.
797 grabHandle.verticallyFlipped = ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w );
799 // The grab handle 'y' position in local coords.
800 // If the grab handle exceeds the bottom of the decoration box,
801 // set the 'y' position to the top of the line.
802 // The SetGrabHandleImage() method will change the orientation.
803 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
805 grabHandle.actor.SetPosition( grabHandle.position.x - floor( 0.5f * mCursorWidth ),
806 yLocalPosition ); // TODO : Fix for multiline.
809 void SetSelectionHandlePosition( HandleType type )
811 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
813 // Reference to the selection handle.
814 HandleImpl& handle = mHandle[type];
816 // Get the world coordinates of the handle position.
817 Vector2 handleWorldPosition;
818 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
820 // Whether to flip the handle.
821 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
823 // Whether to flip the handles if they are crossed.
824 bool crossFlip = false;
825 if( mFlipSelectionHandlesOnCross || !mHandlePanning )
827 crossFlip = mHandleCurrentCrossed;
830 // Does not flip if both conditions are true (double flip)
831 flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
833 // Check if the selection handle exceeds the boundaries of the decoration box.
834 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
836 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
838 // Does not flip if both conditions are true (double flip)
839 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
843 if( !handle.horizontallyFlipped )
845 // Change the anchor point to flip the image.
846 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
848 handle.horizontallyFlipped = true;
853 if( handle.horizontallyFlipped )
855 // Reset the anchor point.
856 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
858 handle.horizontallyFlipped = false;
862 // Whether to flip the handle vertically.
863 handle.verticallyFlipped = ( handleWorldPosition.y + handle.size.height > mBoundingBox.w );
865 // The primary selection handle 'y' position in local coords.
866 // If the handle exceeds the bottom of the decoration box,
867 // set the 'y' position to the top of the line.
868 // The SetHandleImage() method will change the orientation.
869 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
871 handle.actor.SetPosition( handle.position.x,
872 yLocalPosition ); // TODO : Fix for multiline.
875 void SetHandleImage( HandleType type )
877 HandleImpl& handle = mHandle[type];
879 HandleType markerType = HANDLE_TYPE_COUNT;
880 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
881 if( LEFT_SELECTION_HANDLE == type )
883 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
884 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
886 else if( RIGHT_SELECTION_HANDLE == type )
888 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
889 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
892 // Chooses between the released or pressed image. It checks whether the pressed image exists.
893 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
895 handle.actor.SetImage( mHandleImages[type][imageType] );
897 if( HANDLE_TYPE_COUNT != markerType )
899 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
900 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
903 // Whether to flip the handle vertically.
904 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
907 void CreateHighlight()
909 if( !mHighlightActor )
911 mHighlightActor = Actor::New();
913 #ifdef DECORATOR_DEBUG
914 mHighlightActor.SetName( "HighlightActor" );
916 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
917 mHighlightActor.SetSize( 1.0f, 1.0f );
918 mHighlightActor.SetColor( mHighlightColor );
919 mHighlightActor.SetColorMode( USE_OWN_COLOR );
922 // Add the highlight box telling the controller it needs clipping.
923 mController.AddDecoration( mHighlightActor, true );
926 void UpdateHighlight()
928 if ( mHighlightActor )
930 if( !mHighlightQuadList.empty() )
932 Vector< Vector2 > vertices;
933 Vector< unsigned int> indices;
936 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
937 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
939 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
941 QuadCoordinates& quad = *iter;
944 vertex.x = quad.min.x;
945 vertex.y = quad.min.y;
946 vertices.PushBack( vertex );
949 vertex.x = quad.max.x;
950 vertex.y = quad.min.y;
951 vertices.PushBack( vertex );
954 vertex.x = quad.min.x;
955 vertex.y = quad.max.y;
956 vertices.PushBack( vertex );
958 // bottom-right (v+3)
959 vertex.x = quad.max.x;
960 vertex.y = quad.max.y;
961 vertices.PushBack( vertex );
963 // triangle A (3, 1, 0)
964 indices.PushBack( v + 3 );
965 indices.PushBack( v + 1 );
966 indices.PushBack( v );
968 // triangle B (0, 2, 3)
969 indices.PushBack( v );
970 indices.PushBack( v + 2 );
971 indices.PushBack( v + 3 );
976 mQuadVertices.SetSize( vertices.Size() );
980 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
985 mQuadIndices.SetSize( indices.Size() );
989 mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
992 mQuadVertices.SetData( &vertices[ 0 ] );
993 mQuadIndices.SetData( &indices[ 0 ] );
997 mQuadGeometry = Geometry::New();
998 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1000 mQuadGeometry.SetIndexBuffer( mQuadIndices );
1002 if( !mHighlightRenderer )
1004 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1005 mHighlightActor.AddRenderer( mHighlightRenderer );
1009 mHighlightActor.SetPosition( mHighlightPosition.x,
1010 mHighlightPosition.y );
1012 mHighlightQuadList.clear();
1014 if( mHighlightRenderer )
1016 mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1021 void OnTap( Actor actor, const TapGesture& tap )
1023 if( actor == mHandle[GRAB_HANDLE].actor )
1029 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1031 if( Gesture::Started == gesture.state )
1033 handle.grabDisplacementX = handle.grabDisplacementY = 0;
1036 handle.grabDisplacementX += gesture.displacement.x;
1037 handle.grabDisplacementY += gesture.displacement.y;
1039 const float x = handle.position.x + handle.grabDisplacementX;
1040 const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1042 if( Gesture::Started == gesture.state ||
1043 Gesture::Continuing == gesture.state )
1046 mController.GetTargetSize( targetSize );
1048 if( x < mScrollThreshold )
1050 mScrollDirection = SCROLL_RIGHT;
1051 mHandleScrolling = type;
1054 else if( x > targetSize.width - mScrollThreshold )
1056 mScrollDirection = SCROLL_LEFT;
1057 mHandleScrolling = type;
1062 mHandleScrolling = HANDLE_TYPE_COUNT;
1064 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1067 mHandlePanning = true;
1069 else if( Gesture::Finished == gesture.state ||
1070 Gesture::Cancelled == gesture.state )
1073 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1075 mNotifyEndOfScroll = false;
1076 mHandleScrolling = HANDLE_TYPE_COUNT;
1078 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1082 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1085 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1086 handle.pressed = false;
1088 mHandlePanning = false;
1092 void OnPan( Actor actor, const PanGesture& gesture )
1094 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1095 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1096 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1098 if( actor == grabHandle.grabArea )
1100 DoPan( grabHandle, GRAB_HANDLE, gesture );
1102 else if( actor == primarySelectionHandle.grabArea )
1104 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1106 else if( actor == secondarySelectionHandle.grabArea )
1108 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1112 bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1114 // Switch between pressed/release grab-handle images
1115 if( event.GetPointCount() > 0 &&
1116 mHandle[GRAB_HANDLE].actor )
1118 const TouchPoint& point = event.GetPoint(0);
1120 if( TouchPoint::Down == point.state )
1122 mHandle[GRAB_HANDLE].pressed = true;
1124 else if( ( TouchPoint::Up == point.state ) ||
1125 ( TouchPoint::Interrupted == point.state ) )
1127 mHandle[GRAB_HANDLE].pressed = false;
1130 SetHandleImage( GRAB_HANDLE );
1133 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1137 bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1139 // Switch between pressed/release selection handle images
1140 if( event.GetPointCount() > 0 &&
1141 mHandle[LEFT_SELECTION_HANDLE].actor )
1143 const TouchPoint& point = event.GetPoint(0);
1145 if( TouchPoint::Down == point.state )
1147 mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1149 else if( ( TouchPoint::Up == point.state ) ||
1150 ( TouchPoint::Interrupted == point.state ) )
1152 mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1153 mHandlePreviousCrossed = mHandleCurrentCrossed;
1154 mHandlePanning = false;
1157 SetHandleImage( LEFT_SELECTION_HANDLE );
1160 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1164 bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1166 // Switch between pressed/release selection handle images
1167 if( event.GetPointCount() > 0 &&
1168 mHandle[RIGHT_SELECTION_HANDLE].actor )
1170 const TouchPoint& point = event.GetPoint(0);
1172 if( TouchPoint::Down == point.state )
1174 mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1176 else if( ( TouchPoint::Up == point.state ) ||
1177 ( TouchPoint::Interrupted == point.state ) )
1179 mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1180 mHandlePreviousCrossed = mHandleCurrentCrossed;
1181 mHandlePanning = false;
1184 SetHandleImage( RIGHT_SELECTION_HANDLE );
1187 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1193 float AlternatePopUpPositionRelativeToCursor()
1195 float alternativePosition=0.0f;;
1197 if ( mPrimaryCursor ) // Secondary cursor not used for paste
1199 Cursor cursor = PRIMARY_CURSOR;
1200 alternativePosition = mCursor[cursor].position.y;
1203 const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1205 if( mHandle[GRAB_HANDLE].active )
1207 // If grab handle enabled then position pop-up below the grab handle.
1208 const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1209 const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1210 alternativePosition += grabHandleSize.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1214 alternativePosition += popupHeight;
1217 return alternativePosition;
1220 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1222 float alternativeYPosition=0.0f;
1223 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1224 // if can't be positioned above, then position below row.
1225 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1227 mCopyPastePopup.actor.SetY( alternativeYPosition );
1231 void SetUpPopupPositionNotifications( )
1233 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1235 // Exceeding vertical boundary
1237 float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1239 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1240 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1241 mBoundingBox.w - popupHeight * 0.5f ) );
1243 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1246 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld )
1248 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1250 // Parent must already by added to Stage for these Get calls to work
1251 Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1252 Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1253 Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1254 Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1256 // Calculate distance to move popup (in local space) so fits within the boundary
1257 float xOffSetToKeepWithinBounds = 0.0f;
1258 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1260 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1262 else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1264 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1267 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1268 if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1270 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1273 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1275 // Prevent pixel mis-alignment by rounding down.
1276 requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1277 requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1280 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1282 HandleImpl& handle = mHandle[handleType];
1283 handle.size = Size( image.GetWidth(), image.GetHeight() );
1285 mHandleImages[handleType][handleImageType] = image;
1288 void SetScrollThreshold( float threshold )
1290 mScrollThreshold = threshold;
1293 float GetScrollThreshold() const
1295 return mScrollThreshold;
1298 void SetScrollSpeed( float speed )
1300 mScrollSpeed = speed;
1301 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1304 float GetScrollSpeed() const
1306 return mScrollSpeed;
1309 void NotifyEndOfScroll()
1315 mNotifyEndOfScroll = true;
1320 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1322 * It only starts the timer if it's already created.
1324 void StartScrollTimer()
1328 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1329 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1332 if( !mScrollTimer.IsRunning() )
1334 mScrollTimer.Start();
1339 * Stops the timer used to scroll the text.
1341 void StopScrollTimer()
1345 mScrollTimer.Stop();
1350 * Callback called by the timer used to scroll the text.
1352 * It calculates and sets a new scroll position.
1354 bool OnScrollTimerTick()
1356 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1358 mController.DecorationEvent( mHandleScrolling,
1360 mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1367 ControllerInterface& mController;
1369 TapGestureDetector mTapDetector;
1370 PanGestureDetector mPanGestureDetector;
1371 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1372 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1374 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1375 ImageActor mPrimaryCursor;
1376 ImageActor mSecondaryCursor;
1378 Actor mHighlightActor; ///< Actor to display highlight
1379 Renderer mHighlightRenderer;
1380 Material mHighlightMaterial; ///< Material used for highlight
1381 Property::Map mQuadVertexFormat;
1382 Property::Map mQuadIndexFormat;
1383 PopupImpl mCopyPastePopup;
1384 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1385 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1387 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1388 Vector4 mHandleColor;
1390 CursorImpl mCursor[CURSOR_COUNT];
1391 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1393 PropertyBuffer mQuadVertices;
1394 PropertyBuffer mQuadIndices;
1395 Geometry mQuadGeometry;
1396 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1398 Vector4 mBoundingBox; ///< The bounding box in world coords.
1399 Vector4 mHighlightColor; ///< Color of the highlight
1400 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1402 unsigned int mActiveCursor;
1403 unsigned int mCursorBlinkInterval;
1404 float mCursorBlinkDuration;
1405 float mCursorWidth; ///< The width of the cursors in pixels.
1406 HandleType mHandleScrolling; ///< The handle which is scrolling.
1407 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1408 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1409 float mScrollSpeed; ///< The scroll speed in pixels per second.
1410 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1411 int mTextDepth; ///< The depth used to render the text.
1413 bool mActiveCopyPastePopup : 1;
1414 bool mPopupSetNewPosition : 1;
1415 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1416 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1417 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1418 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1419 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1420 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1421 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1422 bool mHandlePanning : 1; ///< Whether any of the handles is moving.
1423 bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
1424 bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1425 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1428 DecoratorPtr Decorator::New( ControllerInterface& controller,
1429 TextSelectionPopupCallbackInterface& callbackInterface )
1431 return DecoratorPtr( new Decorator( controller,
1432 callbackInterface ) );
1435 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1437 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1440 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1442 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1445 void Decorator::Relayout( const Vector2& size )
1447 mImpl->Relayout( size );
1450 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1452 mImpl->UpdatePositions( scrollOffset );
1457 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1459 mImpl->mActiveCursor = activeCursor;
1462 unsigned int Decorator::GetActiveCursor() const
1464 return mImpl->mActiveCursor;
1467 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1469 mImpl->mCursor[cursor].position.x = x;
1470 mImpl->mCursor[cursor].position.y = y;
1471 mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1472 mImpl->mCursor[cursor].lineHeight = lineHeight;
1475 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1477 x = mImpl->mCursor[cursor].position.x;
1478 y = mImpl->mCursor[cursor].position.y;
1479 cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1480 lineHeight = mImpl->mCursor[cursor].lineHeight;
1483 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1485 return mImpl->mCursor[cursor].position;
1488 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1490 mImpl->mCursor[cursor].color = color;
1493 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1495 return mImpl->mCursor[cursor].color;
1498 void Decorator::StartCursorBlink()
1500 if ( !mImpl->mCursorBlinkTimer )
1502 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1503 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1506 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1508 mImpl->mCursorBlinkTimer.Start();
1512 void Decorator::StopCursorBlink()
1514 if ( mImpl->mCursorBlinkTimer )
1516 mImpl->mCursorBlinkTimer.Stop();
1519 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1522 void Decorator::DelayCursorBlink()
1524 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1525 mImpl->mDelayCursorBlink = true;
1528 void Decorator::SetCursorBlinkInterval( float seconds )
1530 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1533 float Decorator::GetCursorBlinkInterval() const
1535 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1538 void Decorator::SetCursorBlinkDuration( float seconds )
1540 mImpl->mCursorBlinkDuration = seconds;
1543 float Decorator::GetCursorBlinkDuration() const
1545 return mImpl->mCursorBlinkDuration;
1548 void Decorator::SetCursorWidth( int width )
1550 mImpl->mCursorWidth = static_cast<float>( width );
1553 int Decorator::GetCursorWidth() const
1555 return static_cast<int>( mImpl->mCursorWidth );
1560 void Decorator::SetHandleActive( HandleType handleType, bool active )
1562 mImpl->mHandle[handleType].active = active;
1566 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1568 mImpl->mHandlePreviousCrossed = false;
1571 // TODO: this is a work-around.
1572 // The problem is the handle actor does not receive the touch event with the Interrupt
1573 // state when the power button is pressed and the application goes to background.
1574 mImpl->mHandle[handleType].pressed = false;
1575 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1576 ImageActor imageActor = mImpl->mHandle[handleType].actor;
1577 if( imageReleased && imageActor )
1579 imageActor.SetImage( imageReleased );
1585 bool Decorator::IsHandleActive( HandleType handleType ) const
1587 return mImpl->mHandle[handleType].active ;
1590 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1592 mImpl->SetHandleImage( handleType, handleImageType, image );
1595 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1597 return mImpl->mHandleImages[handleType][handleImageType];
1600 void Decorator::SetHandleColor( const Vector4& color )
1602 mImpl->mHandleColor = color;
1605 const Vector4& Decorator::GetHandleColor() const
1607 return mImpl->mHandleColor;
1610 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1612 // Adjust grab handle displacement
1613 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1615 handle.grabDisplacementX -= x - handle.position.x;
1616 handle.grabDisplacementY -= y - handle.position.y;
1618 handle.position.x = x;
1619 handle.position.y = y;
1620 handle.lineHeight = height;
1623 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1625 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1627 x = handle.position.x;
1628 y = handle.position.y;
1629 height = handle.lineHeight;
1632 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1634 return mImpl->mHandle[handleType].position;
1637 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1639 mImpl->mFlipSelectionHandlesOnCross = enable;
1642 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1644 mImpl->mHandleCurrentCrossed = indicesSwapped;
1645 mImpl->mFlipLeftSelectionHandleDirection = left;
1646 mImpl->mFlipRightSelectionHandleDirection = right;
1649 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1651 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1654 void Decorator::ClearHighlights()
1656 mImpl->mHighlightQuadList.clear();
1657 mImpl->mHighlightPosition = Vector2::ZERO;
1660 void Decorator::SetHighlightColor( const Vector4& color )
1662 mImpl->mHighlightColor = color;
1665 const Vector4& Decorator::GetHighlightColor() const
1667 return mImpl->mHighlightColor;
1670 void Decorator::SetTextDepth( int textDepth )
1672 mImpl->mTextDepth = textDepth;
1675 void Decorator::SetPopupActive( bool active )
1677 mImpl->mActiveCopyPastePopup = active;
1680 bool Decorator::IsPopupActive() const
1682 return mImpl->mActiveCopyPastePopup ;
1685 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1687 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1689 if ( !mImpl->mCopyPastePopup.actor )
1691 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1692 #ifdef DECORATOR_DEBUG
1693 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1695 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1696 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
1699 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1702 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1704 return mImpl->mEnabledPopupButtons;
1709 void Decorator::SetScrollThreshold( float threshold )
1711 mImpl->SetScrollThreshold( threshold );
1714 float Decorator::GetScrollThreshold() const
1716 return mImpl->GetScrollThreshold();
1719 void Decorator::SetScrollSpeed( float speed )
1721 mImpl->SetScrollSpeed( speed );
1724 float Decorator::GetScrollSpeed() const
1726 return mImpl->GetScrollSpeed();
1729 void Decorator::NotifyEndOfScroll()
1731 mImpl->NotifyEndOfScroll();
1734 Decorator::~Decorator()
1739 Decorator::Decorator( ControllerInterface& controller,
1740 TextSelectionPopupCallbackInterface& callbackInterface )
1743 mImpl = new Decorator::Impl( controller, callbackInterface );
1748 } // namespace Toolkit