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=cf450ef30c2f33f29ba0328edd3a1560cb0833d6;hp=2e8570faa1eeead7cf5720bcb1732615bda09b15;hb=5fa76665a97d14bf519ad0f41ad9f7bbacf81ec1;hpb=074b05f53f89491982b2d5380cab7f05b1932a76 diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp index 2e8570f..cf450ef 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@ -24,16 +24,14 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include -#include -#include #include #include #include @@ -41,19 +39,53 @@ #include #include +#include +#include +#include +#include +#include + // INTERNAL INCLUDES #include +#include #include #include #include #include -#include +#include #ifdef DEBUG_ENABLED #define DECORATOR_DEBUG #endif +#define MAKE_SHADER(A)#A + +namespace +{ +const char* VERTEX_SHADER = MAKE_SHADER( +attribute mediump vec2 aPosition; +uniform mediump mat4 uMvpMatrix; +uniform mediump vec3 uSize; + +void main() +{ + mediump vec4 position = vec4( aPosition, 0.0, 1.0 ); + position.xyz *= uSize; + gl_Position = uMvpMatrix * position; +} +); + +const char* FRAGMENT_SHADER = MAKE_SHADER( +uniform lowp vec4 uColor; + +void main() +{ + gl_FragColor = uColor; +} +); +} + namespace Dali { namespace Internal @@ -74,13 +106,17 @@ namespace const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "insertpoint-icon.png" ); const char* DEFAULT_GRAB_HANDLE_IMAGE_PRESSED( DALI_IMAGE_DIR "insertpoint-icon-pressed.png" ); -const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" ); -const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); +const char* DEFAULT_SELECTION_HANDLE_ONE_RELEASED( DALI_IMAGE_DIR "text-input-selection-handle-left.png" ); +const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" ); +const char* DEFAULT_SELECTION_HANDLE_TWO_RELEASED( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); +const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.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.5f, 2.0f, 1.0f ); -const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); +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 ); -const Dali::Vector4 LIGHT_BLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // The text highlight color. +const Dali::Vector4 LIGHT_BLUE( (0xb2 / 255.0f), (0xeb / 255.0f), (0xf2 / 255.0f), 0.5f ); // The text highlight color. const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval const float TO_MILLISECONDS = 1000.f; @@ -192,6 +228,7 @@ struct Decorator::Impl : public ConnectionTracker grabDisplacementY( 0.f ), active( false ), visible( false ), + pressed( false ), flipped( false ) { } @@ -205,14 +242,31 @@ struct Decorator::Impl : public ConnectionTracker float grabDisplacementY; bool active : 1; bool visible : 1; + bool pressed : 1; bool flipped : 1; }; - Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer ) - : mTextControlParent( parent ), - mObserver( observer ), + struct PopupImpl + { + PopupImpl() + : position(), + offset( DEFAULT_POPUP_OFFSET ) + { + } + + TextSelectionPopup actor; + Vector3 position; + int offset; + }; + + Impl( ControllerInterface& controller, + TextSelectionPopupCallbackInterface& callbackInterface ) + : mController( controller ), + mEnabledPopupButtons( TextSelectionPopup::NONE ), + mTextSelectionPopupCallbackInterface( callbackInterface ), mBoundingBox( Rect() ), mHighlightColor( LIGHT_BLUE ), + mHighlightPosition( Vector2::ZERO ), mActiveCursor( ACTIVE_CURSOR_NONE ), mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ), mCursorBlinkDuration( 0.0f ), @@ -224,8 +278,13 @@ struct Decorator::Impl : public ConnectionTracker mActiveCopyPastePopup( false ), mCursorBlinkStatus( true ), mPrimaryCursorVisible( false ), - mSecondaryCursorVisible( false ) + mSecondaryCursorVisible( false ), + mSwapSelectionHandles( false ), + mNotifyEndOfScroll( false ) { + mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2; + mQuadIndexFormat[ "indices" ] = Property::UNSIGNED_INTEGER; + mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) ); } /** @@ -246,17 +305,6 @@ struct Decorator::Impl : public ConnectionTracker if( mPrimaryCursorVisible ) { Vector2 position = cursor.position; - if( GRAB_HANDLE == mHandleScrolling ) - { - if( mScrollDirection == SCROLL_RIGHT ) - { - position.x = 0.f; - } - else - { - position.x = size.width; - } - } mPrimaryCursor.SetPosition( position.x, position.y ); @@ -283,18 +331,6 @@ struct Decorator::Impl : public ConnectionTracker { Vector2 position = grabHandle.position; - if( GRAB_HANDLE == mHandleScrolling ) - { - if( mScrollDirection == SCROLL_RIGHT ) - { - position.x = 0.f; - } - else - { - position.x = size.width; - } - } - const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f ); if( isVisible ) @@ -318,15 +354,35 @@ struct Decorator::Impl : public ConnectionTracker HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ]; if( primary.active || secondary.active ) { - SetupTouchEvents(); + Vector2 primaryPosition = primary.position; + Vector2 secondaryPosition = secondary.position; - CreateSelectionHandles(); + const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f ); + const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f ); - primary.actor.SetPosition( primary.position.x, - primary.position.y + primary.lineHeight ); + if( isPrimaryVisible || isSecondaryVisible ) + { + SetupTouchEvents(); - secondary.actor.SetPosition( secondary.position.x, - secondary.position.y + secondary.lineHeight ); + CreateSelectionHandles(); + + if( isPrimaryVisible ) + { + primary.actor.SetPosition( primaryPosition.x, + primaryPosition.y + primary.lineHeight ); + } + + if( isSecondaryVisible ) + { + secondary.actor.SetPosition( secondaryPosition.x, + secondaryPosition.y + secondary.lineHeight ); + } + } + primary.actor.SetVisible( isPrimaryVisible ); + secondary.actor.SetVisible( isSecondaryVisible ); + + // Shouldn't be needed...... + UnparentAndReset( mHighlightActor ); CreateHighlight(); UpdateHighlight(); @@ -335,27 +391,29 @@ struct Decorator::Impl : public ConnectionTracker { UnparentAndReset( primary.actor ); UnparentAndReset( secondary.actor ); - UnparentAndReset( mHighlightMeshActor ); + UnparentAndReset( mHighlightActor ); } if ( mActiveCopyPastePopup ) { - if ( !mCopyPastePopup ) + // todo Swap UnparentAndReset for DeterminePositionPopup() if mCopyPastePopup.actor valid Once the issue with the labels disappearing is fixed. + UnparentAndReset( mCopyPastePopup.actor ); + if ( !mCopyPastePopup.actor ) { - mCopyPastePopup = TextSelectionPopup::New(); + mCopyPastePopup.actor = TextSelectionPopup::New( mEnabledPopupButtons, &mTextSelectionPopupCallbackInterface ); #ifdef DECORATOR_DEBUG - mCopyPastePopup.SetName("mCopyPastePopup"); + mCopyPastePopup.actor.SetName("mCopyPastePopup"); #endif - mCopyPastePopup.SetAnchorPoint( AnchorPoint::CENTER ); - mCopyPastePopup.OnRelayoutSignal().Connect( this, &Decorator::Impl::PopUpRelayoutComplete ); // Position popup after size negotiation - mActiveLayer.Add ( mCopyPastePopup ); + mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER ); + mCopyPastePopup.actor.OnRelayoutSignal().Connect( this, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation + mActiveLayer.Add ( mCopyPastePopup.actor ); } } else { - if ( mCopyPastePopup ) + if ( mCopyPastePopup.actor ) { - UnparentAndReset( mCopyPastePopup ); + UnparentAndReset( mCopyPastePopup.actor ); } } } @@ -367,30 +425,52 @@ struct Decorator::Impl : public ConnectionTracker mHandle[ GRAB_HANDLE ].position += scrollOffset; mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset; mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset; - - // TODO Highlight box?? + mHighlightPosition += scrollOffset; } - void PopUpRelayoutComplete( Actor actor ) + void DeterminePositionPopup() { - // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box. + if ( !mActiveCopyPastePopup ) + { + return; + } - mCopyPastePopup.OnRelayoutSignal().Disconnect( this, &Decorator::Impl::PopUpRelayoutComplete ); + 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 ); - Vector3 popupPosition( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f); //todo 100 to be an offset Property + float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y ); - Vector3 popupSize = Vector3( mCopyPastePopup.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT ), 0.0f ); + mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f ); + mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset; + } + 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 + } + + Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f ); - GetConstrainedPopupPosition( popupPosition, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox ); + GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox ); - SetUpPopUpPositionNotifications(); + SetUpPopupPositionNotifications(); - mCopyPastePopup.SetPosition( popupPosition ); //todo grabhandle(cursor) or selection handle positions to be used + mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position ); + } + + void PopupRelayoutComplete( Actor actor ) + { + // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box. + mCopyPastePopup.actor.OnRelayoutSignal().Disconnect( this, &Decorator::Impl::PopupRelayoutComplete ); + + DeterminePositionPopup(); } void CreateCursor( ImageActor& 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.SetAnchorPoint( AnchorPoint::TOP_CENTER ); } @@ -405,7 +485,7 @@ struct Decorator::Impl : public ConnectionTracker } else { - /* Create Primary and or Secondary Cursor(s) if active and add to parent */ + // Create Primary and or Secondary Cursor(s) if active and add to parent if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY || mActiveCursor == ACTIVE_CURSOR_BOTH ) { @@ -473,8 +553,6 @@ struct Decorator::Impl : public ConnectionTracker { if( !mActiveLayer ) { - Actor parent = mTextControlParent.Self(); - mActiveLayer = Layer::New(); #ifdef DECORATOR_DEBUG mActiveLayer.SetName ( "ActiveLayerActor" ); @@ -484,7 +562,8 @@ struct Decorator::Impl : public ConnectionTracker mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION ); - parent.Add( mActiveLayer ); + // Add the active layer telling the controller it doesn't need clipping. + mController.AddDecoration( mActiveLayer, false ); } mActiveLayer.RaiseToTop(); @@ -505,8 +584,8 @@ struct Decorator::Impl : public ConnectionTracker } grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ); + grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - grabHandle.actor.SetDrawMode( DrawMode::OVERLAY ); // Area that Grab handle responds to, larger than actual handle so easier to move #ifdef DECORATOR_DEBUG grabHandle.actor.SetName( "GrabHandleActor" ); @@ -530,6 +609,7 @@ struct Decorator::Impl : public ConnectionTracker grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); grabHandle.actor.Add( grabHandle.grabArea ); + grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched ); mTapDetector.Attach( grabHandle.grabArea ); mPanGestureDetector.Attach( grabHandle.grabArea ); @@ -544,7 +624,11 @@ struct Decorator::Impl : public ConnectionTracker { if( !mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) { - mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE ); + mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_RELEASED ); + } + if( !mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) + { + mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED ); } primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); @@ -552,7 +636,7 @@ struct Decorator::Impl : public ConnectionTracker 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.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text + primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); primary.flipped = false; primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move @@ -560,8 +644,9 @@ struct Decorator::Impl : public ConnectionTracker 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.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); mTapDetector.Attach( primary.grabArea ); mPanGestureDetector.Attach( primary.grabArea ); @@ -576,7 +661,11 @@ struct Decorator::Impl : public ConnectionTracker { if( !mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) { - mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO ); + mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_RELEASED ); + } + if( !mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) + { + mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED ); } secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); @@ -584,7 +673,7 @@ struct Decorator::Impl : public ConnectionTracker 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.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text + secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX ); secondary.flipped = false; secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move @@ -592,8 +681,9 @@ struct Decorator::Impl : public ConnectionTracker 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.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); mTapDetector.Attach( secondary.grabArea ); mPanGestureDetector.Attach( secondary.grabArea ); @@ -606,117 +696,92 @@ struct Decorator::Impl : public ConnectionTracker void CreateHighlight() { - if ( !mHighlightMeshActor ) + if ( !mHighlightActor ) { - mHighlightMaterial = Material::New( "HighlightMaterial" ); - mHighlightMaterial.SetDiffuseColor( mHighlightColor ); - - mHighlightMeshData.SetMaterial( mHighlightMaterial ); - mHighlightMeshData.SetHasNormals( true ); + mHighlightActor = Actor::New(); - mHighlightMesh = Mesh::New( mHighlightMeshData ); - - mHighlightMeshActor = MeshActor::New( mHighlightMesh ); #ifdef DECORATOR_DEBUG - mHighlightMeshActor.SetName( "HighlightMeshActor" ); + mHighlightActor.SetName( "HighlightActor" ); #endif - mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET ); - - Actor parent = mTextControlParent.Self(); - parent.Add( mHighlightMeshActor ); + mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mHighlightActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET ); + mHighlightActor.SetSize( 1.0f, 1.0f ); + mHighlightActor.SetColor( mHighlightColor ); + mHighlightActor.SetColorMode( USE_OWN_COLOR ); + + // Add the highlight box telling the controller it needs clipping. + mController.AddDecoration( mHighlightActor, true ); } } void UpdateHighlight() { - // Construct a Mesh with a texture to be used as the highlight 'box' for selected text - // - // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads. - // - // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ] - // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ] - // [ BOTTOM] [ MIDDLE ] [ MIDDLE ] - // [BOTTOM] [ MIDDLE ] - // [BOTTOM] - // - // Each quad is created as 2 triangles. - // Middle is just 1 quad regardless of its size. - // - // (0,0) (0,0) - // 0* *2 0* *2 - // TOP TOP - // 3* *1 3* *1 - // 4* *1 4* *6 - // MIDDLE BOTTOM - // 6* *5 7* *5 - // 6* *8 - // BOTTOM - // 9* *7 - // - - if ( mHighlightMesh && mHighlightMaterial && !mHighlightQuadList.empty() ) + + if ( mHighlightActor && !mHighlightQuadList.empty() ) { - MeshData::VertexContainer vertices; - Dali::MeshData::FaceIndices faceIndices; + Vector< Vector2 > vertices; + Vector< unsigned int> indices; + Vector2 vertex; std::vector::iterator iter = mHighlightQuadList.begin(); std::vector::iterator endIter = mHighlightQuadList.end(); - // vertex position defaults to (0 0 0) - MeshData::Vertex vertex; - // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor. - vertex.nZ = 1.0f; - for(std::size_t v = 0; iter != endIter; ++iter,v+=4 ) { - // Add each quad geometry (a sub-selection) to the mesh data. - - // 0-----1 - // |\ | - // | \ A | - // | \ | - // | B \ | - // | \| - // 2-----3 QuadCoordinates& quad = *iter; + // top-left (v+0) vertex.x = quad.min.x; vertex.y = quad.min.y; - vertices.push_back( vertex ); + vertices.PushBack( vertex ); // top-right (v+1) vertex.x = quad.max.x; vertex.y = quad.min.y; - vertices.push_back( vertex ); + vertices.PushBack( vertex ); // bottom-left (v+2) vertex.x = quad.min.x; vertex.y = quad.max.y; - vertices.push_back( vertex ); + vertices.PushBack( vertex ); // bottom-right (v+3) vertex.x = quad.max.x; vertex.y = quad.max.y; - vertices.push_back( vertex ); + vertices.PushBack( vertex ); // triangle A (3, 1, 0) - faceIndices.push_back( v + 3 ); - faceIndices.push_back( v + 1 ); - faceIndices.push_back( v ); + indices.PushBack( v + 3 ); + indices.PushBack( v + 1 ); + indices.PushBack( v ); // triangle B (0, 2, 3) - faceIndices.push_back( v ); - faceIndices.push_back( v + 2 ); - faceIndices.push_back( v + 3 ); - - mHighlightMeshData.SetFaceIndices( faceIndices ); + indices.PushBack( v ); + indices.PushBack( v + 2 ); + indices.PushBack( v + 3 ); } - BoneContainer bones(0); // passed empty as bones not required - mHighlightMeshData.SetData( vertices, faceIndices, bones, mHighlightMaterial ); - mHighlightMesh.UpdateMeshData( mHighlightMeshData ); + PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() ); + PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() ); + + quadVertices.SetData( &vertices[ 0 ] ); + quadIndices.SetData( &indices[ 0 ] ); + + Geometry quadGeometry = Geometry::New(); + quadGeometry.AddVertexBuffer( quadVertices ); + quadGeometry.SetIndexBuffer( quadIndices ); + + // if ( mHighlightRenderer ) + // { + // mHighlightRenderer.SetGeometry( quadGeometry ); + // } + // else + // { + mHighlightRenderer = Dali::Renderer::New( quadGeometry, mHighlightMaterial ); + mHighlightRenderer.SetDepthIndex( DECORATION_DEPTH_INDEX - 1 ); + // } + mHighlightActor.AddRenderer( mHighlightRenderer ); } } @@ -733,10 +798,6 @@ struct Decorator::Impl : public ConnectionTracker if( Gesture::Started == gesture.state ) { handle.grabDisplacementX = handle.grabDisplacementY = 0; - if( mHandleImages[type][HANDLE_IMAGE_PRESSED] ) - { - handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_PRESSED] ); - } } handle.grabDisplacementX += gesture.displacement.x; @@ -748,13 +809,16 @@ struct Decorator::Impl : public ConnectionTracker if( Gesture::Started == gesture.state || Gesture::Continuing == gesture.state ) { + Vector2 targetSize; + mController.GetTargetSize( targetSize ); + if( x < mScrollThreshold ) { mScrollDirection = SCROLL_RIGHT; mHandleScrolling = type; StartScrollTimer(); } - else if( x > mTextControlParent.GetControlSize().width - mScrollThreshold ) + else if( x > targetSize.width - mScrollThreshold ) { mScrollDirection = SCROLL_LEFT; mHandleScrolling = type; @@ -764,23 +828,41 @@ struct Decorator::Impl : public ConnectionTracker { mHandleScrolling = HANDLE_TYPE_COUNT; StopScrollTimer(); - mObserver.HandleEvent( type, HANDLE_PRESSED, x, y ); + mController.DecorationEvent( type, HANDLE_PRESSED, x, y ); } } else if( Gesture::Finished == gesture.state || Gesture::Cancelled == gesture.state ) { - if( mScrollTimer && mScrollTimer.IsRunning() ) + if( mScrollTimer && + ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) ) { + mNotifyEndOfScroll = false; mHandleScrolling = HANDLE_TYPE_COUNT; StopScrollTimer(); - mObserver.HandleEvent( type, HANDLE_STOP_SCROLLING, x, y ); + mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y ); + } + else + { + mController.DecorationEvent( type, HANDLE_RELEASED, x, y ); + } + + if( GRAB_HANDLE == type ) + { + handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] ); } else { - mObserver.HandleEvent( type, HANDLE_RELEASED, x, y ); + HandleType selectionHandleType = type; + + if( mSwapSelectionHandles != handle.flipped ) + { + selectionHandleType = ( LEFT_SELECTION_HANDLE == type ) ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE; + } + + handle.actor.SetImage( mHandleImages[selectionHandleType][HANDLE_IMAGE_RELEASED] ); } - handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] ); + handle.pressed = false; } } @@ -804,16 +886,105 @@ struct Decorator::Impl : public ConnectionTracker } } - bool OnHandleOneTouched( Actor actor, const TouchEvent& touch ) + bool OnGrabHandleTouched( Actor actor, const TouchEvent& event ) + { + // Switch between pressed/release grab-handle images + if( event.GetPointCount() > 0 && + mHandle[GRAB_HANDLE].actor ) + { + const TouchPoint& point = event.GetPoint(0); + + if( TouchPoint::Down == point.state ) + { + mHandle[GRAB_HANDLE].pressed = true; + Image imagePressed = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED]; + if( imagePressed ) + { + mHandle[GRAB_HANDLE].actor.SetImage( imagePressed ); + } + } + else if( ( TouchPoint::Up == point.state ) || + ( TouchPoint::Interrupted == point.state ) ) + { + mHandle[GRAB_HANDLE].pressed = false; + Image imageReleased = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]; + if( imageReleased ) + { + mHandle[GRAB_HANDLE].actor.SetImage( imageReleased ); + } + } + } + + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; + } + + bool OnHandleOneTouched( Actor actor, const TouchEvent& event ) { - // TODO - return false; + // Switch between pressed/release selection handle images + if( event.GetPointCount() > 0 && + mHandle[LEFT_SELECTION_HANDLE].actor ) + { + const TouchPoint& point = event.GetPoint(0); + + const bool flip = mSwapSelectionHandles != mHandle[LEFT_SELECTION_HANDLE].flipped; + if( TouchPoint::Down == point.state ) + { + mHandle[LEFT_SELECTION_HANDLE].pressed = true; + Image imagePressed = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED]; + if( imagePressed ) + { + mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imagePressed ); + } + } + else if( ( TouchPoint::Up == point.state ) || + ( TouchPoint::Interrupted == point.state ) ) + { + mHandle[LEFT_SELECTION_HANDLE].pressed = false; + Image imageReleased = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]; + if( imageReleased ) + { + mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imageReleased ); + } + } + } + + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; } - bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch ) + bool OnHandleTwoTouched( Actor actor, const TouchEvent& event ) { - // TODO - return false; + // Switch between pressed/release selection handle images + if( event.GetPointCount() > 0 && + mHandle[RIGHT_SELECTION_HANDLE].actor ) + { + const TouchPoint& point = event.GetPoint(0); + + const bool flip = mSwapSelectionHandles != mHandle[RIGHT_SELECTION_HANDLE].flipped; + if( TouchPoint::Down == point.state ) + { + Image imagePressed = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED]; + mHandle[RIGHT_SELECTION_HANDLE].pressed = true; + if( imagePressed ) + { + mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imagePressed ); + } + } + else if( ( TouchPoint::Up == point.state ) || + ( TouchPoint::Interrupted == point.state ) ) + { + Image imageReleased = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]; + mHandle[RIGHT_SELECTION_HANDLE].pressed = false; + if( imageReleased ) + { + mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imageReleased ); + } + } + } + + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; } // Popup @@ -852,11 +1023,11 @@ struct Decorator::Impl : public ConnectionTracker // if can't be positioned above, then position below row. alternativeYPosition = AlternatePopUpPositionRelativeToCursor(); - mCopyPastePopup.SetY( alternativeYPosition ); + 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. @@ -865,11 +1036,11 @@ struct Decorator::Impl : public ConnectionTracker Vector4 worldCoordinatesBoundingBox; LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox ); - float popupHeight = mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT); + float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT); - PropertyNotification verticalExceedNotification = mCopyPastePopup.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, - OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight/2, - worldCoordinatesBoundingBox.w - popupHeight/2 ) ); + PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight * 0.5f, + worldCoordinatesBoundingBox.w - popupHeight * 0.5f ) ); verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary ); } @@ -906,6 +1077,34 @@ 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 ); + + } + + void FlipSelectionHandleImages() + { + SetupTouchEvents(); + + CreateSelectionHandles(); + + HandleImpl& leftHandle = mHandle[LEFT_SELECTION_HANDLE]; + HandleImpl& rightHandle = mHandle[RIGHT_SELECTION_HANDLE]; + + const HandleImageType leftImageType = leftHandle.pressed ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED; + const HandleImageType rightImageType = rightHandle.pressed ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED; + const bool leftFlipped = mSwapSelectionHandles != leftHandle.flipped; + const bool rightFlipped = mSwapSelectionHandles != rightHandle.flipped; + + leftHandle.actor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE][leftImageType] ); + + leftHandle.actor.SetAnchorPoint( leftFlipped ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT ); + + rightHandle.actor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE][rightImageType] ); + + rightHandle.actor.SetAnchorPoint( rightFlipped ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT ); } void SetScrollThreshold( float threshold ) @@ -929,6 +1128,16 @@ struct Decorator::Impl : public ConnectionTracker return mScrollSpeed; } + void NotifyEndOfScroll() + { + StopScrollTimer(); + + if( mScrollTimer ) + { + mNotifyEndOfScroll = true; + } + } + /** * Creates and starts a timer to scroll the text when handles are close to the edges of the text. * @@ -968,17 +1177,16 @@ struct Decorator::Impl : public ConnectionTracker { if( HANDLE_TYPE_COUNT != mHandleScrolling ) { - mObserver.HandleEvent( mHandleScrolling, - HANDLE_SCROLLING, - mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance, - 0.f ); + mController.DecorationEvent( mHandleScrolling, + HANDLE_SCROLLING, + mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance, + 0.f ); } return true; } - Internal::Control& mTextControlParent; - Observer& mObserver; + ControllerInterface& mController; TapGestureDetector mTapDetector; PanGestureDetector mPanGestureDetector; @@ -988,14 +1196,18 @@ struct Decorator::Impl : public ConnectionTracker Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else. ImageActor mPrimaryCursor; ImageActor mSecondaryCursor; - MeshActor mHighlightMeshActor; ///< Mesh Actor to display highlight - TextSelectionPopup mCopyPastePopup; + + Actor mHighlightActor; ///< Actor to display highlight + Renderer mHighlightRenderer; + Material mHighlightMaterial; ///< Material used for highlight + Property::Map mQuadVertexFormat; + Property::Map mQuadIndexFormat; + PopupImpl mCopyPastePopup; + TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons + TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface; Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT]; Image mCursorImage; - Mesh mHighlightMesh; ///< Mesh for highlight - MeshData mHighlightMeshData; ///< Mesh Data for highlight - Material mHighlightMaterial; ///< Material used for highlight CursorImpl mCursor[CURSOR_COUNT]; HandleImpl mHandle[HANDLE_TYPE_COUNT]; @@ -1003,6 +1215,7 @@ struct Decorator::Impl : public ConnectionTracker Rect mBoundingBox; Vector4 mHighlightColor; ///< Color of the highlight + Vector2 mHighlightPosition; ///< The position of the highlight actor. unsigned int mActiveCursor; unsigned int mCursorBlinkInterval; @@ -1012,17 +1225,20 @@ struct Decorator::Impl : public ConnectionTracker float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events. float mScrollSpeed; ///< The scroll speed in pixels per second. float mScrollDistance; ///< Distance the text scrolls during a scroll interval. - unsigned int mScrollInterval; ///< Time in milliseconds of a scroll interval. bool mActiveCopyPastePopup : 1; bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off. bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible. bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible. + bool mSwapSelectionHandles : 1; ///< Whether to swap the selection handle images. + bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll. }; -DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer ) +DecoratorPtr Decorator::New( ControllerInterface& controller, + TextSelectionPopupCallbackInterface& callbackInterface ) { - return DecoratorPtr( new Decorator(parent, observer) ); + return DecoratorPtr( new Decorator( controller, + callbackInterface ) ); } void Decorator::SetBoundingBox( const Rect& boundingBox ) @@ -1135,6 +1351,20 @@ float Decorator::GetCursorBlinkDuration() const void Decorator::SetHandleActive( HandleType handleType, bool active ) { mImpl->mHandle[handleType].active = active; + + if( !active ) + { + // TODO: this is a work-around. + // The problem is the handle actor does not receive the touch event with the Interrupt + // 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 ) + { + imageActor.SetImage( imageReleased ); + } + } } bool Decorator::IsHandleActive( HandleType handleType ) const @@ -1174,6 +1404,18 @@ void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& h height = handle.lineHeight; } +const Vector2& Decorator::GetPosition( HandleType handleType ) const +{ + return mImpl->mHandle[handleType].position; +} + +void Decorator::SwapSelectionHandlesEnabled( bool enable ) +{ + mImpl->mSwapSelectionHandles = enable; + + mImpl->FlipSelectionHandleImages(); +} + void Decorator::AddHighlight( float x1, float y1, float x2, float y2 ) { mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) ); @@ -1182,6 +1424,7 @@ void Decorator::AddHighlight( float x1, float y1, float x2, float y2 ) void Decorator::ClearHighlights() { mImpl->mHighlightQuadList.clear(); + mImpl->mHighlightPosition = Vector2::ZERO; } void Decorator::SetHighlightColor( const Vector4& color ) @@ -1204,6 +1447,16 @@ bool Decorator::IsPopupActive() const return mImpl->mActiveCopyPastePopup ; } +void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask ) +{ + mImpl->mEnabledPopupButtons = enabledButtonsBitMask; +} + +TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons() +{ + return mImpl->mEnabledPopupButtons; +} + /** Scroll **/ void Decorator::SetScrollThreshold( float threshold ) @@ -1226,15 +1479,21 @@ float Decorator::GetScrollSpeed() const return mImpl->GetScrollSpeed(); } +void Decorator::NotifyEndOfScroll() +{ + mImpl->NotifyEndOfScroll(); +} + Decorator::~Decorator() { delete mImpl; } -Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer ) +Decorator::Decorator( ControllerInterface& controller, + TextSelectionPopupCallbackInterface& callbackInterface ) : mImpl( NULL ) { - mImpl = new Decorator::Impl( parent, observer ); + mImpl = new Decorator::Impl( controller, callbackInterface ); } } // namespace Text