+ 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, mHighlightMaterial );
+ 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] );
+ }
+ handle.pressed = false;
+
+ mHandlePanning = false;
+ }
+ }
+
+ void OnPan( Actor actor, const PanGesture& gesture )
+ {
+ HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
+ HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
+
+ if( actor == grabHandle.grabArea )
+ {
+ DoPan( grabHandle, GRAB_HANDLE, gesture );
+ }
+ else if( actor == primarySelectionHandle.grabArea )
+ {
+ DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
+ }
+ else if( actor == secondarySelectionHandle.grabArea )
+ {
+ DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
+ }
+ }
+
+ 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;
+ }
+ else if( ( TouchPoint::Up == point.state ) ||
+ ( TouchPoint::Interrupted == point.state ) )
+ {
+ mHandle[GRAB_HANDLE].pressed = false;
+ }
+
+ SetHandleImage( GRAB_HANDLE );
+ }
+
+ // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
+ return true;
+ }
+
+ bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
+ {
+ // Switch between pressed/release selection handle images
+ if( event.GetPointCount() > 0 &&
+ mHandle[LEFT_SELECTION_HANDLE].actor )
+ {
+ const TouchPoint& point = event.GetPoint(0);
+
+ if( TouchPoint::Down == point.state )
+ {
+ mHandle[LEFT_SELECTION_HANDLE].pressed = true;
+ }
+ else if( ( TouchPoint::Up == point.state ) ||
+ ( TouchPoint::Interrupted == point.state ) )
+ {
+ mHandle[LEFT_SELECTION_HANDLE].pressed = false;
+ mHandlePreviousCrossed = mHandleCurrentCrossed;
+ mHandlePanning = false;
+ }
+
+ SetHandleImage( LEFT_SELECTION_HANDLE );
+ }
+
+ // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
+ return true;
+ }
+
+ bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
+ {
+ // Switch between pressed/release selection handle images
+ if( event.GetPointCount() > 0 &&
+ mHandle[RIGHT_SELECTION_HANDLE].actor )
+ {
+ const TouchPoint& point = event.GetPoint(0);
+
+ if( TouchPoint::Down == point.state )
+ {
+ mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
+ }
+ else if( ( TouchPoint::Up == point.state ) ||
+ ( TouchPoint::Interrupted == point.state ) )
+ {
+ mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
+ mHandlePreviousCrossed = mHandleCurrentCrossed;
+ mHandlePanning = false;
+ }
+
+ SetHandleImage( RIGHT_SELECTION_HANDLE );
+ }
+
+ // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
+ return true;
+ }
+
+ void HandleResetPosition( PropertyNotification& source )
+ {
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+
+ if( grabHandle.active )
+ {
+ // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetGrabHandlePosition();
+
+ // Sets the grab handle image according if it's pressed, flipped, etc.
+ SetHandleImage( GRAB_HANDLE );
+ }
+ else
+ {
+ // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
+
+ // Sets the primary handle image according if it's pressed, flipped, etc.
+ SetHandleImage( LEFT_SELECTION_HANDLE );
+
+ // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
+
+ // Sets the secondary handle image according if it's pressed, flipped, etc.
+ SetHandleImage( RIGHT_SELECTION_HANDLE );
+ }
+ }
+
+ void SetupActiveLayerPropertyNotifications()
+ {
+ if( !mActiveLayer )
+ {
+ return;
+ }
+
+ // Vertical notifications.
+
+ // Disconnect any previous connected callback.
+ if( mVerticalLessThanNotification )
+ {
+ mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
+ }
+
+ if( mVerticalGreaterThanNotification )
+ {
+ mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
+ }
+
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
+ const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
+
+ if( grabHandle.active )
+ {
+ if( grabHandle.verticallyFlipped )
+ {
+ // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
+ mVerticalGreaterThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else
+ {
+ // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
+ mVerticalLessThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ }
+ else // The selection handles are active
+ {
+ if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
+ {
+ // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
+ mVerticalGreaterThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
+ {
+ // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
+ mVerticalLessThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
+ secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else
+ {
+ // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
+ -primaryHandle.position.y + primaryHandle.size.height :
+ -secondaryHandle.position.y + secondaryHandle.size.height );
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
+ secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
+ primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ }
+
+ // Horizontal notifications.
+
+ // Disconnect any previous connected callback.
+ if( mHorizontalLessThanNotification )
+ {
+ mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
+ }
+
+ if( mHorizontalGreaterThanNotification )
+ {
+ mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
+ }
+
+ if( primaryHandle.active || secondaryHandle.active )
+ {
+ // The horizontal distance from the center of the active layer to the left edje of the display.
+ const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
+ -secondaryHandle.position.x + secondaryHandle.size.width );
+
+ mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
+ LessThanCondition( mBoundingBox.x + leftWidth ) );
+
+ // Notifies the change from false to true and from true to false.
+ mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+
+ // The horizontal distance from the center of the active layer to the right edje of the display.
+ const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
+ secondaryHandle.position.x + secondaryHandle.size.width );
+
+ mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
+ GreaterThanCondition( mBoundingBox.z - rightWidth ) );
+
+ // Notifies the change from false to true and from true to false.
+ mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ }
+
+ // Popup
+
+ float AlternatePopUpPositionRelativeToCursor()
+ {
+ float alternativePosition = 0.0f;
+
+ const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
+
+ const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
+ const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
+
+ if( primaryHandle.active || secondaryHandle.active )
+ {
+ const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
+ alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
+ }
+ else
+ {
+ alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
+ }
+
+ return alternativePosition;
+ }
+
+ void PopUpLeavesVerticalBoundary( PropertyNotification& source )
+ {
+ float alternativeYPosition = 0.0f;
+ // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
+ // if can't be positioned above, then position below row.
+ alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
+
+ mCopyPastePopup.actor.SetY( alternativeYPosition );
+ }
+
+ void SetUpPopupPositionNotifications()
+ {
+ // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
+
+ // Exceeding vertical boundary
+
+ const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
+
+ PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
+ mBoundingBox.w - popupHeight * 0.5f ) );
+
+ verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
+ }
+
+ void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
+ {
+ DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
+
+ // Parent must already by added to Stage for these Get calls to work
+ const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
+ const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
+
+ // Calculate distance to move popup (in local space) so fits within the boundary
+ float xOffSetToKeepWithinBounds = 0.0f;
+ if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
+ {
+ xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
+ }
+ else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
+ {
+ xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
+ }
+
+ // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
+ if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
+ {
+ requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
+ }
+
+ requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
+
+ // Prevent pixel mis-alignment by rounding down.
+ requiredPopupPosition.x = floor( requiredPopupPosition.x );
+ requiredPopupPosition.y = floor( requiredPopupPosition.y );
+ }
+
+ void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
+ {
+ HandleImpl& handle = mHandle[handleType];
+ handle.size = Size( image.GetWidth(), image.GetHeight() );
+
+ mHandleImages[handleType][handleImageType] = image;
+ }
+
+ void SetScrollThreshold( float threshold )
+ {
+ mScrollThreshold = threshold;
+ }
+
+ float GetScrollThreshold() const
+ {
+ return mScrollThreshold;
+ }
+
+ void SetScrollSpeed( float speed )
+ {
+ mScrollSpeed = speed;
+ mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
+ }
+
+ float GetScrollSpeed() const
+ {
+ 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.
+ *
+ * It only starts the timer if it's already created.
+ */
+ void StartScrollTimer()
+ {
+ if( !mScrollTimer )
+ {
+ mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
+ mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
+ }
+
+ if( !mScrollTimer.IsRunning() )
+ {
+ mScrollTimer.Start();
+ }
+ }
+
+ /**
+ * Stops the timer used to scroll the text.
+ */
+ void StopScrollTimer()
+ {
+ if( mScrollTimer )
+ {
+ mScrollTimer.Stop();
+ }
+ }
+
+ /**
+ * Callback called by the timer used to scroll the text.
+ *
+ * It calculates and sets a new scroll position.
+ */
+ bool OnScrollTimerTick()
+ {
+ if( HANDLE_TYPE_COUNT != mHandleScrolling )
+ {
+ mController.DecorationEvent( mHandleScrolling,
+ HANDLE_SCROLLING,
+ mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
+ 0.f );
+ }
+
+ return true;
+ }
+
+ ControllerInterface& mController;
+
+ TapGestureDetector mTapDetector;
+ PanGestureDetector mPanGestureDetector;
+ Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
+ Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
+
+ Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
+ PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
+ PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
+ PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
+ PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
+ Control mPrimaryCursor;
+ Control mSecondaryCursor;
+
+ 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];
+ Vector4 mHandleColor;
+
+ CursorImpl mCursor[CURSOR_COUNT];
+ HandleImpl mHandle[HANDLE_TYPE_COUNT];
+
+ PropertyBuffer mQuadVertices;
+ PropertyBuffer mQuadIndices;
+ Geometry mQuadGeometry;
+ QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
+
+ Vector4 mBoundingBox; ///< The bounding box in world coords.
+ Vector4 mHighlightColor; ///< Color of the highlight
+ Vector2 mHighlightPosition; ///< The position of the highlight actor.
+ Vector2 mControlSize; ///< The control's size. Set by the Relayout.
+
+ unsigned int mActiveCursor;
+ unsigned int mCursorBlinkInterval;
+ float mCursorBlinkDuration;
+ float mCursorWidth; ///< The width of the cursors in pixels.
+ HandleType mHandleScrolling; ///< The handle which is scrolling.
+ ScrollDirection mScrollDirection; ///< The direction of the scroll.
+ 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.
+ int mTextDepth; ///< The depth used to render the text.
+
+ bool mActiveCopyPastePopup : 1;
+ bool mPopupSetNewPosition : 1;
+ bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
+ bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
+ bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
+ bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
+ bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
+ bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
+ bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
+ bool mHandlePanning : 1; ///< Whether any of the handles is moving.
+ bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
+ bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
+ bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
+};
+
+DecoratorPtr Decorator::New( ControllerInterface& controller,
+ TextSelectionPopupCallbackInterface& callbackInterface )
+{
+ return DecoratorPtr( new Decorator( controller,
+ callbackInterface ) );
+}
+
+void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
+{
+ LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
+}
+
+void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const