+
+ if( !mSecondaryCursor.GetParent() )
+ {
+ mActiveLayer.Add( mSecondaryCursor );
+ }
+ }
+ else
+ {
+ if( mSecondaryCursor )
+ {
+ mSecondaryCursor.Unparent();
+ }
+ }
+ }
+ }
+
+ bool OnCursorBlinkTimerTick()
+ {
+ if( !mDelayCursorBlink )
+ {
+ // Cursor blinking
+ if ( mPrimaryCursor )
+ {
+ mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
+ }
+ if ( mSecondaryCursor )
+ {
+ mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
+ }
+
+ mCursorBlinkStatus = !mCursorBlinkStatus;
+ }
+ else
+ {
+ // Resume blinking
+ mDelayCursorBlink = false;
+ }
+
+ return true;
+ }
+
+ void SetupTouchEvents()
+ {
+ mTapDetector = TapGestureDetector::New();
+ mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
+
+ mPanGestureDetector = PanGestureDetector::New();
+ mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
+ }
+
+ void CreateActiveLayer()
+ {
+ if( !mActiveLayer )
+ {
+ mActiveLayer = Layer::New();
+#ifdef DECORATOR_DEBUG
+ mActiveLayer.SetName ( "ActiveLayerActor" );
+#endif
+
+ mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
+ mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
+
+ // Add the active layer telling the controller it doesn't need clipping.
+ mController.AddDecoration( mActiveLayer, false );
+ }
+
+ mActiveLayer.RaiseToTop();
+ }
+
+ void SetSelectionHandleMarkerSize( HandleImpl& handle )
+ {
+ if( handle.markerActor )
+ {
+ handle.markerActor.SetSize( 0, handle.lineHeight );
+ }
+ }
+
+ void CreateGrabHandle()
+ {
+ HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ if( !grabHandle.actor )
+ {
+ if( mHandleImages[GRAB_HANDLE][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 );
+
+ // 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 = 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();
+#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.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
+ mTapDetector.Attach( grabHandle.grabArea );
+ mPanGestureDetector.Attach( grabHandle.grabArea );
+
+ mActiveLayer.Add( grabHandle.actor );
+ }
+ }
+
+ if( grabHandle.actor && !grabHandle.actor.GetParent() )
+ {
+ mActiveLayer.Add( grabHandle.actor );
+ }
+ }
+
+ void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
+ {
+ if( 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 )
+ {
+ handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
+ handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
+ }
+ else if( RIGHT_SELECTION_HANDLE == handleType )
+ {
+ handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
+ handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+ }
+ }
+ }
+
+ void CreateSelectionHandles()
+ {
+ HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
+ if( !primary.actor )
+ {
+ 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");
+#endif
+ 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
+#ifdef DECORATOR_DEBUG
+ 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 );
+
+ mTapDetector.Attach( primary.grabArea );
+ mPanGestureDetector.Attach( primary.grabArea );
+ primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
+
+ primary.actor.Add( primary.grabArea );
+
+ CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
+ }
+ }
+
+ if( primary.actor && !primary.actor.GetParent() )
+ {
+ mActiveLayer.Add( primary.actor );
+ }
+
+ HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
+ if( !secondary.actor )
+ {
+ 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");
+#endif
+ 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
+#ifdef DECORATOR_DEBUG
+ 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 );
+
+ mTapDetector.Attach( secondary.grabArea );
+ mPanGestureDetector.Attach( secondary.grabArea );
+ secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
+
+ secondary.actor.Add( secondary.grabArea );
+
+ CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
+ }
+ }
+
+ if( secondary.actor && !secondary.actor.GetParent() )
+ {
+ mActiveLayer.Add( secondary.actor );
+ }
+ }
+
+ void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
+ {
+ // Gets the world position of the active layer. The active layer is where the handles are added.
+ const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
+
+ // The grab handle position in world coords.
+ // 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()
+ {
+ // Reference to the grab handle.
+ HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+
+ // 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 = ( 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,
+ // set the 'y' position to the top of the line.
+ // The SetGrabHandleImage() method will change the orientation.
+ const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
+
+ if( grabHandle.actor )
+ {
+ grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
+ yLocalPosition ); // TODO : Fix for multiline.
+ }
+ }
+
+ void SetSelectionHandlePosition( HandleType type )
+ {
+ const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
+
+ // Reference to the selection handle.
+ HandleImpl& handle = mHandle[type];
+
+ // 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 (horizontally).
+ bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
+
+ // Whether to flip the handles if they are crossed.
+ bool crossFlip = false;
+ if( mFlipSelectionHandlesOnCross || !mHandlePanning )
+ {
+ crossFlip = mHandleCurrentCrossed;
+ }
+
+ // 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)
+ flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
+
+ if( flipHandle )
+ {
+ if( handle.actor && !handle.horizontallyFlipped )
+ {
+ // Change the anchor point to flip the image.
+ handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
+
+ handle.horizontallyFlipped = true;
+ }
+ }
+ else
+ {
+ if( handle.actor && handle.horizontallyFlipped )
+ {
+ // Reset the anchor point.
+ handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
+
+ handle.horizontallyFlipped = false;
+ }
+ }
+
+ // Whether to flip the handle vertically.
+ 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,
+ // set the 'y' position to the top of the line.
+ // The SetHandleImage() method will change the orientation.
+ const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
+
+ if( handle.actor )
+ {
+ handle.actor.SetPosition( handle.position.x,
+ yLocalPosition ); // TODO : Fix for multiline.
+ }
+ }
+
+ void SetHandleImage( HandleType type )
+ {
+ HandleImpl& handle = mHandle[type];
+
+ HandleType markerType = HANDLE_TYPE_COUNT;
+ // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
+ if( LEFT_SELECTION_HANDLE == type )
+ {
+ type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
+ markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
+ }
+ else if( RIGHT_SELECTION_HANDLE == type )
+ {
+ type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
+ markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
+ }
+
+ // Chooses between the released or pressed image. It checks whether the pressed image exists.
+ 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] );
+ }
+
+ if( HANDLE_TYPE_COUNT != markerType )
+ {
+ 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.
+ if( handle.actor )
+ {
+ handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
+ }
+ }
+
+ void CreateHighlight()
+ {
+ if( !mHighlightActor )
+ {
+ mHighlightActor = Actor::New();
+
+#ifdef DECORATOR_DEBUG
+ mHighlightActor.SetName( "HighlightActor" );
+#endif
+ mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+ 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()
+ {
+ if ( mHighlightActor )
+ {
+ if( !mHighlightQuadList.empty() )
+ {
+ Vector< Vector2 > vertices;
+ Vector< unsigned int> indices;
+ Vector2 vertex;
+
+ std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
+ std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
+
+ for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
+ {
+ QuadCoordinates& quad = *iter;
+
+ // top-left (v+0)
+ vertex.x = quad.min.x;
+ vertex.y = quad.min.y;
+ vertices.PushBack( vertex );
+
+ // top-right (v+1)
+ vertex.x = quad.max.x;
+ vertex.y = quad.min.y;
+ vertices.PushBack( vertex );
+
+ // bottom-left (v+2)
+ vertex.x = quad.min.x;
+ vertex.y = quad.max.y;
+ vertices.PushBack( vertex );
+
+ // bottom-right (v+3)
+ vertex.x = quad.max.x;
+ vertex.y = quad.max.y;
+ vertices.PushBack( vertex );
+
+ // triangle A (3, 1, 0)
+ indices.PushBack( v + 3 );
+ indices.PushBack( v + 1 );
+ indices.PushBack( v );
+
+ // triangle B (0, 2, 3)
+ indices.PushBack( v );
+ indices.PushBack( v + 2 );
+ indices.PushBack( v + 3 );
+ }
+
+ if( ! mQuadVertices )
+ {
+ mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
+ }
+
+ if( ! mQuadIndices )
+ {
+ mQuadIndices = PropertyBuffer::New( mQuadIndexFormat );
+ }
+
+ mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
+ mQuadIndices.SetData( &indices[ 0 ], indices.Size() );
+
+ if( !mQuadGeometry )
+ {
+ mQuadGeometry = Geometry::New();
+ mQuadGeometry.AddVertexBuffer( mQuadVertices );
+ }
+ mQuadGeometry.SetIndexBuffer( mQuadIndices );
+
+ if( !mHighlightRenderer )
+ {
+ mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
+ mHighlightActor.AddRenderer( mHighlightRenderer );
+ }
+ }
+
+ mHighlightActor.SetPosition( mHighlightPosition.x,
+ mHighlightPosition.y );
+
+ mHighlightQuadList.clear();
+
+ if( mHighlightRenderer )
+ {
+ mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
+ }
+ }
+ }
+
+ void OnTap( Actor actor, const TapGesture& tap )
+ {
+ if( actor == mHandle[GRAB_HANDLE].actor )
+ {
+ // TODO
+ }
+ }
+
+ void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
+ {
+ if( Gesture::Started == gesture.state )
+ {
+ handle.grabDisplacementX = handle.grabDisplacementY = 0;
+ }
+
+ handle.grabDisplacementX += gesture.displacement.x;
+ handle.grabDisplacementY += gesture.displacement.y;
+
+ const float x = handle.position.x + handle.grabDisplacementX;
+ const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
+
+ 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 > targetSize.width - mScrollThreshold )
+ {
+ mScrollDirection = SCROLL_LEFT;
+ mHandleScrolling = type;
+ StartScrollTimer();
+ }
+ else
+ {
+ mHandleScrolling = HANDLE_TYPE_COUNT;
+ StopScrollTimer();
+ mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
+ }
+
+ mHandlePanning = true;
+ }
+ else if( Gesture::Finished == gesture.state ||
+ Gesture::Cancelled == gesture.state )
+ {
+ if( mScrollTimer &&
+ ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
+ {
+ mNotifyEndOfScroll = false;
+ mHandleScrolling = HANDLE_TYPE_COUNT;
+ StopScrollTimer();
+ mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
+ }
+ else
+ {
+ mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
+ }
+
+ if( handle.actor )
+ {
+ handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );