X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fdecorator%2Ftext-decorator.cpp;h=ff03ac9db308aff0e41e16fa85c56d4b161a52b5;hp=8d553009cf45a38e17f43fdce1fde3815789ad6c;hb=26591c0c1628739c9f06d78e5aaeb552f74ff74b;hpb=54623dab45ae809727d7265ef4c5c75ccf346b91 diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp index 8d55300..ff03ac9 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@ -33,8 +33,9 @@ #include // INTERNAL INCLUDES -#include -#include +#include +#include +#include #ifdef DEBUG_ENABLED #define DECORATOR_DEBUG @@ -85,11 +86,6 @@ Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLog // Local Data namespace { - -const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "cursor_handler_center.png" ); - -const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position. - const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f ); const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f ); @@ -219,14 +215,15 @@ struct Decorator::Impl : public ConnectionTracker active( false ), visible( false ), pressed( false ), + verticallyFlippedPreferred( false ), horizontallyFlipped( false ), verticallyFlipped( false ) { } - ImageActor actor; + ImageView actor; Actor grabArea; - ImageActor markerActor; + ImageView markerActor; Vector2 position; Size size; @@ -236,21 +233,20 @@ struct Decorator::Impl : public ConnectionTracker bool active : 1; bool visible : 1; bool pressed : 1; - bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped. - bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped. + bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped. + bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped. + bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped. }; struct PopupImpl { PopupImpl() - : position(), - offset( DEFAULT_POPUP_OFFSET ) + : position() { } TextSelectionPopup actor; Vector3 position; - int offset; }; Impl( ControllerInterface& controller, @@ -287,9 +283,7 @@ struct Decorator::Impl : public ConnectionTracker mNotifyEndOfScroll( false ) { mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2; - mQuadIndexFormat[ "indices" ] = Property::INTEGER; - mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) ); - + mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ); SetupTouchEvents(); } @@ -299,6 +293,8 @@ struct Decorator::Impl : public ConnectionTracker */ void Relayout( const Vector2& size ) { + mControlSize = size; + // TODO - Remove this if nothing is active CreateActiveLayer(); @@ -308,7 +304,7 @@ struct Decorator::Impl : public ConnectionTracker if( mPrimaryCursor ) { const CursorImpl& cursor = mCursor[PRIMARY_CURSOR]; - mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f ); + mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f ); if( mPrimaryCursorVisible ) { mPrimaryCursor.SetPosition( cursor.position.x, @@ -320,7 +316,7 @@ struct Decorator::Impl : public ConnectionTracker if( mSecondaryCursor ) { const CursorImpl& cursor = mCursor[SECONDARY_CURSOR]; - mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f ); + mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f ); if( mSecondaryCursorVisible ) { mSecondaryCursor.SetPosition( cursor.position.x, @@ -332,9 +328,10 @@ struct Decorator::Impl : public ConnectionTracker // Show or hide the grab handle HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + bool newGrabHandlePosition = false; if( grabHandle.active ) { - const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= size.width ) && ( grabHandle.position.x >= 0.f ); + const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) && ( grabHandle.position.x >= 0.f ); if( isVisible ) { @@ -345,6 +342,8 @@ struct Decorator::Impl : public ConnectionTracker // Sets the grab handle image according if it's pressed, flipped, etc. SetHandleImage( GRAB_HANDLE ); + + newGrabHandlePosition = true; } if( grabHandle.actor ) @@ -360,10 +359,12 @@ struct Decorator::Impl : public ConnectionTracker // Show or hide the selection handles/highlight HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ]; HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ]; + bool newPrimaryHandlePosition = false; + bool newSecondaryHandlePosition = false; if( primary.active || secondary.active ) { - const bool isPrimaryVisible = ( primary.position.x <= size.width ) && ( primary.position.x >= 0.f ); - const bool isSecondaryVisible = ( secondary.position.x <= size.width ) && ( secondary.position.x >= 0.f ); + const bool isPrimaryVisible = ( primary.position.x <= mControlSize.width ) && ( primary.position.x >= 0.f ); + const bool isSecondaryVisible = ( secondary.position.x <= mControlSize.width ) && ( secondary.position.x >= 0.f ); if( isPrimaryVisible || isSecondaryVisible ) { @@ -377,6 +378,8 @@ struct Decorator::Impl : public ConnectionTracker SetHandleImage( LEFT_SELECTION_HANDLE ); SetSelectionHandleMarkerSize( primary ); + + newPrimaryHandlePosition = true; } if( isSecondaryVisible ) @@ -387,6 +390,8 @@ struct Decorator::Impl : public ConnectionTracker SetHandleImage( RIGHT_SELECTION_HANDLE ); SetSelectionHandleMarkerSize( secondary ); + + newSecondaryHandlePosition = true; } } @@ -418,9 +423,18 @@ struct Decorator::Impl : public ConnectionTracker } } + if( newGrabHandlePosition || + newPrimaryHandlePosition || + newSecondaryHandlePosition ) + { + // Setup property notifications to find whether the handles leave the boundaries of the current display. + SetupActiveLayerPropertyNotifications(); + } + if( mActiveCopyPastePopup ) { ShowPopup(); + mPopupSetNewPosition = true; } else { @@ -460,32 +474,47 @@ struct Decorator::Impl : public ConnectionTracker void DeterminePositionPopup() { - if ( !mActiveCopyPastePopup ) + if( !mActiveCopyPastePopup ) { return; } + // Retrieves the popup's size after relayout. + const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f ); + if( mPopupSetNewPosition ) { - if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active ) - { - float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x ); - float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x ); + const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE]; + const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE]; + const CursorImpl& cursor = mCursor[PRIMARY_CURSOR]; - float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y ); + if( primaryHandle.active || secondaryHandle.active ) + { + // Calculates the popup's position if selection handles are active. + const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x ); + const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x ); + const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height ); - mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f ); - mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset; + mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f ); + mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y ); } else { - mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property + // Calculates the popup's position if the grab handle is active. + const HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + if( grabHandle.verticallyFlipped ) + { + mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f ); + } + else + { + mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height + cursor.position.y, 0.0f ); + } } } - Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f ); - - GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox ); + // Checks if there is enough space above the text control. If not it places the popup under it. + GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox ); SetUpPopupPositionNotifications(); @@ -500,11 +529,11 @@ struct Decorator::Impl : public ConnectionTracker DeterminePositionPopup(); } - void CreateCursor( ImageActor& cursor, const Vector4& color ) + void CreateCursor( Control& cursor, const Vector4& color ) { - cursor = CreateSolidColorActor( color ); - cursor.SetSortModifier( DECORATION_DEPTH_INDEX ); - cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one. + cursor = Control::New(); + cursor.SetBackgroundColor( color ); + cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); } @@ -612,7 +641,6 @@ struct Decorator::Impl : public ConnectionTracker mActiveLayer.SetParentOrigin( ParentOrigin::CENTER ); mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); - mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION ); // Add the active layer telling the controller it doesn't need clipping. mController.AddDecoration( mActiveLayer, false ); @@ -623,7 +651,7 @@ struct Decorator::Impl : public ConnectionTracker void SetSelectionHandleMarkerSize( HandleImpl& handle ) { - if ( handle.markerActor ) + if( handle.markerActor ) { handle.markerActor.SetSize( 0, handle.lineHeight ); } @@ -634,46 +662,47 @@ struct Decorator::Impl : public ConnectionTracker HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; if( !grabHandle.actor ) { - if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ) + if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ) { - SetHandleImage( GRAB_HANDLE, HANDLE_IMAGE_RELEASED, ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED ) ); - } + grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ); + GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION ); + grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ); - grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); - grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - // Area that Grab handle responds to, larger than actual handle so easier to move + // Area that Grab handle responds to, larger than actual handle so easier to move #ifdef DECORATOR_DEBUG - grabHandle.actor.SetName( "GrabHandleActor" ); - if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) ) - { - grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 ); - grabHandle.grabArea.SetName( "GrabArea" ); - } - else - { - grabHandle.grabArea = Actor::New(); - grabHandle.grabArea.SetName( "GrabArea" ); - } + grabHandle.actor.SetName( "GrabHandleActor" ); + if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) ) + { + grabHandle.grabArea = Control::New(); + Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea ); + control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) ); + grabHandle.grabArea.SetName( "GrabArea" ); + } + else + { + grabHandle.grabArea = Actor::New(); + grabHandle.grabArea.SetName( "GrabArea" ); + } #else - grabHandle.grabArea = Actor::New(); + grabHandle.grabArea = Actor::New(); #endif - grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); - grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); - grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); - grabHandle.actor.Add( grabHandle.grabArea ); - grabHandle.actor.SetColor( mHandleColor ); + grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); + grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); + grabHandle.actor.Add( grabHandle.grabArea ); + grabHandle.actor.SetColor( mHandleColor ); - grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched ); - mTapDetector.Attach( grabHandle.grabArea ); - mPanGestureDetector.Attach( grabHandle.grabArea ); + grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched ); + mTapDetector.Attach( grabHandle.grabArea ); + mPanGestureDetector.Attach( grabHandle.grabArea ); - mActiveLayer.Add( grabHandle.actor ); + mActiveLayer.Add( grabHandle.actor ); + } } - if( !grabHandle.actor.GetParent() ) + if( grabHandle.actor && !grabHandle.actor.GetParent() ) { mActiveLayer.Add( grabHandle.actor ); } @@ -681,20 +710,20 @@ struct Decorator::Impl : public ConnectionTracker void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType ) { - if ( image ) + if( image ) { - handle.markerActor = ImageActor::New( image ); + handle.markerActor = ImageView::New( image ); handle.markerActor.SetColor( mHandleColor ); handle.actor.Add( handle.markerActor ); handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT ); - if ( LEFT_SELECTION_HANDLE == handleType ) + if( LEFT_SELECTION_HANDLE == handleType ) { handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT ); handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT ); } - else if ( RIGHT_SELECTION_HANDLE == handleType ) + else if( RIGHT_SELECTION_HANDLE == handleType ) { handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT ); handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); @@ -707,33 +736,36 @@ struct Decorator::Impl : public ConnectionTracker HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ]; if( !primary.actor ) { - primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); + if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) + { + primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); #ifdef DECORATOR_DEBUG - primary.actor.SetName("SelectionHandleOne"); + primary.actor.SetName("SelectionHandleOne"); #endif - primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. - primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); - primary.actor.SetColor( mHandleColor ); + primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. + GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION ); + primary.actor.SetColor( mHandleColor ); - primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move #ifdef DECORATOR_DEBUG - primary.grabArea.SetName("SelectionHandleOneGrabArea"); + primary.grabArea.SetName("SelectionHandleOneGrabArea"); #endif - primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); - primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); - primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); + primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); + primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); - mTapDetector.Attach( primary.grabArea ); - mPanGestureDetector.Attach( primary.grabArea ); - primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched ); + mTapDetector.Attach( primary.grabArea ); + mPanGestureDetector.Attach( primary.grabArea ); + primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched ); - primary.actor.Add( primary.grabArea ); + primary.actor.Add( primary.grabArea ); - CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE ); + CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE ); + } } - if( !primary.actor.GetParent() ) + if( primary.actor && !primary.actor.GetParent() ) { mActiveLayer.Add( primary.actor ); } @@ -741,33 +773,36 @@ struct Decorator::Impl : public ConnectionTracker HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ]; if( !secondary.actor ) { - secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); + if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) + { + secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); #ifdef DECORATOR_DEBUG - secondary.actor.SetName("SelectionHandleTwo"); + secondary.actor.SetName("SelectionHandleTwo"); #endif - secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text. - secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); - secondary.actor.SetColor( mHandleColor ); + secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text. + GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION ); + secondary.actor.SetColor( mHandleColor ); - secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move #ifdef DECORATOR_DEBUG - secondary.grabArea.SetName("SelectionHandleTwoGrabArea"); + secondary.grabArea.SetName("SelectionHandleTwoGrabArea"); #endif - secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); - secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); - secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); + secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); + secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); - mTapDetector.Attach( secondary.grabArea ); - mPanGestureDetector.Attach( secondary.grabArea ); - secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched ); + mTapDetector.Attach( secondary.grabArea ); + mPanGestureDetector.Attach( secondary.grabArea ); + secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched ); - secondary.actor.Add( secondary.grabArea ); + secondary.actor.Add( secondary.grabArea ); - CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE ); + CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE ); + } } - if( !secondary.actor.GetParent() ) + if( secondary.actor && !secondary.actor.GetParent() ) { mActiveLayer.Add( secondary.actor ); } @@ -775,16 +810,14 @@ struct Decorator::Impl : public ConnectionTracker void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position ) { - // Get the world position of the active layer + // Gets the world position of the active layer. The active layer is where the handles are added. const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition(); - // Get the size of the UI control. - Vector2 targetSize; - mController.GetTargetSize( targetSize ); - // The grab handle position in world coords. - position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x; - position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight; + // The active layer's world position is the center of the active layer. The origin of the + // coord system of the handles is the top left of the active layer. + position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x; + position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y; } void SetGrabHandlePosition() @@ -792,13 +825,21 @@ struct Decorator::Impl : public ConnectionTracker // Reference to the grab handle. HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; - // The grab handle position in world coords. + // Transforms the handle position into world coordinates. + // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition() + // as it's transforming the handle's position set by the text-controller and not + // the final position set to the actor. Another difference is the GetCurrentWorldPosition() + // retrieves the position of the center of the actor but the handle's position set + // by the text controller is not the center of the actor. Vector2 grabHandleWorldPosition; CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition ); // Check if the grab handle exceeds the boundaries of the decoration box. // At the moment only the height is checked for the grab handle. - grabHandle.verticallyFlipped = ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w ); + + grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred && + ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) || + ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w ); // The grab handle 'y' position in local coords. // If the grab handle exceeds the bottom of the decoration box, @@ -806,8 +847,11 @@ struct Decorator::Impl : public ConnectionTracker // The SetGrabHandleImage() method will change the orientation. const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight; - grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ), - yLocalPosition ); // TODO : Fix for multiline. + if( grabHandle.actor ) + { + grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ), + yLocalPosition ); // TODO : Fix for multiline. + } } void SetSelectionHandlePosition( HandleType type ) @@ -817,11 +861,16 @@ struct Decorator::Impl : public ConnectionTracker // Reference to the selection handle. HandleImpl& handle = mHandle[type]; - // Get the world coordinates of the handle position. + // Transforms the handle position into world coordinates. + // @note This is not the same value as handle.actor.GetCurrentWorldPosition() + // as it's transforming the handle's position set by the text-controller and not + // the final position set to the actor. Another difference is the GetCurrentWorldPosition() + // retrieves the position of the center of the actor but the handle's position set + // by the text controller is not the center of the actor. Vector2 handleWorldPosition; CalculateHandleWorldCoordinates( handle, handleWorldPosition ); - // Whether to flip the handle. + // Whether to flip the handle (horizontally). bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection; // Whether to flip the handles if they are crossed. @@ -834,9 +883,23 @@ struct Decorator::Impl : public ConnectionTracker // Does not flip if both conditions are true (double flip) flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed ); + // Will flip the handles vertically if the user prefers it. + bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred; + + if( crossFlip || mHandlePreviousCrossed ) + { + if( isPrimaryHandle ) + { + verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred; + } + else + { + verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred; + } + } + // Check if the selection handle exceeds the boundaries of the decoration box. const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x ); - const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z ); // Does not flip if both conditions are true (double flip) @@ -844,7 +907,7 @@ struct Decorator::Impl : public ConnectionTracker if( flipHandle ) { - if( !handle.horizontallyFlipped ) + if( handle.actor && !handle.horizontallyFlipped ) { // Change the anchor point to flip the image. handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT ); @@ -854,7 +917,7 @@ struct Decorator::Impl : public ConnectionTracker } else { - if( handle.horizontallyFlipped ) + if( handle.actor && handle.horizontallyFlipped ) { // Reset the anchor point. handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT ); @@ -864,7 +927,9 @@ struct Decorator::Impl : public ConnectionTracker } // Whether to flip the handle vertically. - handle.verticallyFlipped = ( handleWorldPosition.y + handle.size.height > mBoundingBox.w ); + handle.verticallyFlipped = ( verticallyFlippedPreferred && + ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) || + ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w ); // The primary selection handle 'y' position in local coords. // If the handle exceeds the bottom of the decoration box, @@ -872,8 +937,11 @@ struct Decorator::Impl : public ConnectionTracker // The SetHandleImage() method will change the orientation. const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight; - handle.actor.SetPosition( handle.position.x, - yLocalPosition ); // TODO : Fix for multiline. + if( handle.actor ) + { + handle.actor.SetPosition( handle.position.x, + yLocalPosition ); // TODO : Fix for multiline. + } } void SetHandleImage( HandleType type ) @@ -894,18 +962,27 @@ struct Decorator::Impl : public ConnectionTracker } // Chooses between the released or pressed image. It checks whether the pressed image exists. - const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED ); + if( handle.actor ) + { + const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED ); - handle.actor.SetImage( mHandleImages[type][imageType] ); + handle.actor.SetImage( mHandleImages[type][imageType] ); + } if( HANDLE_TYPE_COUNT != markerType ) { - const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED ); - handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] ); + if( handle.markerActor ) + { + const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED ); + handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] ); + } } // Whether to flip the handle vertically. - handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ); + if( handle.actor ) + { + handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ); + } } void CreateHighlight() @@ -934,7 +1011,7 @@ struct Decorator::Impl : public ConnectionTracker if( !mHighlightQuadList.empty() ) { Vector< Vector2 > vertices; - Vector< unsigned int> indices; + Vector< unsigned short> indices; Vector2 vertex; std::vector::iterator iter = mHighlightQuadList.begin(); @@ -975,37 +1052,23 @@ struct Decorator::Impl : public ConnectionTracker indices.PushBack( v + 3 ); } - if( mQuadVertices ) - { - mQuadVertices.SetSize( vertices.Size() ); - } - else - { - mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() ); - } - - if( mQuadIndices ) + if( ! mQuadVertices ) { - mQuadIndices.SetSize( indices.Size() ); - } - else - { - mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() ); + mQuadVertices = PropertyBuffer::New( mQuadVertexFormat ); } - mQuadVertices.SetData( &vertices[ 0 ] ); - mQuadIndices.SetData( &indices[ 0 ] ); + mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() ); if( !mQuadGeometry ) { mQuadGeometry = Geometry::New(); mQuadGeometry.AddVertexBuffer( mQuadVertices ); } - mQuadGeometry.SetIndexBuffer( mQuadIndices ); + mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() ); if( !mHighlightRenderer ) { - mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial ); + mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader ); mHighlightActor.AddRenderer( mHighlightRenderer ); } } @@ -1017,7 +1080,7 @@ struct Decorator::Impl : public ConnectionTracker if( mHighlightRenderer ) { - mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u. + mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u. } } } @@ -1034,11 +1097,11 @@ struct Decorator::Impl : public ConnectionTracker { if( Gesture::Started == gesture.state ) { - handle.grabDisplacementX = handle.grabDisplacementY = 0; + handle.grabDisplacementX = handle.grabDisplacementY = 0.f; } handle.grabDisplacementX += gesture.displacement.x; - handle.grabDisplacementY += gesture.displacement.y; + handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y ); const float x = handle.position.x + handle.grabDisplacementX; const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY; @@ -1086,7 +1149,10 @@ struct Decorator::Impl : public ConnectionTracker mController.DecorationEvent( type, HANDLE_RELEASED, x, y ); } - handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] ); + if( handle.actor ) + { + handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] ); + } handle.pressed = false; mHandlePanning = false; @@ -1192,30 +1258,234 @@ struct Decorator::Impl : public ConnectionTracker return true; } - // Popup + void HandleResetPosition( PropertyNotification& source ) + { + const HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; - float AlternatePopUpPositionRelativeToCursor() + if( grabHandle.active ) + { + // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box. + SetGrabHandlePosition(); + + // Sets the grab handle image according if it's pressed, flipped, etc. + SetHandleImage( GRAB_HANDLE ); + } + else + { + // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box. + SetSelectionHandlePosition( LEFT_SELECTION_HANDLE ); + + // Sets the primary handle image according if it's pressed, flipped, etc. + SetHandleImage( LEFT_SELECTION_HANDLE ); + + // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box. + SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE ); + + // Sets the secondary handle image according if it's pressed, flipped, etc. + SetHandleImage( RIGHT_SELECTION_HANDLE ); + } + } + + void SetupActiveLayerPropertyNotifications() { - float alternativePosition=0.0f;; + if( !mActiveLayer ) + { + return; + } + + // Vertical notifications. + + // Disconnect any previous connected callback. + if( mVerticalLessThanNotification ) + { + mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition ); + mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification ); + } - if ( mPrimaryCursor ) // Secondary cursor not used for paste + if( mVerticalGreaterThanNotification ) { - Cursor cursor = PRIMARY_CURSOR; - alternativePosition = mCursor[cursor].position.y; + mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition ); + mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification ); } - const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class. + const HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE]; + const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE]; - if( mHandle[GRAB_HANDLE].active ) + if( grabHandle.active ) { - // If grab handle enabled then position pop-up below the grab handle. - const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo - const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property - alternativePosition += grabHandleSize.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ; + if( grabHandle.verticallyFlipped ) + { + // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display. + mVerticalGreaterThanNotification.Reset(); + + // The vertical distance from the center of the active layer to the top edje of the display. + const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height; + + mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + LessThanCondition( mBoundingBox.y + topHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + else + { + // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display. + mVerticalLessThanNotification.Reset(); + + // The vertical distance from the center of the active layer to the bottom edje of the display. + const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height; + + mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + GreaterThanCondition( mBoundingBox.w - bottomHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + } + else // The selection handles are active + { + if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped ) + { + // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display. + mVerticalGreaterThanNotification.Reset(); + + // The vertical distance from the center of the active layer to the top edje of the display. + const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height ); + + mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + LessThanCondition( mBoundingBox.y + topHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped ) + { + // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display. + mVerticalLessThanNotification.Reset(); + + // The vertical distance from the center of the active layer to the bottom edje of the display. + const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height, + secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height ); + + mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + GreaterThanCondition( mBoundingBox.w - bottomHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + else + { + // Only one of the selection handles is vertically flipped. Both vertical notifications are needed. + + // The vertical distance from the center of the active layer to the top edje of the display. + const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ? + -primaryHandle.position.y + primaryHandle.size.height : + -secondaryHandle.position.y + secondaryHandle.size.height ); + + mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + LessThanCondition( mBoundingBox.y + topHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + + // The vertical distance from the center of the active layer to the bottom edje of the display. + const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ? + secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height : + primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height ); + + mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + GreaterThanCondition( mBoundingBox.w - bottomHeight ) ); + + // Notifies the change from false to true and from true to false. + mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + } + + // Horizontal notifications. + + // Disconnect any previous connected callback. + if( mHorizontalLessThanNotification ) + { + mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition ); + mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification ); + } + + if( mHorizontalGreaterThanNotification ) + { + mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition ); + mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification ); + } + + if( primaryHandle.active || secondaryHandle.active ) + { + // The horizontal distance from the center of the active layer to the left edje of the display. + const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width, + -secondaryHandle.position.x + secondaryHandle.size.width ); + + mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, + LessThanCondition( mBoundingBox.x + leftWidth ) ); + + // Notifies the change from false to true and from true to false. + mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + + // The horizontal distance from the center of the active layer to the right edje of the display. + const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width, + secondaryHandle.position.x + secondaryHandle.size.width ); + + mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X, + GreaterThanCondition( mBoundingBox.z - rightWidth ) ); + + // Notifies the change from false to true and from true to false. + mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged ); + + // Connects the signals with the callbacks. + mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition ); + } + } + + // Popup + + float AlternatePopUpPositionRelativeToCursor() + { + float alternativePosition = 0.0f; + + const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ); + + const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE]; + const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE]; + const HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + const CursorImpl& cursor = mCursor[PRIMARY_CURSOR]; + + if( primaryHandle.active || secondaryHandle.active ) + { + const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height ); + alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y ); } else { - alternativePosition += popupHeight; + alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y; } return alternativePosition; @@ -1223,7 +1493,7 @@ struct Decorator::Impl : public ConnectionTracker void PopUpLeavesVerticalBoundary( PropertyNotification& source ) { - float alternativeYPosition=0.0f; + float alternativeYPosition = 0.0f; // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted // if can't be positioned above, then position below row. alternativeYPosition = AlternatePopUpPositionRelativeToCursor(); @@ -1231,31 +1501,28 @@ struct Decorator::Impl : public ConnectionTracker mCopyPastePopup.actor.SetY( alternativeYPosition ); } - - void SetUpPopupPositionNotifications( ) + void SetUpPopupPositionNotifications() { // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left. // Exceeding vertical boundary - float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT); + const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ); PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, - OutsideCondition( mBoundingBox.y + popupHeight * 0.5f, - mBoundingBox.w - popupHeight * 0.5f ) ); + OutsideCondition( mBoundingBox.y + popupHeight * 0.5f, + mBoundingBox.w - popupHeight * 0.5f ) ); verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary ); } - void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld ) + void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld ) { DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() ) // Parent must already by added to Stage for these Get calls to work - Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint(); - Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint; - Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position - Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint; + const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint(); + const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position // Calculate distance to move popup (in local space) so fits within the boundary float xOffSetToKeepWithinBounds = 0.0f; @@ -1263,13 +1530,13 @@ struct Decorator::Impl : public ConnectionTracker { xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x ); } - else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z ) + else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z ) { xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x ); } // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind. - if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y ) + if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y ) { requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor(); } @@ -1277,8 +1544,8 @@ struct Decorator::Impl : public ConnectionTracker requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds; // Prevent pixel mis-alignment by rounding down. - requiredPopupPosition.x = static_cast( requiredPopupPosition.x ); - requiredPopupPosition.y = static_cast( requiredPopupPosition.y ); + requiredPopupPosition.x = floor( requiredPopupPosition.x ); + requiredPopupPosition.y = floor( requiredPopupPosition.y ); } void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image ) @@ -1375,15 +1642,18 @@ struct Decorator::Impl : public ConnectionTracker Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges. - Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else. - ImageActor mPrimaryCursor; - ImageActor mSecondaryCursor; + Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else. + PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value. + PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value. + PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value. + PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value. + Control mPrimaryCursor; + Control mSecondaryCursor; - Actor mHighlightActor; ///< Actor to display highlight + Actor mHighlightActor; ///< Actor to display highlight Renderer mHighlightRenderer; - Material mHighlightMaterial; ///< Material used for highlight + Shader mHighlightShader; ///< Shader used for highlight Property::Map mQuadVertexFormat; - Property::Map mQuadIndexFormat; PopupImpl mCopyPastePopup; TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface; @@ -1395,13 +1665,13 @@ struct Decorator::Impl : public ConnectionTracker HandleImpl mHandle[HANDLE_TYPE_COUNT]; PropertyBuffer mQuadVertices; - PropertyBuffer mQuadIndices; Geometry mQuadGeometry; QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight Vector4 mBoundingBox; ///< The bounding box in world coords. Vector4 mHighlightColor; ///< Color of the highlight Vector2 mHighlightPosition; ///< The position of the highlight actor. + Vector2 mControlSize; ///< The control's size. Set by the Relayout. unsigned int mActiveCursor; unsigned int mCursorBlinkInterval; @@ -1577,10 +1847,10 @@ void Decorator::SetHandleActive( HandleType handleType, bool active ) // state when the power button is pressed and the application goes to background. mImpl->mHandle[handleType].pressed = false; Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]; - ImageActor imageActor = mImpl->mHandle[handleType].actor; - if( imageReleased && imageActor ) + ImageView imageView = mImpl->mHandle[handleType].actor; + if( imageReleased && imageView ) { - imageActor.SetImage( imageReleased ); + imageView.SetImage( imageReleased ); } } @@ -1613,7 +1883,7 @@ const Vector4& Decorator::GetHandleColor() const void Decorator::SetPosition( HandleType handleType, float x, float y, float height ) { - // Adjust grab handle displacement + // Adjust handle's displacement Impl::HandleImpl& handle = mImpl->mHandle[handleType]; handle.grabDisplacementX -= x - handle.position.x; @@ -1638,6 +1908,16 @@ const Vector2& Decorator::GetPosition( HandleType handleType ) const return mImpl->mHandle[handleType].position; } +void Decorator::FlipHandleVertically( HandleType handleType, bool flip ) +{ + mImpl->mHandle[handleType].verticallyFlippedPreferred = flip; +} + +bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const +{ + return mImpl->mHandle[handleType].verticallyFlippedPreferred; +} + void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable ) { mImpl->mFlipSelectionHandlesOnCross = enable;