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