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") );
88 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
89 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
91 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.
93 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
95 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
96 const float TO_MILLISECONDS = 1000.f;
97 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
99 const unsigned int SCROLL_TICK_INTERVAL = 50u;
101 const float SCROLL_THRESHOLD = 10.f;
102 const float SCROLL_SPEED = 300.f;
103 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
105 const float CURSOR_WIDTH = 1.f;
108 * structure to hold coordinates of each quad, which will make up the mesh.
110 struct QuadCoordinates
113 * Default constructor
121 * @param[in] x1 left co-ordinate
122 * @param[in] y1 top co-ordinate
123 * @param[in] x2 right co-ordinate
124 * @param[in] y2 bottom co-ordinate
126 QuadCoordinates(float x1, float y1, float x2, float y2)
132 Dali::Vector2 min; ///< top-left (minimum) position of quad
133 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
136 typedef std::vector<QuadCoordinates> QuadContainer;
139 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
140 * @param[in] boundingRectangle local bounding
141 * @param[out] Vector4 World coordinate bounding Box.
143 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
145 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
146 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
148 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
149 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
151 boundingBox = Dali::Vector4( originX,
153 originX + boundingRectangle.width,
154 originY + boundingRectangle.height );
157 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
159 // Convert to local coordinates and store as a Dali::Rect.
160 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
162 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
163 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
164 boundingRectangle.width = boundingBox.z - boundingBox.x;
165 boundingRectangle.height = boundingBox.w - boundingBox.y;
168 } // end of namespace
179 struct Decorator::Impl : public ConnectionTracker
193 : color( Dali::Color::BLACK ),
195 cursorHeight( 0.0f ),
212 grabDisplacementX( 0.f ),
213 grabDisplacementY( 0.f ),
217 verticallyFlippedPreferred( false ),
218 horizontallyFlipped( false ),
219 verticallyFlipped( false )
225 ImageActor markerActor;
229 float lineHeight; ///< Not the handle height
230 float grabDisplacementX;
231 float grabDisplacementY;
235 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
236 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
237 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
247 TextSelectionPopup actor;
251 Impl( ControllerInterface& controller,
252 TextSelectionPopupCallbackInterface& callbackInterface )
253 : mController( controller ),
254 mEnabledPopupButtons( TextSelectionPopup::NONE ),
255 mTextSelectionPopupCallbackInterface( callbackInterface ),
256 mHandleColor( HANDLE_COLOR ),
258 mHighlightColor( LIGHT_BLUE ),
259 mHighlightPosition( Vector2::ZERO ),
260 mActiveCursor( ACTIVE_CURSOR_NONE ),
261 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
262 mCursorBlinkDuration( 0.0f ),
263 mCursorWidth( CURSOR_WIDTH ),
264 mHandleScrolling( HANDLE_TYPE_COUNT ),
265 mScrollDirection( SCROLL_NONE ),
266 mScrollThreshold( SCROLL_THRESHOLD ),
267 mScrollSpeed( SCROLL_SPEED ),
268 mScrollDistance( SCROLL_DISTANCE ),
270 mActiveCopyPastePopup( false ),
271 mPopupSetNewPosition( true ),
272 mCursorBlinkStatus( true ),
273 mDelayCursorBlink( false ),
274 mPrimaryCursorVisible( false ),
275 mSecondaryCursorVisible( false ),
276 mFlipSelectionHandlesOnCross( false ),
277 mFlipLeftSelectionHandleDirection( false ),
278 mFlipRightSelectionHandleDirection( false ),
279 mHandlePanning( false ),
280 mHandleCurrentCrossed( false ),
281 mHandlePreviousCrossed( false ),
282 mNotifyEndOfScroll( false )
284 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
285 mQuadIndexFormat[ "indices" ] = Property::INTEGER;
286 mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
292 * Relayout of the decorations owned by the decorator.
293 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
295 void Relayout( const Vector2& size )
299 // TODO - Remove this if nothing is active
302 // Show or hide the cursors
307 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
308 mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
309 if( mPrimaryCursorVisible )
311 mPrimaryCursor.SetPosition( cursor.position.x,
313 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
315 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
317 if( mSecondaryCursor )
319 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
320 mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
321 if( mSecondaryCursorVisible )
323 mSecondaryCursor.SetPosition( cursor.position.x,
325 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
327 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
330 // Show or hide the grab handle
331 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
332 bool newGrabHandlePosition = false;
333 if( grabHandle.active )
335 const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) && ( grabHandle.position.x >= 0.f );
341 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
342 SetGrabHandlePosition();
344 // Sets the grab handle image according if it's pressed, flipped, etc.
345 SetHandleImage( GRAB_HANDLE );
347 newGrabHandlePosition = true;
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 bool newPrimaryHandlePosition = false;
364 bool newSecondaryHandlePosition = false;
365 if( primary.active || secondary.active )
367 const bool isPrimaryVisible = ( primary.position.x <= mControlSize.width ) && ( primary.position.x >= 0.f );
368 const bool isSecondaryVisible = ( secondary.position.x <= mControlSize.width ) && ( secondary.position.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 );
383 newPrimaryHandlePosition = true;
386 if( isSecondaryVisible )
388 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
390 // Sets the secondary handle image according if it's pressed, flipped, etc.
391 SetHandleImage( RIGHT_SELECTION_HANDLE );
393 SetSelectionHandleMarkerSize( secondary );
395 newSecondaryHandlePosition = true;
401 primary.actor.SetVisible( isPrimaryVisible );
403 if( secondary.actor )
405 secondary.actor.SetVisible( isSecondaryVisible );
415 primary.actor.Unparent();
417 if( secondary.actor )
419 secondary.actor.Unparent();
421 if( mHighlightActor )
423 mHighlightActor.Unparent();
427 if( newGrabHandlePosition ||
428 newPrimaryHandlePosition ||
429 newSecondaryHandlePosition )
431 // Setup property notifications to find whether the handles leave the boundaries of the current display.
432 SetupActiveLayerPropertyNotifications();
435 if( mActiveCopyPastePopup )
438 mPopupSetNewPosition = true;
442 if( mCopyPastePopup.actor )
444 mCopyPastePopup.actor.HidePopup();
445 mPopupSetNewPosition = true;
450 void UpdatePositions( const Vector2& scrollOffset )
452 mCursor[PRIMARY_CURSOR].position += scrollOffset;
453 mCursor[SECONDARY_CURSOR].position += scrollOffset;
454 mHandle[ GRAB_HANDLE ].position += scrollOffset;
455 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
456 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
457 mHighlightPosition += scrollOffset;
462 if ( !mCopyPastePopup.actor )
467 if( !mCopyPastePopup.actor.GetParent() )
469 mActiveLayer.Add( mCopyPastePopup.actor );
472 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
473 mCopyPastePopup.actor.ShowPopup();
476 void DeterminePositionPopup()
478 if( !mActiveCopyPastePopup )
483 // Retrieves the popup's size after relayout.
484 const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
486 if( mPopupSetNewPosition )
488 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
489 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
490 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
491 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
493 if( primaryHandle.active || secondaryHandle.active )
495 // Calculates the popup's position if selection handles are active.
496 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
497 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
498 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
500 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
501 mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
505 // Calculates the popup's position if the grab handle is active.
506 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
510 // Checks if there is enough space above the text control. If not it places the popup under it.
511 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
513 SetUpPopupPositionNotifications();
515 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
516 mPopupSetNewPosition = false;
519 void PopupRelayoutComplete( Actor actor )
521 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
523 DeterminePositionPopup();
526 void CreateCursor( ImageActor& cursor, const Vector4& color )
528 cursor = CreateSolidColorActor( color );
529 cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
530 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
531 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
534 // Add or Remove cursor(s) from parent
537 if( mActiveCursor == ACTIVE_CURSOR_NONE )
541 mPrimaryCursor.Unparent();
543 if( mSecondaryCursor )
545 mSecondaryCursor.Unparent();
550 // Create Primary and or Secondary Cursor(s) if active and add to parent
551 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
552 mActiveCursor == ACTIVE_CURSOR_BOTH )
554 if ( !mPrimaryCursor )
556 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
557 #ifdef DECORATOR_DEBUG
558 mPrimaryCursor.SetName( "PrimaryCursorActor" );
562 if( !mPrimaryCursor.GetParent() )
564 mActiveLayer.Add( mPrimaryCursor );
568 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
570 if ( !mSecondaryCursor )
572 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
573 #ifdef DECORATOR_DEBUG
574 mSecondaryCursor.SetName( "SecondaryCursorActor" );
578 if( !mSecondaryCursor.GetParent() )
580 mActiveLayer.Add( mSecondaryCursor );
585 if( mSecondaryCursor )
587 mSecondaryCursor.Unparent();
593 bool OnCursorBlinkTimerTick()
595 if( !mDelayCursorBlink )
598 if ( mPrimaryCursor )
600 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
602 if ( mSecondaryCursor )
604 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
607 mCursorBlinkStatus = !mCursorBlinkStatus;
612 mDelayCursorBlink = false;
618 void SetupTouchEvents()
620 mTapDetector = TapGestureDetector::New();
621 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
623 mPanGestureDetector = PanGestureDetector::New();
624 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
627 void CreateActiveLayer()
631 mActiveLayer = Layer::New();
632 #ifdef DECORATOR_DEBUG
633 mActiveLayer.SetName ( "ActiveLayerActor" );
636 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
637 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
638 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
640 // Add the active layer telling the controller it doesn't need clipping.
641 mController.AddDecoration( mActiveLayer, false );
644 mActiveLayer.RaiseToTop();
647 void SetSelectionHandleMarkerSize( HandleImpl& handle )
649 if( handle.markerActor )
651 handle.markerActor.SetSize( 0, handle.lineHeight );
655 void CreateGrabHandle()
657 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
658 if( !grabHandle.actor )
660 grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
661 grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
662 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
663 // Area that Grab handle responds to, larger than actual handle so easier to move
664 #ifdef DECORATOR_DEBUG
665 grabHandle.actor.SetName( "GrabHandleActor" );
666 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
668 grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
669 grabHandle.grabArea.SetName( "GrabArea" );
673 grabHandle.grabArea = Actor::New();
674 grabHandle.grabArea.SetName( "GrabArea" );
677 grabHandle.grabArea = Actor::New();
680 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
681 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
682 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
683 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
684 grabHandle.actor.Add( grabHandle.grabArea );
685 grabHandle.actor.SetColor( mHandleColor );
687 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
688 mTapDetector.Attach( grabHandle.grabArea );
689 mPanGestureDetector.Attach( grabHandle.grabArea );
691 mActiveLayer.Add( grabHandle.actor );
694 if( !grabHandle.actor.GetParent() )
696 mActiveLayer.Add( grabHandle.actor );
700 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
704 handle.markerActor = ImageActor::New( image );
705 handle.markerActor.SetColor( mHandleColor );
706 handle.actor.Add( handle.markerActor );
708 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
710 if( LEFT_SELECTION_HANDLE == handleType )
712 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
713 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
715 else if( RIGHT_SELECTION_HANDLE == handleType )
717 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
718 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
723 void CreateSelectionHandles()
725 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
728 primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
729 #ifdef DECORATOR_DEBUG
730 primary.actor.SetName("SelectionHandleOne");
732 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
733 primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
734 primary.actor.SetColor( mHandleColor );
736 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
737 #ifdef DECORATOR_DEBUG
738 primary.grabArea.SetName("SelectionHandleOneGrabArea");
740 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
741 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
742 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
743 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
745 mTapDetector.Attach( primary.grabArea );
746 mPanGestureDetector.Attach( primary.grabArea );
747 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
749 primary.actor.Add( primary.grabArea );
751 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
754 if( !primary.actor.GetParent() )
756 mActiveLayer.Add( primary.actor );
759 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
760 if( !secondary.actor )
762 secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
763 #ifdef DECORATOR_DEBUG
764 secondary.actor.SetName("SelectionHandleTwo");
766 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
767 secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
768 secondary.actor.SetColor( mHandleColor );
770 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
771 #ifdef DECORATOR_DEBUG
772 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
774 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
775 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
776 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
777 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
779 mTapDetector.Attach( secondary.grabArea );
780 mPanGestureDetector.Attach( secondary.grabArea );
781 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
783 secondary.actor.Add( secondary.grabArea );
785 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
788 if( !secondary.actor.GetParent() )
790 mActiveLayer.Add( secondary.actor );
794 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
796 // Gets the world position of the active layer. The active layer is where the handles are added.
797 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
799 // The grab handle position in world coords.
800 // The active layer's world position is the center of the active layer. The origin of the
801 // coord system of the handles is the top left of the active layer.
802 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
803 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
806 void SetGrabHandlePosition()
808 // Reference to the grab handle.
809 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
811 // Transforms the handle position into world coordinates.
812 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
813 // as it's transforming the handle's position set by the text-controller and not
814 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
815 // retrieves the position of the center of the actor but the handle's position set
816 // by the text controller is not the center of the actor.
817 Vector2 grabHandleWorldPosition;
818 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
820 // Check if the grab handle exceeds the boundaries of the decoration box.
821 // At the moment only the height is checked for the grab handle.
823 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
824 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
825 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
827 // The grab handle 'y' position in local coords.
828 // If the grab handle exceeds the bottom of the decoration box,
829 // set the 'y' position to the top of the line.
830 // The SetGrabHandleImage() method will change the orientation.
831 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
833 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
834 yLocalPosition ); // TODO : Fix for multiline.
837 void SetSelectionHandlePosition( HandleType type )
839 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
841 // Reference to the selection handle.
842 HandleImpl& handle = mHandle[type];
844 // Transforms the handle position into world coordinates.
845 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
846 // as it's transforming the handle's position set by the text-controller and not
847 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
848 // retrieves the position of the center of the actor but the handle's position set
849 // by the text controller is not the center of the actor.
850 Vector2 handleWorldPosition;
851 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
853 // Whether to flip the handle (horizontally).
854 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
856 // Whether to flip the handles if they are crossed.
857 bool crossFlip = false;
858 if( mFlipSelectionHandlesOnCross || !mHandlePanning )
860 crossFlip = mHandleCurrentCrossed;
863 // Does not flip if both conditions are true (double flip)
864 flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
866 // Will flip the handles vertically if the user prefers it.
867 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
869 if( crossFlip || mHandlePreviousCrossed )
871 if( isPrimaryHandle )
873 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
877 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
881 // Check if the selection handle exceeds the boundaries of the decoration box.
882 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
883 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
885 // Does not flip if both conditions are true (double flip)
886 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
890 if( !handle.horizontallyFlipped )
892 // Change the anchor point to flip the image.
893 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
895 handle.horizontallyFlipped = true;
900 if( handle.horizontallyFlipped )
902 // Reset the anchor point.
903 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
905 handle.horizontallyFlipped = false;
909 // Whether to flip the handle vertically.
910 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
911 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
912 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
914 // The primary selection handle 'y' position in local coords.
915 // If the handle exceeds the bottom of the decoration box,
916 // set the 'y' position to the top of the line.
917 // The SetHandleImage() method will change the orientation.
918 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
920 handle.actor.SetPosition( handle.position.x,
921 yLocalPosition ); // TODO : Fix for multiline.
924 void SetHandleImage( HandleType type )
926 HandleImpl& handle = mHandle[type];
928 HandleType markerType = HANDLE_TYPE_COUNT;
929 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
930 if( LEFT_SELECTION_HANDLE == type )
932 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
933 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
935 else if( RIGHT_SELECTION_HANDLE == type )
937 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
938 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
941 // Chooses between the released or pressed image. It checks whether the pressed image exists.
944 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
946 handle.actor.SetImage( mHandleImages[type][imageType] );
949 if( HANDLE_TYPE_COUNT != markerType )
951 if( handle.markerActor )
953 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
954 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
958 // Whether to flip the handle vertically.
961 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
965 void CreateHighlight()
967 if( !mHighlightActor )
969 mHighlightActor = Actor::New();
971 #ifdef DECORATOR_DEBUG
972 mHighlightActor.SetName( "HighlightActor" );
974 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
975 mHighlightActor.SetSize( 1.0f, 1.0f );
976 mHighlightActor.SetColor( mHighlightColor );
977 mHighlightActor.SetColorMode( USE_OWN_COLOR );
980 // Add the highlight box telling the controller it needs clipping.
981 mController.AddDecoration( mHighlightActor, true );
984 void UpdateHighlight()
986 if ( mHighlightActor )
988 if( !mHighlightQuadList.empty() )
990 Vector< Vector2 > vertices;
991 Vector< unsigned int> indices;
994 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
995 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
997 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
999 QuadCoordinates& quad = *iter;
1002 vertex.x = quad.min.x;
1003 vertex.y = quad.min.y;
1004 vertices.PushBack( vertex );
1007 vertex.x = quad.max.x;
1008 vertex.y = quad.min.y;
1009 vertices.PushBack( vertex );
1011 // bottom-left (v+2)
1012 vertex.x = quad.min.x;
1013 vertex.y = quad.max.y;
1014 vertices.PushBack( vertex );
1016 // bottom-right (v+3)
1017 vertex.x = quad.max.x;
1018 vertex.y = quad.max.y;
1019 vertices.PushBack( vertex );
1021 // triangle A (3, 1, 0)
1022 indices.PushBack( v + 3 );
1023 indices.PushBack( v + 1 );
1024 indices.PushBack( v );
1026 // triangle B (0, 2, 3)
1027 indices.PushBack( v );
1028 indices.PushBack( v + 2 );
1029 indices.PushBack( v + 3 );
1034 mQuadVertices.SetSize( vertices.Size() );
1038 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
1043 mQuadIndices.SetSize( indices.Size() );
1047 mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
1050 mQuadVertices.SetData( &vertices[ 0 ] );
1051 mQuadIndices.SetData( &indices[ 0 ] );
1053 if( !mQuadGeometry )
1055 mQuadGeometry = Geometry::New();
1056 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1058 mQuadGeometry.SetIndexBuffer( mQuadIndices );
1060 if( !mHighlightRenderer )
1062 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1063 mHighlightActor.AddRenderer( mHighlightRenderer );
1067 mHighlightActor.SetPosition( mHighlightPosition.x,
1068 mHighlightPosition.y );
1070 mHighlightQuadList.clear();
1072 if( mHighlightRenderer )
1074 mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1079 void OnTap( Actor actor, const TapGesture& tap )
1081 if( actor == mHandle[GRAB_HANDLE].actor )
1087 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1089 if( Gesture::Started == gesture.state )
1091 handle.grabDisplacementX = handle.grabDisplacementY = 0;
1094 handle.grabDisplacementX += gesture.displacement.x;
1095 handle.grabDisplacementY += gesture.displacement.y;
1097 const float x = handle.position.x + handle.grabDisplacementX;
1098 const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1100 if( Gesture::Started == gesture.state ||
1101 Gesture::Continuing == gesture.state )
1104 mController.GetTargetSize( targetSize );
1106 if( x < mScrollThreshold )
1108 mScrollDirection = SCROLL_RIGHT;
1109 mHandleScrolling = type;
1112 else if( x > targetSize.width - mScrollThreshold )
1114 mScrollDirection = SCROLL_LEFT;
1115 mHandleScrolling = type;
1120 mHandleScrolling = HANDLE_TYPE_COUNT;
1122 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1125 mHandlePanning = true;
1127 else if( Gesture::Finished == gesture.state ||
1128 Gesture::Cancelled == gesture.state )
1131 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1133 mNotifyEndOfScroll = false;
1134 mHandleScrolling = HANDLE_TYPE_COUNT;
1136 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1140 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1143 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1144 handle.pressed = false;
1146 mHandlePanning = false;
1150 void OnPan( Actor actor, const PanGesture& gesture )
1152 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1153 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1154 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1156 if( actor == grabHandle.grabArea )
1158 DoPan( grabHandle, GRAB_HANDLE, gesture );
1160 else if( actor == primarySelectionHandle.grabArea )
1162 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1164 else if( actor == secondarySelectionHandle.grabArea )
1166 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1170 bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1172 // Switch between pressed/release grab-handle images
1173 if( event.GetPointCount() > 0 &&
1174 mHandle[GRAB_HANDLE].actor )
1176 const TouchPoint& point = event.GetPoint(0);
1178 if( TouchPoint::Down == point.state )
1180 mHandle[GRAB_HANDLE].pressed = true;
1182 else if( ( TouchPoint::Up == point.state ) ||
1183 ( TouchPoint::Interrupted == point.state ) )
1185 mHandle[GRAB_HANDLE].pressed = false;
1188 SetHandleImage( GRAB_HANDLE );
1191 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1195 bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1197 // Switch between pressed/release selection handle images
1198 if( event.GetPointCount() > 0 &&
1199 mHandle[LEFT_SELECTION_HANDLE].actor )
1201 const TouchPoint& point = event.GetPoint(0);
1203 if( TouchPoint::Down == point.state )
1205 mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1207 else if( ( TouchPoint::Up == point.state ) ||
1208 ( TouchPoint::Interrupted == point.state ) )
1210 mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1211 mHandlePreviousCrossed = mHandleCurrentCrossed;
1212 mHandlePanning = false;
1215 SetHandleImage( LEFT_SELECTION_HANDLE );
1218 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1222 bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1224 // Switch between pressed/release selection handle images
1225 if( event.GetPointCount() > 0 &&
1226 mHandle[RIGHT_SELECTION_HANDLE].actor )
1228 const TouchPoint& point = event.GetPoint(0);
1230 if( TouchPoint::Down == point.state )
1232 mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1234 else if( ( TouchPoint::Up == point.state ) ||
1235 ( TouchPoint::Interrupted == point.state ) )
1237 mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1238 mHandlePreviousCrossed = mHandleCurrentCrossed;
1239 mHandlePanning = false;
1242 SetHandleImage( RIGHT_SELECTION_HANDLE );
1245 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1249 void HandleResetPosition( PropertyNotification& source )
1251 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1253 if( grabHandle.active )
1255 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1256 SetGrabHandlePosition();
1258 // Sets the grab handle image according if it's pressed, flipped, etc.
1259 SetHandleImage( GRAB_HANDLE );
1263 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1264 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1266 // Sets the primary handle image according if it's pressed, flipped, etc.
1267 SetHandleImage( LEFT_SELECTION_HANDLE );
1269 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1270 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1272 // Sets the secondary handle image according if it's pressed, flipped, etc.
1273 SetHandleImage( RIGHT_SELECTION_HANDLE );
1277 void SetupActiveLayerPropertyNotifications()
1284 // Vertical notifications.
1286 // Disconnect any previous connected callback.
1287 if( mVerticalLessThanNotification )
1289 mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1290 mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1293 if( mVerticalGreaterThanNotification )
1295 mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1296 mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1299 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1300 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1301 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1303 if( grabHandle.active )
1305 if( grabHandle.verticallyFlipped )
1307 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1308 mVerticalGreaterThanNotification.Reset();
1310 // The vertical distance from the center of the active layer to the top edje of the display.
1311 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1313 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1314 LessThanCondition( mBoundingBox.y + topHeight ) );
1316 // Notifies the change from false to true and from true to false.
1317 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1319 // Connects the signals with the callbacks.
1320 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1324 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1325 mVerticalLessThanNotification.Reset();
1327 // The vertical distance from the center of the active layer to the bottom edje of the display.
1328 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1330 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1331 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1333 // Notifies the change from false to true and from true to false.
1334 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1336 // Connects the signals with the callbacks.
1337 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1340 else // The selection handles are active
1342 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1344 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1345 mVerticalGreaterThanNotification.Reset();
1347 // The vertical distance from the center of the active layer to the top edje of the display.
1348 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1350 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1351 LessThanCondition( mBoundingBox.y + topHeight ) );
1353 // Notifies the change from false to true and from true to false.
1354 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1356 // Connects the signals with the callbacks.
1357 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1359 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1361 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1362 mVerticalLessThanNotification.Reset();
1364 // The vertical distance from the center of the active layer to the bottom edje of the display.
1365 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1366 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1368 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1369 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1371 // Notifies the change from false to true and from true to false.
1372 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1374 // Connects the signals with the callbacks.
1375 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1379 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1381 // The vertical distance from the center of the active layer to the top edje of the display.
1382 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1383 -primaryHandle.position.y + primaryHandle.size.height :
1384 -secondaryHandle.position.y + secondaryHandle.size.height );
1386 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1387 LessThanCondition( mBoundingBox.y + topHeight ) );
1389 // Notifies the change from false to true and from true to false.
1390 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1392 // Connects the signals with the callbacks.
1393 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1395 // The vertical distance from the center of the active layer to the bottom edje of the display.
1396 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1397 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1398 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1400 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1401 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1403 // Notifies the change from false to true and from true to false.
1404 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1406 // Connects the signals with the callbacks.
1407 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1411 // Horizontal notifications.
1413 // Disconnect any previous connected callback.
1414 if( mHorizontalLessThanNotification )
1416 mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1417 mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1420 if( mHorizontalGreaterThanNotification )
1422 mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1423 mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1426 if( primaryHandle.active || secondaryHandle.active )
1428 // The horizontal distance from the center of the active layer to the left edje of the display.
1429 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1430 -secondaryHandle.position.x + secondaryHandle.size.width );
1432 mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1433 LessThanCondition( mBoundingBox.x + leftWidth ) );
1435 // Notifies the change from false to true and from true to false.
1436 mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1438 // Connects the signals with the callbacks.
1439 mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1441 // The horizontal distance from the center of the active layer to the right edje of the display.
1442 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1443 secondaryHandle.position.x + secondaryHandle.size.width );
1445 mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1446 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1448 // Notifies the change from false to true and from true to false.
1449 mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1451 // Connects the signals with the callbacks.
1452 mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1458 float AlternatePopUpPositionRelativeToCursor()
1460 float alternativePosition = 0.0f;
1462 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1464 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1465 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1466 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1467 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1469 if( primaryHandle.active || secondaryHandle.active )
1471 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1472 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1476 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1479 return alternativePosition;
1482 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1484 float alternativeYPosition = 0.0f;
1485 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1486 // if can't be positioned above, then position below row.
1487 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1489 mCopyPastePopup.actor.SetY( alternativeYPosition );
1492 void SetUpPopupPositionNotifications()
1494 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1496 // Exceeding vertical boundary
1498 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1500 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1501 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1502 mBoundingBox.w - popupHeight * 0.5f ) );
1504 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1507 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1509 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1511 // Parent must already by added to Stage for these Get calls to work
1512 const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1513 const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1515 // Calculate distance to move popup (in local space) so fits within the boundary
1516 float xOffSetToKeepWithinBounds = 0.0f;
1517 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1519 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1521 else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1523 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1526 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1527 if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1529 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1532 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1534 // Prevent pixel mis-alignment by rounding down.
1535 requiredPopupPosition.x = floor( requiredPopupPosition.x );
1536 requiredPopupPosition.y = floor( requiredPopupPosition.y );
1539 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1541 HandleImpl& handle = mHandle[handleType];
1542 handle.size = Size( image.GetWidth(), image.GetHeight() );
1544 mHandleImages[handleType][handleImageType] = image;
1547 void SetScrollThreshold( float threshold )
1549 mScrollThreshold = threshold;
1552 float GetScrollThreshold() const
1554 return mScrollThreshold;
1557 void SetScrollSpeed( float speed )
1559 mScrollSpeed = speed;
1560 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1563 float GetScrollSpeed() const
1565 return mScrollSpeed;
1568 void NotifyEndOfScroll()
1574 mNotifyEndOfScroll = true;
1579 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1581 * It only starts the timer if it's already created.
1583 void StartScrollTimer()
1587 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1588 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1591 if( !mScrollTimer.IsRunning() )
1593 mScrollTimer.Start();
1598 * Stops the timer used to scroll the text.
1600 void StopScrollTimer()
1604 mScrollTimer.Stop();
1609 * Callback called by the timer used to scroll the text.
1611 * It calculates and sets a new scroll position.
1613 bool OnScrollTimerTick()
1615 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1617 mController.DecorationEvent( mHandleScrolling,
1619 mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1626 ControllerInterface& mController;
1628 TapGestureDetector mTapDetector;
1629 PanGestureDetector mPanGestureDetector;
1630 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1631 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1633 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1634 PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1635 PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1636 PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1637 PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1638 ImageActor mPrimaryCursor;
1639 ImageActor mSecondaryCursor;
1641 Actor mHighlightActor; ///< Actor to display highlight
1642 Renderer mHighlightRenderer;
1643 Material mHighlightMaterial; ///< Material used for highlight
1644 Property::Map mQuadVertexFormat;
1645 Property::Map mQuadIndexFormat;
1646 PopupImpl mCopyPastePopup;
1647 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1648 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1650 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1651 Vector4 mHandleColor;
1653 CursorImpl mCursor[CURSOR_COUNT];
1654 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1656 PropertyBuffer mQuadVertices;
1657 PropertyBuffer mQuadIndices;
1658 Geometry mQuadGeometry;
1659 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1661 Vector4 mBoundingBox; ///< The bounding box in world coords.
1662 Vector4 mHighlightColor; ///< Color of the highlight
1663 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1664 Vector2 mControlSize; ///< The control's size. Set by the Relayout.
1666 unsigned int mActiveCursor;
1667 unsigned int mCursorBlinkInterval;
1668 float mCursorBlinkDuration;
1669 float mCursorWidth; ///< The width of the cursors in pixels.
1670 HandleType mHandleScrolling; ///< The handle which is scrolling.
1671 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1672 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1673 float mScrollSpeed; ///< The scroll speed in pixels per second.
1674 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1675 int mTextDepth; ///< The depth used to render the text.
1677 bool mActiveCopyPastePopup : 1;
1678 bool mPopupSetNewPosition : 1;
1679 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1680 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1681 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1682 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1683 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1684 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1685 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1686 bool mHandlePanning : 1; ///< Whether any of the handles is moving.
1687 bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
1688 bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1689 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1692 DecoratorPtr Decorator::New( ControllerInterface& controller,
1693 TextSelectionPopupCallbackInterface& callbackInterface )
1695 return DecoratorPtr( new Decorator( controller,
1696 callbackInterface ) );
1699 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1701 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1704 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1706 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1709 void Decorator::Relayout( const Vector2& size )
1711 mImpl->Relayout( size );
1714 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1716 mImpl->UpdatePositions( scrollOffset );
1721 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1723 mImpl->mActiveCursor = activeCursor;
1726 unsigned int Decorator::GetActiveCursor() const
1728 return mImpl->mActiveCursor;
1731 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1733 mImpl->mCursor[cursor].position.x = x;
1734 mImpl->mCursor[cursor].position.y = y;
1735 mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1736 mImpl->mCursor[cursor].lineHeight = lineHeight;
1739 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1741 x = mImpl->mCursor[cursor].position.x;
1742 y = mImpl->mCursor[cursor].position.y;
1743 cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1744 lineHeight = mImpl->mCursor[cursor].lineHeight;
1747 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1749 return mImpl->mCursor[cursor].position;
1752 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1754 mImpl->mCursor[cursor].color = color;
1757 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1759 return mImpl->mCursor[cursor].color;
1762 void Decorator::StartCursorBlink()
1764 if ( !mImpl->mCursorBlinkTimer )
1766 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1767 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1770 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1772 mImpl->mCursorBlinkTimer.Start();
1776 void Decorator::StopCursorBlink()
1778 if ( mImpl->mCursorBlinkTimer )
1780 mImpl->mCursorBlinkTimer.Stop();
1783 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1786 void Decorator::DelayCursorBlink()
1788 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1789 mImpl->mDelayCursorBlink = true;
1792 void Decorator::SetCursorBlinkInterval( float seconds )
1794 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1797 float Decorator::GetCursorBlinkInterval() const
1799 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1802 void Decorator::SetCursorBlinkDuration( float seconds )
1804 mImpl->mCursorBlinkDuration = seconds;
1807 float Decorator::GetCursorBlinkDuration() const
1809 return mImpl->mCursorBlinkDuration;
1812 void Decorator::SetCursorWidth( int width )
1814 mImpl->mCursorWidth = static_cast<float>( width );
1817 int Decorator::GetCursorWidth() const
1819 return static_cast<int>( mImpl->mCursorWidth );
1824 void Decorator::SetHandleActive( HandleType handleType, bool active )
1826 mImpl->mHandle[handleType].active = active;
1830 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1832 mImpl->mHandlePreviousCrossed = false;
1835 // TODO: this is a work-around.
1836 // The problem is the handle actor does not receive the touch event with the Interrupt
1837 // state when the power button is pressed and the application goes to background.
1838 mImpl->mHandle[handleType].pressed = false;
1839 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1840 ImageActor imageActor = mImpl->mHandle[handleType].actor;
1841 if( imageReleased && imageActor )
1843 imageActor.SetImage( imageReleased );
1849 bool Decorator::IsHandleActive( HandleType handleType ) const
1851 return mImpl->mHandle[handleType].active ;
1854 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1856 mImpl->SetHandleImage( handleType, handleImageType, image );
1859 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1861 return mImpl->mHandleImages[handleType][handleImageType];
1864 void Decorator::SetHandleColor( const Vector4& color )
1866 mImpl->mHandleColor = color;
1869 const Vector4& Decorator::GetHandleColor() const
1871 return mImpl->mHandleColor;
1874 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1876 // Adjust handle's displacement
1877 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1879 handle.grabDisplacementX -= x - handle.position.x;
1880 handle.grabDisplacementY -= y - handle.position.y;
1882 handle.position.x = x;
1883 handle.position.y = y;
1884 handle.lineHeight = height;
1887 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1889 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1891 x = handle.position.x;
1892 y = handle.position.y;
1893 height = handle.lineHeight;
1896 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1898 return mImpl->mHandle[handleType].position;
1901 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1903 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1906 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1908 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1911 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1913 mImpl->mFlipSelectionHandlesOnCross = enable;
1916 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1918 mImpl->mHandleCurrentCrossed = indicesSwapped;
1919 mImpl->mFlipLeftSelectionHandleDirection = left;
1920 mImpl->mFlipRightSelectionHandleDirection = right;
1923 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1925 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1928 void Decorator::ClearHighlights()
1930 mImpl->mHighlightQuadList.clear();
1931 mImpl->mHighlightPosition = Vector2::ZERO;
1934 void Decorator::SetHighlightColor( const Vector4& color )
1936 mImpl->mHighlightColor = color;
1939 const Vector4& Decorator::GetHighlightColor() const
1941 return mImpl->mHighlightColor;
1944 void Decorator::SetTextDepth( int textDepth )
1946 mImpl->mTextDepth = textDepth;
1949 void Decorator::SetPopupActive( bool active )
1951 mImpl->mActiveCopyPastePopup = active;
1954 bool Decorator::IsPopupActive() const
1956 return mImpl->mActiveCopyPastePopup ;
1959 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1961 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1963 if ( !mImpl->mCopyPastePopup.actor )
1965 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1966 #ifdef DECORATOR_DEBUG
1967 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1969 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1970 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
1973 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1976 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1978 return mImpl->mEnabledPopupButtons;
1983 void Decorator::SetScrollThreshold( float threshold )
1985 mImpl->SetScrollThreshold( threshold );
1988 float Decorator::GetScrollThreshold() const
1990 return mImpl->GetScrollThreshold();
1993 void Decorator::SetScrollSpeed( float speed )
1995 mImpl->SetScrollSpeed( speed );
1998 float Decorator::GetScrollSpeed() const
2000 return mImpl->GetScrollSpeed();
2003 void Decorator::NotifyEndOfScroll()
2005 mImpl->NotifyEndOfScroll();
2008 Decorator::~Decorator()
2013 Decorator::Decorator( ControllerInterface& controller,
2014 TextSelectionPopupCallbackInterface& callbackInterface )
2017 mImpl = new Decorator::Impl( controller, callbackInterface );
2022 } // namespace Toolkit