From ebbf2aed6e030f66f640d0f4c933e76e9b63ee27 Mon Sep 17 00:00:00 2001 From: Paul Wisbey Date: Tue, 24 Mar 2015 11:58:51 +0000 Subject: [PATCH] Added some Selection Highlight code to Decorator Change-Id: I1ee4e535a529e0803d07b484d950729172c788b7 --- .../internal/text/decorator/text-decorator.cpp | 177 ++++++++++++++++++++- .../internal/text/decorator/text-decorator.h | 15 ++ dali-toolkit/internal/text/text-controller.cpp | 34 +++- 3 files changed, 223 insertions(+), 3 deletions(-) diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp index d8deaf6..92b475c 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@ -23,11 +23,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -62,6 +65,39 @@ const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval const std::size_t MILLISECONDS = 1000; +const float DISPLAYED_HIGHLIGHT_Z_OFFSET( -0.05f ); + +/** + * structure to hold coordinates of each quad, which will make up the mesh. + */ +struct QuadCoordinates +{ + /** + * Default constructor + */ + QuadCoordinates() + { + } + + /** + * Constructor + * @param[in] x1 left co-ordinate + * @param[in] y1 top co-ordinate + * @param[in] x2 right co-ordinate + * @param[in] y2 bottom co-ordinate + */ + QuadCoordinates(float x1, float y1, float x2, float y2) + : min(x1, y1), + max(x2, y2) + { + } + + Dali::Vector2 min; ///< top-left (minimum) position of quad + Dali::Vector2 max; ///< bottom-right (maximum) position of quad +}; + +typedef std::vector QuadContainer; + } // end of namespace namespace Dali @@ -126,6 +162,7 @@ struct Decorator::Impl : public ConnectionTracker mCursorBlinkStatus( true ), mGrabDisplacementX( 0.0f ), mGrabDisplacementY( 0.0f ), + mHighlightColor( 0.07f, 0.41f, 0.59f, 1.0f ), // light blue mBoundingBox( Rect() ) { } @@ -184,12 +221,14 @@ struct Decorator::Impl : public ConnectionTracker secondary.actor.SetPosition( secondary.x + scrollPosition.x, secondary.y + scrollPosition.y + secondary.cursorHeight ); - //CreateHighlight(); TODO + CreateHighlight(); + UpdateHighlight(); } else { UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor ); UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor ); + UnparentAndReset( mHighlightMeshActor ); } if ( mActiveCopyPastePopup ) @@ -394,7 +433,7 @@ struct Decorator::Impl : public ConnectionTracker secondary.actor.SetName("SelectionHandleTwo"); #endif secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); - secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. + 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.flipped = false; @@ -417,6 +456,123 @@ struct Decorator::Impl : public ConnectionTracker //SetUpHandlePropertyNotifications(); TODO } + void CreateHighlight() + { + if ( !mHighlightMeshActor ) + { + mHighlightMaterial = Material::New( "HighlightMaterial" ); + mHighlightMaterial.SetDiffuseColor( mHighlightColor ); + + mHighlightMeshData.SetMaterial( mHighlightMaterial ); + mHighlightMeshData.SetHasNormals( true ); + + mHighlightMesh = Mesh::New( mHighlightMeshData ); + + mHighlightMeshActor = MeshActor::New( mHighlightMesh ); +#ifdef DECORATOR_DEBUG + mHighlightMeshActor.SetName( "HighlightMeshActor" ); +#endif + mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET ); + + Actor parent = mTextControlParent.Self(); + parent.Add( mHighlightMeshActor ); + } + } + + 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() ) + { + MeshData::VertexContainer vertices; + Dali::MeshData::FaceIndices faceIndices; + + 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 ); + + // top-right (v+1) + vertex.x = quad.max.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // bottom-left (v+2) + vertex.x = quad.min.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // bottom-right (v+3) + vertex.x = quad.max.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // triangle A (3, 1, 0) + faceIndices.push_back( v + 3 ); + faceIndices.push_back( v + 1 ); + faceIndices.push_back( v ); + + // triangle B (0, 2, 3) + faceIndices.push_back( v ); + faceIndices.push_back( v + 2 ); + faceIndices.push_back( v + 3 ); + + mHighlightMeshData.SetFaceIndices( faceIndices ); + } + + BoneContainer bones(0); // passed empty as bones not required + mHighlightMeshData.SetData( vertices, faceIndices, bones, mHighlightMaterial ); + mHighlightMesh.UpdateMeshData( mHighlightMeshData ); + } + } + void OnTap( Actor actor, const TapGesture& tap ) { if( actor == mGrabHandle ) @@ -493,6 +649,13 @@ struct Decorator::Impl : public ConnectionTracker SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT]; + MeshActor mHighlightMeshActor; ///< Mesh Actor to display highlight + Mesh mHighlightMesh; ///< Mesh for highlight + MeshData mHighlightMeshData; ///< Mesh Data for highlight + Material mHighlightMaterial; ///< Material used for highlight + Vector4 mHighlightColor; ///< Color of the highlight + QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight + TextSelectionPopup mCopyPastePopup; Image mCursorImage; @@ -676,6 +839,16 @@ Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState st return mImpl->mSelectionHandle[handle].releasedImage; } +void Decorator::AddHighlight( float x1, float y1, float x2, float y2 ) +{ + mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) ); +} + +void Decorator::ClearHighlights() +{ + mImpl->mHighlightQuadList.clear(); +} + void Decorator::SetPopupActive( bool active ) { mImpl->mActiveCopyPastePopup = active; diff --git a/dali-toolkit/internal/text/decorator/text-decorator.h b/dali-toolkit/internal/text/decorator/text-decorator.h index b7b793f..f0bfd39 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.h +++ b/dali-toolkit/internal/text/decorator/text-decorator.h @@ -342,6 +342,21 @@ public: Dali::Image GetImage( SelectionHandle handle, SelectionHandleState state ) const; /** + * @brief Adds a quad to the existing selection highlights. + * + * @param[in] x1 The top-left x position. + * @param[in] y1 The top-left y position. + * @param[in] x2 The bottom-right x position. + * @param[in] y3 The bottom-right y position. + */ + void AddHighlight( float x1, float y1, float x2, float y2 ); + + /** + * @brief Removes all of the previously added highlights. + */ + void ClearHighlights(); + + /** * @brief Set the Selection Popup to show or hide via the active flaf * @param[in] active true to show, false to hide */ diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 151539c..1a39d16 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -126,7 +126,7 @@ struct Controller::TextInput mCursorBlinkEnabled( true ), mGrabHandleEnabled( true ), mGrabHandlePopupEnabled( true ), - mSelectionEnabled( false ), + mSelectionEnabled( true ), mHorizontalScrollingEnabled( true ), mVerticalScrollingEnabled( false ), mUpdateCursorPosition( false ) @@ -254,6 +254,8 @@ struct Controller::TextInput 2u == tapCount ) { ChangeState( SELECTING ); + + RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat ); } } @@ -337,6 +339,36 @@ struct Controller::TextInput } } + void RepositionSelectionHandles( float visualX, float visualY ) + { + // TODO - Find which word was selected + + const Vector& glyphs = mVisualModel->mGlyphs; + const Vector::SizeType glyphCount = glyphs.Count(); + + const Vector& positions = mVisualModel->mGlyphPositions; + const Vector::SizeType positionCount = positions.Count(); + + // Guard against glyphs which did not fit inside the layout + const Vector::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount; + + if( count ) + { + float primaryX = positions[0].x; + float secondaryX = positions[count-1].x + glyphs[count-1].width; + + // TODO - multi-line selection + const Vector& lines = mVisualModel->mLines; + float height = lines.Count() ? lines[0].lineSize.height : 0.0f; + + mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height ); + mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height ); + + mDecorator->ClearHighlights(); + mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height ); + } + } + void ChangeState( State newState ) { if( mState != newState ) -- 2.7.4