#include <dali/public-api/adaptor-framework/timer.h>
#include <dali/public-api/actors/image-actor.h>
#include <dali/public-api/actors/layer.h>
+#include <dali/public-api/actors/mesh-actor.h>
#include <dali/public-api/common/constants.h>
#include <dali/public-api/events/tap-gesture.h>
#include <dali/public-api/events/tap-gesture-detector.h>
#include <dali/public-api/events/pan-gesture.h>
#include <dali/public-api/events/pan-gesture-detector.h>
+#include <dali/public-api/geometry/mesh.h>
+#include <dali/public-api/geometry/mesh-data.h>
#include <dali/public-api/images/resource-image.h>
#include <dali/public-api/math/vector2.h>
#include <dali/public-api/math/vector4.h>
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<QuadCoordinates> QuadContainer;
+
} // end of namespace
namespace Dali
CursorImpl()
: x(0.0f),
y(0.0f),
- height(0.0f),
+ cursorHeight(0.0f),
+ lineHeight(0.0f),
color(Dali::Color::WHITE)
{
}
float x;
float y;
- float height;
+ float cursorHeight;
+ float lineHeight;
Vector4 color;
};
SelectionHandleImpl()
: x(0.0f),
y(0.0f),
- cursorHeight(0.0f),
+ lineHeight(0.0f),
flipped(false)
{
}
float x;
float y;
- float cursorHeight; ///< Not the handle height
+ float lineHeight; ///< Not the handle height
bool flipped;
ImageActor actor;
mCursorBlinkStatus( true ),
mGrabDisplacementX( 0.0f ),
mGrabDisplacementY( 0.0f ),
+ mHighlightColor( 0.07f, 0.41f, 0.59f, 1.0f ), // light blue
mBoundingBox( Rect<int>() )
{
}
{
mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x,
mCursor[PRIMARY_CURSOR].y + scrollPosition.y );
- mPrimaryCursor.SetSize( 1.0f, mCursor[PRIMARY_CURSOR].height );
+ mPrimaryCursor.SetSize( Size( 1.0f, mCursor[PRIMARY_CURSOR].cursorHeight ) );
}
if( mSecondaryCursor )
{
mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x + scrollPosition.x,
mCursor[SECONDARY_CURSOR].y + scrollPosition.y );
- mSecondaryCursor.SetSize( 1.0f, mCursor[SECONDARY_CURSOR].height );
+ mSecondaryCursor.SetSize( Size( 1.0f, mCursor[SECONDARY_CURSOR].cursorHeight ) );
}
// Show or hide the grab handle
CreateGrabHandle();
mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x,
- mCursor[PRIMARY_CURSOR].y + scrollPosition.y + mCursor[PRIMARY_CURSOR].height );
+ mCursor[PRIMARY_CURSOR].lineHeight + scrollPosition.y );
}
else if( mGrabHandle )
{
SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
primary.actor.SetPosition( primary.x + scrollPosition.x,
- primary.y + scrollPosition.y + primary.cursorHeight );
+ primary.lineHeight + scrollPosition.y );
SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
secondary.actor.SetPosition( secondary.x + scrollPosition.x,
- secondary.y + scrollPosition.y + secondary.cursorHeight );
+ secondary.lineHeight + scrollPosition.y );
- //CreateHighlight(); TODO
+ CreateHighlight();
+ UpdateHighlight();
}
else
{
UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
+ UnparentAndReset( mHighlightMeshActor );
}
if ( mActiveCopyPastePopup )
void CreateCursor( ImageActor& cursor )
{
cursor = CreateSolidColorActor( Color::WHITE );
- cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+ cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
+ cursor.SetRelayoutEnabled( false );
}
// Add or Remove cursor(s) from parent
mActiveLayer.Add( mSecondaryCursor);
}
}
+ else
+ {
+ UnparentAndReset( mSecondaryCursor );
+ }
}
}
mActiveLayer.SetName ( "ActiveLayerActor" );
#endif
- mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
- mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
- mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
+ mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
+ mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
parent.Add( mActiveLayer );
#ifdef DECORATOR_DEBUG
mGrabHandle.SetName( "GrabHandleActor" );
#endif
- mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
-
// Area that Grab handle responds to, larger than actual handle so easier to move
#ifdef DECORATOR_DEBUG
- mGrabArea = Toolkit::CreateSolidColorActor( Vector4(1.0f, 0.0f, 0.0f, 0.5f) );
- mGrabArea.SetName( "GrabArea" );
+ mGrabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
+ mGrabArea.SetName( "GrabArea" );
#else
mGrabArea = Actor::New();
+ mGrabArea.SetRelayoutEnabled( true );
#endif
mGrabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
mGrabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
- mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+ mGrabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
- mGrabHandle.Add(mGrabArea);
+ mGrabHandle.Add( mGrabArea );
mTapDetector.Attach( mGrabArea );
mPanGestureDetector.Attach( mGrabArea );
#ifdef DECORATOR_DEBUG
primary.actor.SetName("SelectionHandleOne");
#endif
- primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
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.flipped = false;
primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
+ primary.grabArea.SetRelayoutEnabled( true );
#ifdef DECORATOR_DEBUG
primary.grabArea.SetName("SelectionHandleOneGrabArea");
#endif
- primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+ primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
#ifdef DECORATOR_DEBUG
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;
secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
+ secondary.grabArea.SetRelayoutEnabled( true );
#ifdef DECORATOR_DEBUG
secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
#endif
- secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+ secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
//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.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<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
+ std::vector<QuadCoordinates>::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 )
mGrabDisplacementY += gesture.displacement.y;
float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX;
- float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY;
+ float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].lineHeight*0.5f + mGrabDisplacementY;
if( Gesture::Started == gesture.state ||
Gesture::Continuing == gesture.state )
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;
return mImpl->mActiveCursor;
}
-void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
+void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
{
// Adjust grab handle displacement
mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x;
mImpl->mCursor[cursor].x = x;
mImpl->mCursor[cursor].y = y;
- mImpl->mCursor[cursor].height = height;
+ mImpl->mCursor[cursor].cursorHeight = cursorHeight;
+ mImpl->mCursor[cursor].lineHeight = lineHeight;
}
-void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
+void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
{
x = mImpl->mCursor[cursor].x;
y = mImpl->mCursor[cursor].y;
- height = mImpl->mCursor[cursor].height;
+ cursorHeight = mImpl->mCursor[cursor].cursorHeight;
+ lineHeight = mImpl->mCursor[cursor].lineHeight;
}
void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
{
mImpl->mSelectionHandle[handle].x = x;
mImpl->mSelectionHandle[handle].y = y;
- mImpl->mSelectionHandle[handle].cursorHeight = height;
+ mImpl->mSelectionHandle[handle].lineHeight = height;
}
void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
{
x = mImpl->mSelectionHandle[handle].x;
y = mImpl->mSelectionHandle[handle].y;
- height = mImpl->mSelectionHandle[handle].cursorHeight;
+ height = mImpl->mSelectionHandle[handle].lineHeight;
}
void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
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;