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 + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f );
312 if( mPrimaryCursorVisible )
314 mPrimaryCursor.SetPosition( cursor.position.x,
316 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
318 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
320 if( mSecondaryCursor )
322 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
323 mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f );
324 if( mSecondaryCursorVisible )
326 mSecondaryCursor.SetPosition( cursor.position.x,
328 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
330 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
333 // Show or hide the grab handle
334 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
335 if( grabHandle.active )
337 const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= size.width ) && ( grabHandle.position.x >= 0.f );
343 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
344 SetGrabHandlePosition();
346 // Sets the grab handle image according if it's pressed, flipped, etc.
347 SetHandleImage( GRAB_HANDLE );
350 if( grabHandle.actor )
352 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 bool isPrimaryVisible = ( primary.position.x <= size.width ) && ( primary.position.x >= 0.f );
366 const bool isSecondaryVisible = ( secondary.position.x <= size.width ) && ( secondary.position.x >= 0.f );
368 if( isPrimaryVisible || isSecondaryVisible )
370 CreateSelectionHandles();
372 if( isPrimaryVisible )
374 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
376 // Sets the primary handle image according if it's pressed, flipped, etc.
377 SetHandleImage( LEFT_SELECTION_HANDLE );
379 SetSelectionHandleMarkerSize( primary );
382 if( isSecondaryVisible )
384 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
386 // Sets the secondary handle image according if it's pressed, flipped, etc.
387 SetHandleImage( RIGHT_SELECTION_HANDLE );
389 SetSelectionHandleMarkerSize( secondary );
395 primary.actor.SetVisible( isPrimaryVisible );
397 if( secondary.actor )
399 secondary.actor.SetVisible( isSecondaryVisible );
409 primary.actor.Unparent();
411 if( secondary.actor )
413 secondary.actor.Unparent();
415 if( mHighlightActor )
417 mHighlightActor.Unparent();
421 if( mActiveCopyPastePopup )
427 if( mCopyPastePopup.actor )
429 mCopyPastePopup.actor.HidePopup();
430 mPopupSetNewPosition = true;
435 void UpdatePositions( const Vector2& scrollOffset )
437 mCursor[PRIMARY_CURSOR].position += scrollOffset;
438 mCursor[SECONDARY_CURSOR].position += scrollOffset;
439 mHandle[ GRAB_HANDLE ].position += scrollOffset;
440 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
441 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
442 mHighlightPosition += scrollOffset;
447 if ( !mCopyPastePopup.actor )
452 if( !mCopyPastePopup.actor.GetParent() )
454 mActiveLayer.Add( mCopyPastePopup.actor );
457 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
458 mCopyPastePopup.actor.ShowPopup();
461 void DeterminePositionPopup()
463 if ( !mActiveCopyPastePopup )
468 if( mPopupSetNewPosition )
470 if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
472 float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
473 float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
475 float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
477 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
478 mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
482 mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
486 Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
488 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
490 SetUpPopupPositionNotifications();
492 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
493 mPopupSetNewPosition = false;
496 void PopupRelayoutComplete( Actor actor )
498 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
500 DeterminePositionPopup();
503 void CreateCursor( ImageActor& cursor, const Vector4& color )
505 cursor = CreateSolidColorActor( color );
506 cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
507 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
508 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
511 // Add or Remove cursor(s) from parent
514 if( mActiveCursor == ACTIVE_CURSOR_NONE )
518 mPrimaryCursor.Unparent();
520 if( mSecondaryCursor )
522 mSecondaryCursor.Unparent();
527 // Create Primary and or Secondary Cursor(s) if active and add to parent
528 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
529 mActiveCursor == ACTIVE_CURSOR_BOTH )
531 if ( !mPrimaryCursor )
533 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
534 #ifdef DECORATOR_DEBUG
535 mPrimaryCursor.SetName( "PrimaryCursorActor" );
539 if( !mPrimaryCursor.GetParent() )
541 mActiveLayer.Add( mPrimaryCursor );
545 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
547 if ( !mSecondaryCursor )
549 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
550 #ifdef DECORATOR_DEBUG
551 mSecondaryCursor.SetName( "SecondaryCursorActor" );
555 if( !mSecondaryCursor.GetParent() )
557 mActiveLayer.Add( mSecondaryCursor );
562 if( mSecondaryCursor )
564 mSecondaryCursor.Unparent();
570 bool OnCursorBlinkTimerTick()
572 if( !mDelayCursorBlink )
575 if ( mPrimaryCursor )
577 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
579 if ( mSecondaryCursor )
581 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
584 mCursorBlinkStatus = !mCursorBlinkStatus;
589 mDelayCursorBlink = false;
595 void SetupTouchEvents()
597 mTapDetector = TapGestureDetector::New();
598 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
600 mPanGestureDetector = PanGestureDetector::New();
601 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
604 void CreateActiveLayer()
608 mActiveLayer = Layer::New();
609 #ifdef DECORATOR_DEBUG
610 mActiveLayer.SetName ( "ActiveLayerActor" );
613 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
614 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
615 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
617 // Add the active layer telling the controller it doesn't need clipping.
618 mController.AddDecoration( mActiveLayer, false );
621 mActiveLayer.RaiseToTop();
624 void SetSelectionHandleMarkerSize( HandleImpl& handle )
626 if ( handle.markerActor )
628 handle.markerActor.SetSize( 0, handle.lineHeight );
632 void CreateGrabHandle()
634 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
635 if( !grabHandle.actor )
637 if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
639 SetHandleImage( GRAB_HANDLE, HANDLE_IMAGE_RELEASED, ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED ) );
642 grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
643 grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
644 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
645 // Area that Grab handle responds to, larger than actual handle so easier to move
646 #ifdef DECORATOR_DEBUG
647 grabHandle.actor.SetName( "GrabHandleActor" );
648 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
650 grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
651 grabHandle.grabArea.SetName( "GrabArea" );
655 grabHandle.grabArea = Actor::New();
656 grabHandle.grabArea.SetName( "GrabArea" );
659 grabHandle.grabArea = Actor::New();
662 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
663 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
664 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
665 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
666 grabHandle.actor.Add( grabHandle.grabArea );
667 grabHandle.actor.SetColor( mHandleColor );
669 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
670 mTapDetector.Attach( grabHandle.grabArea );
671 mPanGestureDetector.Attach( grabHandle.grabArea );
673 mActiveLayer.Add( grabHandle.actor );
676 if( !grabHandle.actor.GetParent() )
678 mActiveLayer.Add( grabHandle.actor );
682 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
686 handle.markerActor = ImageActor::New( image );
687 handle.markerActor.SetColor( mHandleColor );
688 handle.actor.Add( handle.markerActor );
690 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
692 if ( LEFT_SELECTION_HANDLE == handleType )
694 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
695 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
697 else if ( RIGHT_SELECTION_HANDLE == handleType )
699 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
700 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
705 void CreateSelectionHandles()
707 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
710 primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
711 #ifdef DECORATOR_DEBUG
712 primary.actor.SetName("SelectionHandleOne");
714 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
715 primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
716 primary.actor.SetColor( mHandleColor );
718 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
719 #ifdef DECORATOR_DEBUG
720 primary.grabArea.SetName("SelectionHandleOneGrabArea");
722 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
723 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
724 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
725 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
727 mTapDetector.Attach( primary.grabArea );
728 mPanGestureDetector.Attach( primary.grabArea );
729 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
731 primary.actor.Add( primary.grabArea );
733 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
736 if( !primary.actor.GetParent() )
738 mActiveLayer.Add( primary.actor );
741 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
742 if( !secondary.actor )
744 secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
745 #ifdef DECORATOR_DEBUG
746 secondary.actor.SetName("SelectionHandleTwo");
748 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
749 secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
750 secondary.actor.SetColor( mHandleColor );
752 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
753 #ifdef DECORATOR_DEBUG
754 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
756 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
757 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
758 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
759 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
761 mTapDetector.Attach( secondary.grabArea );
762 mPanGestureDetector.Attach( secondary.grabArea );
763 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
765 secondary.actor.Add( secondary.grabArea );
767 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
770 if( !secondary.actor.GetParent() )
772 mActiveLayer.Add( secondary.actor );
776 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
778 // Get the world position of the active layer
779 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
781 // Get the size of the UI control.
783 mController.GetTargetSize( targetSize );
785 // The grab handle position in world coords.
786 position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x;
787 position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight;
790 void SetGrabHandlePosition()
792 // Reference to the grab handle.
793 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
795 // The grab handle position in world coords.
796 Vector2 grabHandleWorldPosition;
797 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
799 // Check if the grab handle exceeds the boundaries of the decoration box.
800 // At the moment only the height is checked for the grab handle.
801 grabHandle.verticallyFlipped = ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w );
803 // The grab handle 'y' position in local coords.
804 // If the grab handle exceeds the bottom of the decoration box,
805 // set the 'y' position to the top of the line.
806 // The SetGrabHandleImage() method will change the orientation.
807 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
809 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
810 yLocalPosition ); // TODO : Fix for multiline.
813 void SetSelectionHandlePosition( HandleType type )
815 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
817 // Reference to the selection handle.
818 HandleImpl& handle = mHandle[type];
820 // Get the world coordinates of the handle position.
821 Vector2 handleWorldPosition;
822 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
824 // Whether to flip the handle.
825 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
827 // Whether to flip the handles if they are crossed.
828 bool crossFlip = false;
829 if( mFlipSelectionHandlesOnCross || !mHandlePanning )
831 crossFlip = mHandleCurrentCrossed;
834 // Does not flip if both conditions are true (double flip)
835 flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
837 // Check if the selection handle exceeds the boundaries of the decoration box.
838 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
840 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
842 // Does not flip if both conditions are true (double flip)
843 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
847 if( !handle.horizontallyFlipped )
849 // Change the anchor point to flip the image.
850 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
852 handle.horizontallyFlipped = true;
857 if( handle.horizontallyFlipped )
859 // Reset the anchor point.
860 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
862 handle.horizontallyFlipped = false;
866 // Whether to flip the handle vertically.
867 handle.verticallyFlipped = ( handleWorldPosition.y + handle.size.height > mBoundingBox.w );
869 // The primary selection handle 'y' position in local coords.
870 // If the handle exceeds the bottom of the decoration box,
871 // set the 'y' position to the top of the line.
872 // The SetHandleImage() method will change the orientation.
873 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
875 handle.actor.SetPosition( handle.position.x,
876 yLocalPosition ); // TODO : Fix for multiline.
879 void SetHandleImage( HandleType type )
881 HandleImpl& handle = mHandle[type];
883 HandleType markerType = HANDLE_TYPE_COUNT;
884 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
885 if( LEFT_SELECTION_HANDLE == type )
887 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
888 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
890 else if( RIGHT_SELECTION_HANDLE == type )
892 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
893 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
896 // Chooses between the released or pressed image. It checks whether the pressed image exists.
897 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
899 handle.actor.SetImage( mHandleImages[type][imageType] );
901 if( HANDLE_TYPE_COUNT != markerType )
903 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
904 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
907 // Whether to flip the handle vertically.
908 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
911 void CreateHighlight()
913 if( !mHighlightActor )
915 mHighlightActor = Actor::New();
917 #ifdef DECORATOR_DEBUG
918 mHighlightActor.SetName( "HighlightActor" );
920 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
921 mHighlightActor.SetSize( 1.0f, 1.0f );
922 mHighlightActor.SetColor( mHighlightColor );
923 mHighlightActor.SetColorMode( USE_OWN_COLOR );
926 // Add the highlight box telling the controller it needs clipping.
927 mController.AddDecoration( mHighlightActor, true );
930 void UpdateHighlight()
932 if ( mHighlightActor )
934 if( !mHighlightQuadList.empty() )
936 Vector< Vector2 > vertices;
937 Vector< unsigned int> indices;
940 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
941 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
943 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
945 QuadCoordinates& quad = *iter;
948 vertex.x = quad.min.x;
949 vertex.y = quad.min.y;
950 vertices.PushBack( vertex );
953 vertex.x = quad.max.x;
954 vertex.y = quad.min.y;
955 vertices.PushBack( vertex );
958 vertex.x = quad.min.x;
959 vertex.y = quad.max.y;
960 vertices.PushBack( vertex );
962 // bottom-right (v+3)
963 vertex.x = quad.max.x;
964 vertex.y = quad.max.y;
965 vertices.PushBack( vertex );
967 // triangle A (3, 1, 0)
968 indices.PushBack( v + 3 );
969 indices.PushBack( v + 1 );
970 indices.PushBack( v );
972 // triangle B (0, 2, 3)
973 indices.PushBack( v );
974 indices.PushBack( v + 2 );
975 indices.PushBack( v + 3 );
980 mQuadVertices.SetSize( vertices.Size() );
984 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
989 mQuadIndices.SetSize( indices.Size() );
993 mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
996 mQuadVertices.SetData( &vertices[ 0 ] );
997 mQuadIndices.SetData( &indices[ 0 ] );
1001 mQuadGeometry = Geometry::New();
1002 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1004 mQuadGeometry.SetIndexBuffer( mQuadIndices );
1006 if( !mHighlightRenderer )
1008 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1009 mHighlightActor.AddRenderer( mHighlightRenderer );
1013 mHighlightActor.SetPosition( mHighlightPosition.x,
1014 mHighlightPosition.y );
1016 mHighlightQuadList.clear();
1018 if( mHighlightRenderer )
1020 mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1025 void OnTap( Actor actor, const TapGesture& tap )
1027 if( actor == mHandle[GRAB_HANDLE].actor )
1033 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1035 if( Gesture::Started == gesture.state )
1037 handle.grabDisplacementX = handle.grabDisplacementY = 0;
1040 handle.grabDisplacementX += gesture.displacement.x;
1041 handle.grabDisplacementY += gesture.displacement.y;
1043 const float x = handle.position.x + handle.grabDisplacementX;
1044 const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1046 if( Gesture::Started == gesture.state ||
1047 Gesture::Continuing == gesture.state )
1050 mController.GetTargetSize( targetSize );
1052 if( x < mScrollThreshold )
1054 mScrollDirection = SCROLL_RIGHT;
1055 mHandleScrolling = type;
1058 else if( x > targetSize.width - mScrollThreshold )
1060 mScrollDirection = SCROLL_LEFT;
1061 mHandleScrolling = type;
1066 mHandleScrolling = HANDLE_TYPE_COUNT;
1068 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1071 mHandlePanning = true;
1073 else if( Gesture::Finished == gesture.state ||
1074 Gesture::Cancelled == gesture.state )
1077 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1079 mNotifyEndOfScroll = false;
1080 mHandleScrolling = HANDLE_TYPE_COUNT;
1082 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1086 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1089 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1090 handle.pressed = false;
1092 mHandlePanning = false;
1096 void OnPan( Actor actor, const PanGesture& gesture )
1098 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1099 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1100 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1102 if( actor == grabHandle.grabArea )
1104 DoPan( grabHandle, GRAB_HANDLE, gesture );
1106 else if( actor == primarySelectionHandle.grabArea )
1108 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1110 else if( actor == secondarySelectionHandle.grabArea )
1112 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1116 bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1118 // Switch between pressed/release grab-handle images
1119 if( event.GetPointCount() > 0 &&
1120 mHandle[GRAB_HANDLE].actor )
1122 const TouchPoint& point = event.GetPoint(0);
1124 if( TouchPoint::Down == point.state )
1126 mHandle[GRAB_HANDLE].pressed = true;
1128 else if( ( TouchPoint::Up == point.state ) ||
1129 ( TouchPoint::Interrupted == point.state ) )
1131 mHandle[GRAB_HANDLE].pressed = false;
1134 SetHandleImage( GRAB_HANDLE );
1137 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1141 bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1143 // Switch between pressed/release selection handle images
1144 if( event.GetPointCount() > 0 &&
1145 mHandle[LEFT_SELECTION_HANDLE].actor )
1147 const TouchPoint& point = event.GetPoint(0);
1149 if( TouchPoint::Down == point.state )
1151 mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1153 else if( ( TouchPoint::Up == point.state ) ||
1154 ( TouchPoint::Interrupted == point.state ) )
1156 mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1157 mHandlePreviousCrossed = mHandleCurrentCrossed;
1158 mHandlePanning = false;
1161 SetHandleImage( LEFT_SELECTION_HANDLE );
1164 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1168 bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1170 // Switch between pressed/release selection handle images
1171 if( event.GetPointCount() > 0 &&
1172 mHandle[RIGHT_SELECTION_HANDLE].actor )
1174 const TouchPoint& point = event.GetPoint(0);
1176 if( TouchPoint::Down == point.state )
1178 mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1180 else if( ( TouchPoint::Up == point.state ) ||
1181 ( TouchPoint::Interrupted == point.state ) )
1183 mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1184 mHandlePreviousCrossed = mHandleCurrentCrossed;
1185 mHandlePanning = false;
1188 SetHandleImage( RIGHT_SELECTION_HANDLE );
1191 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1197 float AlternatePopUpPositionRelativeToCursor()
1199 float alternativePosition=0.0f;;
1201 if ( mPrimaryCursor ) // Secondary cursor not used for paste
1203 Cursor cursor = PRIMARY_CURSOR;
1204 alternativePosition = mCursor[cursor].position.y;
1207 const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1209 if( mHandle[GRAB_HANDLE].active )
1211 // If grab handle enabled then position pop-up below the grab handle.
1212 const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1213 const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1214 alternativePosition += grabHandleSize.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1218 alternativePosition += popupHeight;
1221 return alternativePosition;
1224 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1226 float alternativeYPosition=0.0f;
1227 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1228 // if can't be positioned above, then position below row.
1229 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1231 mCopyPastePopup.actor.SetY( alternativeYPosition );
1235 void SetUpPopupPositionNotifications( )
1237 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1239 // Exceeding vertical boundary
1241 float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1243 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1244 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1245 mBoundingBox.w - popupHeight * 0.5f ) );
1247 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1250 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld )
1252 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1254 // Parent must already by added to Stage for these Get calls to work
1255 Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1256 Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1257 Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1258 Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1260 // Calculate distance to move popup (in local space) so fits within the boundary
1261 float xOffSetToKeepWithinBounds = 0.0f;
1262 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1264 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1266 else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1268 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1271 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1272 if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1274 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1277 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1279 // Prevent pixel mis-alignment by rounding down.
1280 requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1281 requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1284 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1286 HandleImpl& handle = mHandle[handleType];
1287 handle.size = Size( image.GetWidth(), image.GetHeight() );
1289 mHandleImages[handleType][handleImageType] = image;
1292 void SetScrollThreshold( float threshold )
1294 mScrollThreshold = threshold;
1297 float GetScrollThreshold() const
1299 return mScrollThreshold;
1302 void SetScrollSpeed( float speed )
1304 mScrollSpeed = speed;
1305 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1308 float GetScrollSpeed() const
1310 return mScrollSpeed;
1313 void NotifyEndOfScroll()
1319 mNotifyEndOfScroll = true;
1324 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1326 * It only starts the timer if it's already created.
1328 void StartScrollTimer()
1332 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1333 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1336 if( !mScrollTimer.IsRunning() )
1338 mScrollTimer.Start();
1343 * Stops the timer used to scroll the text.
1345 void StopScrollTimer()
1349 mScrollTimer.Stop();
1354 * Callback called by the timer used to scroll the text.
1356 * It calculates and sets a new scroll position.
1358 bool OnScrollTimerTick()
1360 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1362 mController.DecorationEvent( mHandleScrolling,
1364 mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1371 ControllerInterface& mController;
1373 TapGestureDetector mTapDetector;
1374 PanGestureDetector mPanGestureDetector;
1375 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1376 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1378 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1379 ImageActor mPrimaryCursor;
1380 ImageActor mSecondaryCursor;
1382 Actor mHighlightActor; ///< Actor to display highlight
1383 Renderer mHighlightRenderer;
1384 Material mHighlightMaterial; ///< Material used for highlight
1385 Property::Map mQuadVertexFormat;
1386 Property::Map mQuadIndexFormat;
1387 PopupImpl mCopyPastePopup;
1388 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1389 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1391 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1392 Vector4 mHandleColor;
1394 CursorImpl mCursor[CURSOR_COUNT];
1395 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1397 PropertyBuffer mQuadVertices;
1398 PropertyBuffer mQuadIndices;
1399 Geometry mQuadGeometry;
1400 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1402 Vector4 mBoundingBox; ///< The bounding box in world coords.
1403 Vector4 mHighlightColor; ///< Color of the highlight
1404 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1406 unsigned int mActiveCursor;
1407 unsigned int mCursorBlinkInterval;
1408 float mCursorBlinkDuration;
1409 float mCursorWidth; ///< The width of the cursors in pixels.
1410 HandleType mHandleScrolling; ///< The handle which is scrolling.
1411 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1412 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1413 float mScrollSpeed; ///< The scroll speed in pixels per second.
1414 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1415 int mTextDepth; ///< The depth used to render the text.
1417 bool mActiveCopyPastePopup : 1;
1418 bool mPopupSetNewPosition : 1;
1419 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1420 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1421 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1422 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1423 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1424 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1425 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1426 bool mHandlePanning : 1; ///< Whether any of the handles is moving.
1427 bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
1428 bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1429 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1432 DecoratorPtr Decorator::New( ControllerInterface& controller,
1433 TextSelectionPopupCallbackInterface& callbackInterface )
1435 return DecoratorPtr( new Decorator( controller,
1436 callbackInterface ) );
1439 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1441 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1444 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1446 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1449 void Decorator::Relayout( const Vector2& size )
1451 mImpl->Relayout( size );
1454 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1456 mImpl->UpdatePositions( scrollOffset );
1461 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1463 mImpl->mActiveCursor = activeCursor;
1466 unsigned int Decorator::GetActiveCursor() const
1468 return mImpl->mActiveCursor;
1471 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1473 mImpl->mCursor[cursor].position.x = x;
1474 mImpl->mCursor[cursor].position.y = y;
1475 mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1476 mImpl->mCursor[cursor].lineHeight = lineHeight;
1479 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1481 x = mImpl->mCursor[cursor].position.x;
1482 y = mImpl->mCursor[cursor].position.y;
1483 cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1484 lineHeight = mImpl->mCursor[cursor].lineHeight;
1487 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1489 return mImpl->mCursor[cursor].position;
1492 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1494 mImpl->mCursor[cursor].color = color;
1497 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1499 return mImpl->mCursor[cursor].color;
1502 void Decorator::StartCursorBlink()
1504 if ( !mImpl->mCursorBlinkTimer )
1506 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1507 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1510 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1512 mImpl->mCursorBlinkTimer.Start();
1516 void Decorator::StopCursorBlink()
1518 if ( mImpl->mCursorBlinkTimer )
1520 mImpl->mCursorBlinkTimer.Stop();
1523 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1526 void Decorator::DelayCursorBlink()
1528 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1529 mImpl->mDelayCursorBlink = true;
1532 void Decorator::SetCursorBlinkInterval( float seconds )
1534 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1537 float Decorator::GetCursorBlinkInterval() const
1539 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1542 void Decorator::SetCursorBlinkDuration( float seconds )
1544 mImpl->mCursorBlinkDuration = seconds;
1547 float Decorator::GetCursorBlinkDuration() const
1549 return mImpl->mCursorBlinkDuration;
1552 void Decorator::SetCursorWidth( int width )
1554 mImpl->mCursorWidth = static_cast<float>( width );
1557 int Decorator::GetCursorWidth() const
1559 return static_cast<int>( mImpl->mCursorWidth );
1564 void Decorator::SetHandleActive( HandleType handleType, bool active )
1566 mImpl->mHandle[handleType].active = active;
1570 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1572 mImpl->mHandlePreviousCrossed = false;
1575 // TODO: this is a work-around.
1576 // The problem is the handle actor does not receive the touch event with the Interrupt
1577 // state when the power button is pressed and the application goes to background.
1578 mImpl->mHandle[handleType].pressed = false;
1579 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1580 ImageActor imageActor = mImpl->mHandle[handleType].actor;
1581 if( imageReleased && imageActor )
1583 imageActor.SetImage( imageReleased );
1589 bool Decorator::IsHandleActive( HandleType handleType ) const
1591 return mImpl->mHandle[handleType].active ;
1594 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1596 mImpl->SetHandleImage( handleType, handleImageType, image );
1599 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1601 return mImpl->mHandleImages[handleType][handleImageType];
1604 void Decorator::SetHandleColor( const Vector4& color )
1606 mImpl->mHandleColor = color;
1609 const Vector4& Decorator::GetHandleColor() const
1611 return mImpl->mHandleColor;
1614 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1616 // Adjust grab handle displacement
1617 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1619 handle.grabDisplacementX -= x - handle.position.x;
1620 handle.grabDisplacementY -= y - handle.position.y;
1622 handle.position.x = x;
1623 handle.position.y = y;
1624 handle.lineHeight = height;
1627 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1629 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1631 x = handle.position.x;
1632 y = handle.position.y;
1633 height = handle.lineHeight;
1636 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1638 return mImpl->mHandle[handleType].position;
1641 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1643 mImpl->mFlipSelectionHandlesOnCross = enable;
1646 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1648 mImpl->mHandleCurrentCrossed = indicesSwapped;
1649 mImpl->mFlipLeftSelectionHandleDirection = left;
1650 mImpl->mFlipRightSelectionHandleDirection = right;
1653 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1655 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1658 void Decorator::ClearHighlights()
1660 mImpl->mHighlightQuadList.clear();
1661 mImpl->mHighlightPosition = Vector2::ZERO;
1664 void Decorator::SetHighlightColor( const Vector4& color )
1666 mImpl->mHighlightColor = color;
1669 const Vector4& Decorator::GetHighlightColor() const
1671 return mImpl->mHighlightColor;
1674 void Decorator::SetTextDepth( int textDepth )
1676 mImpl->mTextDepth = textDepth;
1679 void Decorator::SetPopupActive( bool active )
1681 mImpl->mActiveCopyPastePopup = active;
1684 bool Decorator::IsPopupActive() const
1686 return mImpl->mActiveCopyPastePopup ;
1689 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1691 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1693 if ( !mImpl->mCopyPastePopup.actor )
1695 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1696 #ifdef DECORATOR_DEBUG
1697 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1699 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1700 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
1703 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1706 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1708 return mImpl->mEnabledPopupButtons;
1713 void Decorator::SetScrollThreshold( float threshold )
1715 mImpl->SetScrollThreshold( threshold );
1718 float Decorator::GetScrollThreshold() const
1720 return mImpl->GetScrollThreshold();
1723 void Decorator::SetScrollSpeed( float speed )
1725 mImpl->SetScrollSpeed( speed );
1728 float Decorator::GetScrollSpeed() const
1730 return mImpl->GetScrollSpeed();
1733 void Decorator::NotifyEndOfScroll()
1735 mImpl->NotifyEndOfScroll();
1738 Decorator::~Decorator()
1743 Decorator::Decorator( ControllerInterface& controller,
1744 TextSelectionPopupCallbackInterface& callbackInterface )
1747 mImpl = new Decorator::Impl( controller, callbackInterface );
1752 } // namespace Toolkit