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 mCursorBlinkStatus( true ),
277 mDelayCursorBlink( false ),
278 mPrimaryCursorVisible( false ),
279 mSecondaryCursorVisible( false ),
280 mFlipSelectionHandlesOnCross( false ),
281 mFlipLeftSelectionHandleDirection( false ),
282 mFlipRightSelectionHandleDirection( false ),
283 mHandlePanning( false ),
284 mHandleCurrentCrossed( false ),
285 mHandlePreviousCrossed( false ),
286 mNotifyEndOfScroll( false )
288 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
289 mQuadIndexFormat[ "indices" ] = Property::INTEGER;
290 mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
296 * Relayout of the decorations owned by the decorator.
297 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
299 void Relayout( const Vector2& size )
301 // TODO - Remove this if nothing is active
304 // Show or hide the cursors
309 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
310 mPrimaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
311 if( mPrimaryCursorVisible )
313 const Vector2& position = cursor.position;
315 mPrimaryCursor.SetPosition( position.x,
317 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
319 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
321 if( mSecondaryCursor )
323 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
324 mSecondaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
325 if( mSecondaryCursorVisible )
327 mSecondaryCursor.SetPosition( cursor.position.x,
329 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
331 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
334 // Show or hide the grab handle
335 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
336 if( grabHandle.active )
338 const Vector2& position = grabHandle.position;
340 const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f );
346 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
347 SetGrabHandlePosition();
349 // Sets the grab handle image according if it's pressed, flipped, etc.
350 SetHandleImage( GRAB_HANDLE );
352 grabHandle.actor.SetVisible( isVisible );
354 else if( grabHandle.actor )
356 grabHandle.actor.Unparent();
359 // Show or hide the selection handles/highlight
360 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
361 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
362 if( primary.active || secondary.active )
364 const Vector2& primaryPosition = primary.position;
365 const Vector2& secondaryPosition = secondary.position;
367 const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
368 const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
370 if( isPrimaryVisible || isSecondaryVisible )
372 CreateSelectionHandles();
374 if( isPrimaryVisible )
376 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
378 // Sets the primary handle image according if it's pressed, flipped, etc.
379 SetHandleImage( LEFT_SELECTION_HANDLE );
381 SetSelectionHandleMarkerSize( primary );
384 if( isSecondaryVisible )
386 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
388 // Sets the secondary handle image according if it's pressed, flipped, etc.
389 SetHandleImage( RIGHT_SELECTION_HANDLE );
391 SetSelectionHandleMarkerSize( secondary );
394 primary.actor.SetVisible( isPrimaryVisible );
395 secondary.actor.SetVisible( isSecondaryVisible );
404 primary.actor.Unparent();
406 if( secondary.actor )
408 secondary.actor.Unparent();
410 if( mHighlightActor )
412 mHighlightActor.Unparent();
416 if( mActiveCopyPastePopup )
422 if( mCopyPastePopup.actor )
424 mCopyPastePopup.actor.HidePopup();
429 void UpdatePositions( const Vector2& scrollOffset )
431 mCursor[PRIMARY_CURSOR].position += scrollOffset;
432 mCursor[SECONDARY_CURSOR].position += scrollOffset;
433 mHandle[ GRAB_HANDLE ].position += scrollOffset;
434 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
435 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
436 mHighlightPosition += scrollOffset;
441 if ( !mCopyPastePopup.actor )
446 if( !mCopyPastePopup.actor.GetParent() )
448 mActiveLayer.Add( mCopyPastePopup.actor );
451 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
452 mCopyPastePopup.actor.ShowPopup();
455 void DeterminePositionPopup()
457 if ( !mActiveCopyPastePopup )
462 if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
464 float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
465 float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
467 float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
469 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
470 mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
474 mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
477 Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
479 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
481 SetUpPopupPositionNotifications();
483 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
486 void PopupRelayoutComplete( Actor actor )
488 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
490 DeterminePositionPopup();
493 void CreateCursor( ImageActor& cursor, const Vector4& color )
495 cursor = CreateSolidColorActor( color );
496 cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
497 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
498 cursor.SetAnchorPoint( AnchorPoint::TOP_RIGHT );
501 // Add or Remove cursor(s) from parent
504 if( mActiveCursor == ACTIVE_CURSOR_NONE )
508 mPrimaryCursor.Unparent();
510 if( mSecondaryCursor )
512 mSecondaryCursor.Unparent();
517 // Create Primary and or Secondary Cursor(s) if active and add to parent
518 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
519 mActiveCursor == ACTIVE_CURSOR_BOTH )
521 if ( !mPrimaryCursor )
523 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
524 #ifdef DECORATOR_DEBUG
525 mPrimaryCursor.SetName( "PrimaryCursorActor" );
529 if( !mPrimaryCursor.GetParent() )
531 mActiveLayer.Add( mPrimaryCursor );
535 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
537 if ( !mSecondaryCursor )
539 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
540 #ifdef DECORATOR_DEBUG
541 mSecondaryCursor.SetName( "SecondaryCursorActor" );
545 if( !mSecondaryCursor.GetParent() )
547 mActiveLayer.Add( mSecondaryCursor );
552 if( mSecondaryCursor )
554 mSecondaryCursor.Unparent();
560 bool OnCursorBlinkTimerTick()
562 if( !mDelayCursorBlink )
565 if ( mPrimaryCursor )
567 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
569 if ( mSecondaryCursor )
571 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
574 mCursorBlinkStatus = !mCursorBlinkStatus;
579 mDelayCursorBlink = false;
585 void SetupTouchEvents()
587 mTapDetector = TapGestureDetector::New();
588 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
590 mPanGestureDetector = PanGestureDetector::New();
591 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
594 void CreateActiveLayer()
598 mActiveLayer = Layer::New();
599 #ifdef DECORATOR_DEBUG
600 mActiveLayer.SetName ( "ActiveLayerActor" );
603 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
604 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
605 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
607 // Add the active layer telling the controller it doesn't need clipping.
608 mController.AddDecoration( mActiveLayer, false );
611 mActiveLayer.RaiseToTop();
614 void SetSelectionHandleMarkerSize( HandleImpl& handle )
616 if ( handle.markerActor )
618 handle.markerActor.SetSize( 0, handle.lineHeight );
622 void CreateGrabHandle()
624 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
625 if( !grabHandle.actor )
627 if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
629 SetHandleImage( GRAB_HANDLE, HANDLE_IMAGE_RELEASED, ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED ) );
632 grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
633 grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
634 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
635 // Area that Grab handle responds to, larger than actual handle so easier to move
636 #ifdef DECORATOR_DEBUG
637 grabHandle.actor.SetName( "GrabHandleActor" );
638 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
640 grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
641 grabHandle.grabArea.SetName( "GrabArea" );
645 grabHandle.grabArea = Actor::New();
646 grabHandle.grabArea.SetName( "GrabArea" );
649 grabHandle.grabArea = Actor::New();
652 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
653 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
654 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
655 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
656 grabHandle.actor.Add( grabHandle.grabArea );
657 grabHandle.actor.SetColor( mHandleColor );
659 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
660 mTapDetector.Attach( grabHandle.grabArea );
661 mPanGestureDetector.Attach( grabHandle.grabArea );
663 mActiveLayer.Add( grabHandle.actor );
666 if( !grabHandle.actor.GetParent() )
668 mActiveLayer.Add( grabHandle.actor );
672 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
676 handle.markerActor = ImageActor::New( image );
677 handle.markerActor.SetColor( mHandleColor );
678 handle.actor.Add( handle.markerActor );
680 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
682 if ( LEFT_SELECTION_HANDLE == handleType )
684 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
685 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
687 else if ( RIGHT_SELECTION_HANDLE == handleType )
689 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
690 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
695 void CreateSelectionHandles()
697 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
700 primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
701 #ifdef DECORATOR_DEBUG
702 primary.actor.SetName("SelectionHandleOne");
704 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
705 primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
706 primary.actor.SetColor( mHandleColor );
708 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
709 #ifdef DECORATOR_DEBUG
710 primary.grabArea.SetName("SelectionHandleOneGrabArea");
712 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
713 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
714 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
715 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
717 mTapDetector.Attach( primary.grabArea );
718 mPanGestureDetector.Attach( primary.grabArea );
719 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
721 primary.actor.Add( primary.grabArea );
723 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
726 if( !primary.actor.GetParent() )
728 mActiveLayer.Add( primary.actor );
731 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
732 if( !secondary.actor )
734 secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
735 #ifdef DECORATOR_DEBUG
736 secondary.actor.SetName("SelectionHandleTwo");
738 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
739 secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
740 secondary.actor.SetColor( mHandleColor );
742 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
743 #ifdef DECORATOR_DEBUG
744 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
746 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
747 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
748 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
749 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
751 mTapDetector.Attach( secondary.grabArea );
752 mPanGestureDetector.Attach( secondary.grabArea );
753 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
755 secondary.actor.Add( secondary.grabArea );
757 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
760 if( !secondary.actor.GetParent() )
762 mActiveLayer.Add( secondary.actor );
766 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
768 // Get the world position of the active layer
769 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
771 // Get the size of the UI control.
773 mController.GetTargetSize( targetSize );
775 // The grab handle position in world coords.
776 position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x;
777 position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight;
780 void SetGrabHandlePosition()
782 // Reference to the grab handle.
783 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
785 // The grab handle position in world coords.
786 Vector2 grabHandleWorldPosition;
787 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
789 // Check if the grab handle exceeds the boundaries of the decoration box.
790 // At the moment only the height is checked for the grab handle.
791 grabHandle.verticallyFlipped = ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w );
793 // The grab handle 'y' position in local coords.
794 // If the grab handle exceeds the bottom of the decoration box,
795 // set the 'y' position to the top of the line.
796 // The SetGrabHandleImage() method will change the orientation.
797 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
799 grabHandle.actor.SetPosition( grabHandle.position.x - floor( 0.5f * mCursorWidth ),
800 yLocalPosition ); // TODO : Fix for multiline.
803 void SetSelectionHandlePosition( HandleType type )
805 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
807 // Reference to the selection handle.
808 HandleImpl& handle = mHandle[type];
810 // Get the world coordinates of the handle position.
811 Vector2 handleWorldPosition;
812 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
814 // Whether to flip the handle.
815 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
817 // Whether to flip the handles if they are crossed.
818 bool crossFlip = false;
819 if( mFlipSelectionHandlesOnCross || !mHandlePanning )
821 crossFlip = mHandleCurrentCrossed;
824 // Does not flip if both conditions are true (double flip)
825 flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
827 // Check if the selection handle exceeds the boundaries of the decoration box.
828 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
830 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
832 // Does not flip if both conditions are true (double flip)
833 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
837 if( !handle.horizontallyFlipped )
839 // Change the anchor point to flip the image.
840 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
842 handle.horizontallyFlipped = true;
847 if( handle.horizontallyFlipped )
849 // Reset the anchor point.
850 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
852 handle.horizontallyFlipped = false;
856 // Whether to flip the handle vertically.
857 handle.verticallyFlipped = ( handleWorldPosition.y + handle.size.height > mBoundingBox.w );
859 // The primary selection handle 'y' position in local coords.
860 // If the handle exceeds the bottom of the decoration box,
861 // set the 'y' position to the top of the line.
862 // The SetHandleImage() method will change the orientation.
863 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
865 handle.actor.SetPosition( handle.position.x,
866 yLocalPosition ); // TODO : Fix for multiline.
869 void SetHandleImage( HandleType type )
871 HandleImpl& handle = mHandle[type];
873 HandleType markerType = HANDLE_TYPE_COUNT;
874 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
875 if( LEFT_SELECTION_HANDLE == type )
877 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
878 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
880 else if( RIGHT_SELECTION_HANDLE == type )
882 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
883 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
886 // Chooses between the released or pressed image. It checks whether the pressed image exists.
887 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
889 handle.actor.SetImage( mHandleImages[type][imageType] );
891 if( HANDLE_TYPE_COUNT != markerType )
893 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
894 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
897 // Whether to flip the handle vertically.
898 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
901 void CreateHighlight()
903 if( !mHighlightActor )
905 mHighlightActor = Actor::New();
907 #ifdef DECORATOR_DEBUG
908 mHighlightActor.SetName( "HighlightActor" );
910 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
911 mHighlightActor.SetSize( 1.0f, 1.0f );
912 mHighlightActor.SetColor( mHighlightColor );
913 mHighlightActor.SetColorMode( USE_OWN_COLOR );
916 // Add the highlight box telling the controller it needs clipping.
917 mController.AddDecoration( mHighlightActor, true );
920 void UpdateHighlight()
922 if ( mHighlightActor )
924 if( !mHighlightQuadList.empty() )
926 Vector< Vector2 > vertices;
927 Vector< unsigned int> indices;
930 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
931 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
933 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
935 QuadCoordinates& quad = *iter;
938 vertex.x = quad.min.x;
939 vertex.y = quad.min.y;
940 vertices.PushBack( vertex );
943 vertex.x = quad.max.x;
944 vertex.y = quad.min.y;
945 vertices.PushBack( vertex );
948 vertex.x = quad.min.x;
949 vertex.y = quad.max.y;
950 vertices.PushBack( vertex );
952 // bottom-right (v+3)
953 vertex.x = quad.max.x;
954 vertex.y = quad.max.y;
955 vertices.PushBack( vertex );
957 // triangle A (3, 1, 0)
958 indices.PushBack( v + 3 );
959 indices.PushBack( v + 1 );
960 indices.PushBack( v );
962 // triangle B (0, 2, 3)
963 indices.PushBack( v );
964 indices.PushBack( v + 2 );
965 indices.PushBack( v + 3 );
970 mQuadVertices.SetSize( vertices.Size() );
974 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
979 mQuadIndices.SetSize( indices.Size() );
983 mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
986 mQuadVertices.SetData( &vertices[ 0 ] );
987 mQuadIndices.SetData( &indices[ 0 ] );
991 mQuadGeometry = Geometry::New();
992 mQuadGeometry.AddVertexBuffer( mQuadVertices );
994 mQuadGeometry.SetIndexBuffer( mQuadIndices );
996 if( !mHighlightRenderer )
998 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
999 mHighlightActor.AddRenderer( mHighlightRenderer );
1003 mHighlightActor.SetPosition( mHighlightPosition.x,
1004 mHighlightPosition.y );
1006 mHighlightQuadList.clear();
1008 if( mHighlightRenderer )
1010 mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1015 void OnTap( Actor actor, const TapGesture& tap )
1017 if( actor == mHandle[GRAB_HANDLE].actor )
1023 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1025 if( Gesture::Started == gesture.state )
1027 handle.grabDisplacementX = handle.grabDisplacementY = 0;
1030 handle.grabDisplacementX += gesture.displacement.x;
1031 handle.grabDisplacementY += gesture.displacement.y;
1033 const float x = handle.position.x + handle.grabDisplacementX;
1034 const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1036 if( Gesture::Started == gesture.state ||
1037 Gesture::Continuing == gesture.state )
1040 mController.GetTargetSize( targetSize );
1042 if( x < mScrollThreshold )
1044 mScrollDirection = SCROLL_RIGHT;
1045 mHandleScrolling = type;
1048 else if( x > targetSize.width - mScrollThreshold )
1050 mScrollDirection = SCROLL_LEFT;
1051 mHandleScrolling = type;
1056 mHandleScrolling = HANDLE_TYPE_COUNT;
1058 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1061 mHandlePanning = true;
1063 else if( Gesture::Finished == gesture.state ||
1064 Gesture::Cancelled == gesture.state )
1067 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1069 mNotifyEndOfScroll = false;
1070 mHandleScrolling = HANDLE_TYPE_COUNT;
1072 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1076 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1079 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1080 handle.pressed = false;
1082 mHandlePanning = false;
1086 void OnPan( Actor actor, const PanGesture& gesture )
1088 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1089 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1090 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1092 if( actor == grabHandle.grabArea )
1094 DoPan( grabHandle, GRAB_HANDLE, gesture );
1096 else if( actor == primarySelectionHandle.grabArea )
1098 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1100 else if( actor == secondarySelectionHandle.grabArea )
1102 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1106 bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1108 // Switch between pressed/release grab-handle images
1109 if( event.GetPointCount() > 0 &&
1110 mHandle[GRAB_HANDLE].actor )
1112 const TouchPoint& point = event.GetPoint(0);
1114 if( TouchPoint::Down == point.state )
1116 mHandle[GRAB_HANDLE].pressed = true;
1118 else if( ( TouchPoint::Up == point.state ) ||
1119 ( TouchPoint::Interrupted == point.state ) )
1121 mHandle[GRAB_HANDLE].pressed = false;
1124 SetHandleImage( GRAB_HANDLE );
1127 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1131 bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1133 // Switch between pressed/release selection handle images
1134 if( event.GetPointCount() > 0 &&
1135 mHandle[LEFT_SELECTION_HANDLE].actor )
1137 const TouchPoint& point = event.GetPoint(0);
1139 if( TouchPoint::Down == point.state )
1141 mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1143 else if( ( TouchPoint::Up == point.state ) ||
1144 ( TouchPoint::Interrupted == point.state ) )
1146 mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1147 mHandlePreviousCrossed = mHandleCurrentCrossed;
1148 mHandlePanning = false;
1151 SetHandleImage( LEFT_SELECTION_HANDLE );
1154 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1158 bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1160 // Switch between pressed/release selection handle images
1161 if( event.GetPointCount() > 0 &&
1162 mHandle[RIGHT_SELECTION_HANDLE].actor )
1164 const TouchPoint& point = event.GetPoint(0);
1166 if( TouchPoint::Down == point.state )
1168 mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1170 else if( ( TouchPoint::Up == point.state ) ||
1171 ( TouchPoint::Interrupted == point.state ) )
1173 mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1174 mHandlePreviousCrossed = mHandleCurrentCrossed;
1175 mHandlePanning = false;
1178 SetHandleImage( RIGHT_SELECTION_HANDLE );
1181 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1187 float AlternatePopUpPositionRelativeToCursor()
1189 float alternativePosition=0.0f;;
1191 if ( mPrimaryCursor ) // Secondary cursor not used for paste
1193 Cursor cursor = PRIMARY_CURSOR;
1194 alternativePosition = mCursor[cursor].position.y;
1197 const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1199 if( mHandle[GRAB_HANDLE].active )
1201 // If grab handle enabled then position pop-up below the grab handle.
1202 const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1203 const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1204 alternativePosition += grabHandleSize.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1208 alternativePosition += popupHeight;
1211 return alternativePosition;
1214 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1216 float alternativeYPosition=0.0f;
1217 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1218 // if can't be positioned above, then position below row.
1219 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1221 mCopyPastePopup.actor.SetY( alternativeYPosition );
1225 void SetUpPopupPositionNotifications( )
1227 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1229 // Exceeding vertical boundary
1231 float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1233 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1234 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1235 mBoundingBox.w - popupHeight * 0.5f ) );
1237 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1240 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld )
1242 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1244 // Parent must already by added to Stage for these Get calls to work
1245 Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1246 Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1247 Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1248 Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1250 // Calculate distance to move popup (in local space) so fits within the boundary
1251 float xOffSetToKeepWithinBounds = 0.0f;
1252 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1254 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1256 else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1258 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1261 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1262 if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1264 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1267 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1269 // Prevent pixel mis-alignment by rounding down.
1270 requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1271 requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1274 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1276 HandleImpl& handle = mHandle[handleType];
1277 handle.size = Size( image.GetWidth(), image.GetHeight() );
1279 mHandleImages[handleType][handleImageType] = image;
1282 void SetScrollThreshold( float threshold )
1284 mScrollThreshold = threshold;
1287 float GetScrollThreshold() const
1289 return mScrollThreshold;
1292 void SetScrollSpeed( float speed )
1294 mScrollSpeed = speed;
1295 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1298 float GetScrollSpeed() const
1300 return mScrollSpeed;
1303 void NotifyEndOfScroll()
1309 mNotifyEndOfScroll = true;
1314 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1316 * It only starts the timer if it's already created.
1318 void StartScrollTimer()
1322 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1323 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1326 if( !mScrollTimer.IsRunning() )
1328 mScrollTimer.Start();
1333 * Stops the timer used to scroll the text.
1335 void StopScrollTimer()
1339 mScrollTimer.Stop();
1344 * Callback called by the timer used to scroll the text.
1346 * It calculates and sets a new scroll position.
1348 bool OnScrollTimerTick()
1350 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1352 mController.DecorationEvent( mHandleScrolling,
1354 mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1361 ControllerInterface& mController;
1363 TapGestureDetector mTapDetector;
1364 PanGestureDetector mPanGestureDetector;
1365 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1366 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1368 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1369 ImageActor mPrimaryCursor;
1370 ImageActor mSecondaryCursor;
1372 Actor mHighlightActor; ///< Actor to display highlight
1373 Renderer mHighlightRenderer;
1374 Material mHighlightMaterial; ///< Material used for highlight
1375 Property::Map mQuadVertexFormat;
1376 Property::Map mQuadIndexFormat;
1377 PopupImpl mCopyPastePopup;
1378 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1379 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1381 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1382 Vector4 mHandleColor;
1384 CursorImpl mCursor[CURSOR_COUNT];
1385 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1387 PropertyBuffer mQuadVertices;
1388 PropertyBuffer mQuadIndices;
1389 Geometry mQuadGeometry;
1390 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1392 Vector4 mBoundingBox; ///< The bounding box in world coords.
1393 Vector4 mHighlightColor; ///< Color of the highlight
1394 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1396 unsigned int mActiveCursor;
1397 unsigned int mCursorBlinkInterval;
1398 float mCursorBlinkDuration;
1399 float mCursorWidth; ///< The width of the cursors in pixels.
1400 HandleType mHandleScrolling; ///< The handle which is scrolling.
1401 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1402 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1403 float mScrollSpeed; ///< The scroll speed in pixels per second.
1404 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1405 int mTextDepth; ///< The depth used to render the text.
1407 bool mActiveCopyPastePopup : 1;
1408 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1409 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1410 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1411 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1412 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1413 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1414 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1415 bool mHandlePanning : 1; ///< Whether any of the handles is moving.
1416 bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
1417 bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1418 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1421 DecoratorPtr Decorator::New( ControllerInterface& controller,
1422 TextSelectionPopupCallbackInterface& callbackInterface )
1424 return DecoratorPtr( new Decorator( controller,
1425 callbackInterface ) );
1428 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1430 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1433 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1435 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1438 void Decorator::Relayout( const Vector2& size )
1440 mImpl->Relayout( size );
1443 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1445 mImpl->UpdatePositions( scrollOffset );
1450 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1452 mImpl->mActiveCursor = activeCursor;
1455 unsigned int Decorator::GetActiveCursor() const
1457 return mImpl->mActiveCursor;
1460 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1462 mImpl->mCursor[cursor].position.x = x;
1463 mImpl->mCursor[cursor].position.y = y;
1464 mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1465 mImpl->mCursor[cursor].lineHeight = lineHeight;
1468 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1470 x = mImpl->mCursor[cursor].position.x;
1471 y = mImpl->mCursor[cursor].position.y;
1472 cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1473 lineHeight = mImpl->mCursor[cursor].lineHeight;
1476 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1478 return mImpl->mCursor[cursor].position;
1481 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1483 mImpl->mCursor[cursor].color = color;
1486 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1488 return mImpl->mCursor[cursor].color;
1491 void Decorator::StartCursorBlink()
1493 if ( !mImpl->mCursorBlinkTimer )
1495 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1496 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1499 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1501 mImpl->mCursorBlinkTimer.Start();
1505 void Decorator::StopCursorBlink()
1507 if ( mImpl->mCursorBlinkTimer )
1509 mImpl->mCursorBlinkTimer.Stop();
1512 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1515 void Decorator::DelayCursorBlink()
1517 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1518 mImpl->mDelayCursorBlink = true;
1521 void Decorator::SetCursorBlinkInterval( float seconds )
1523 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1526 float Decorator::GetCursorBlinkInterval() const
1528 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1531 void Decorator::SetCursorBlinkDuration( float seconds )
1533 mImpl->mCursorBlinkDuration = seconds;
1536 float Decorator::GetCursorBlinkDuration() const
1538 return mImpl->mCursorBlinkDuration;
1541 void Decorator::SetCursorWidth( int width )
1543 mImpl->mCursorWidth = static_cast<float>( width );
1546 int Decorator::GetCursorWidth() const
1548 return static_cast<int>( mImpl->mCursorWidth );
1553 void Decorator::SetHandleActive( HandleType handleType, bool active )
1555 mImpl->mHandle[handleType].active = active;
1559 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1561 mImpl->mHandlePreviousCrossed = false;
1564 // TODO: this is a work-around.
1565 // The problem is the handle actor does not receive the touch event with the Interrupt
1566 // state when the power button is pressed and the application goes to background.
1567 mImpl->mHandle[handleType].pressed = false;
1568 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1569 ImageActor imageActor = mImpl->mHandle[handleType].actor;
1570 if( imageReleased && imageActor )
1572 imageActor.SetImage( imageReleased );
1578 bool Decorator::IsHandleActive( HandleType handleType ) const
1580 return mImpl->mHandle[handleType].active ;
1583 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1585 mImpl->SetHandleImage( handleType, handleImageType, image );
1588 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1590 return mImpl->mHandleImages[handleType][handleImageType];
1593 void Decorator::SetHandleColor( const Vector4& color )
1595 mImpl->mHandleColor = color;
1598 const Vector4& Decorator::GetHandleColor() const
1600 return mImpl->mHandleColor;
1603 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1605 // Adjust grab handle displacement
1606 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1608 handle.grabDisplacementX -= x - handle.position.x;
1609 handle.grabDisplacementY -= y - handle.position.y;
1611 handle.position.x = x;
1612 handle.position.y = y;
1613 handle.lineHeight = height;
1616 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1618 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1620 x = handle.position.x;
1621 y = handle.position.y;
1622 height = handle.lineHeight;
1625 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1627 return mImpl->mHandle[handleType].position;
1630 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1632 mImpl->mFlipSelectionHandlesOnCross = enable;
1635 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1637 mImpl->mHandleCurrentCrossed = indicesSwapped;
1638 mImpl->mFlipLeftSelectionHandleDirection = left;
1639 mImpl->mFlipRightSelectionHandleDirection = right;
1642 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1644 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1647 void Decorator::ClearHighlights()
1649 mImpl->mHighlightQuadList.clear();
1650 mImpl->mHighlightPosition = Vector2::ZERO;
1653 void Decorator::SetHighlightColor( const Vector4& color )
1655 mImpl->mHighlightColor = color;
1658 const Vector4& Decorator::GetHighlightColor() const
1660 return mImpl->mHighlightColor;
1663 void Decorator::SetTextDepth( int textDepth )
1665 mImpl->mTextDepth = textDepth;
1668 void Decorator::SetPopupActive( bool active )
1670 mImpl->mActiveCopyPastePopup = active;
1673 bool Decorator::IsPopupActive() const
1675 return mImpl->mActiveCopyPastePopup ;
1678 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1680 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1682 if ( !mImpl->mCopyPastePopup.actor )
1684 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1685 #ifdef DECORATOR_DEBUG
1686 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1688 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1689 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
1692 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1695 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1697 return mImpl->mEnabledPopupButtons;
1702 void Decorator::SetScrollThreshold( float threshold )
1704 mImpl->SetScrollThreshold( threshold );
1707 float Decorator::GetScrollThreshold() const
1709 return mImpl->GetScrollThreshold();
1712 void Decorator::SetScrollSpeed( float speed )
1714 mImpl->SetScrollSpeed( speed );
1717 float Decorator::GetScrollSpeed() const
1719 return mImpl->GetScrollSpeed();
1722 void Decorator::NotifyEndOfScroll()
1724 mImpl->NotifyEndOfScroll();
1727 Decorator::~Decorator()
1732 Decorator::Decorator( ControllerInterface& controller,
1733 TextSelectionPopupCallbackInterface& callbackInterface )
1736 mImpl = new Decorator::Impl( controller, callbackInterface );
1741 } // namespace Toolkit