Fix for Text Selection issues. 16/42216/4
authorVictor Cebollada <v.cebollada@samsung.com>
Wed, 24 Jun 2015 06:26:15 +0000 (07:26 +0100)
committerVíctor Cebollada <v.cebollada@samsung.com>
Wed, 24 Jun 2015 15:02:13 +0000 (08:02 -0700)
  - Do not place the left and right selection handles
    in the same cursor position.
  - Do not recalculate the handles position when the
    text stops scrolling.
  - Stop the scroll timer when the text stops scrolling.
  - Remove the unimplemented methods SetScrollTickInterval()
    and GetScrollTickInterval() from the Text::Decorator.
  - Do not scroll to the beginning of the text if the
    'Select' button is pressed and the cursor is at the end.

Change-Id: I4db3cce902943fb1378f976ae622ad702a0a6790
Signed-off-by: Victor Cebollada <v.cebollada@samsung.com>
dali-toolkit/internal/text/decorator/text-decorator.cpp
dali-toolkit/internal/text/decorator/text-decorator.h
dali-toolkit/internal/text/text-controller-impl.cpp

index 02de592..31c62c5 100644 (file)
@@ -235,6 +235,7 @@ struct Decorator::Impl : public ConnectionTracker
     mTextSelectionPopupCallbackInterface( callbackInterface ),
     mBoundingBox( Rect<int>() ),
     mHighlightColor( LIGHT_BLUE ),
+    mHighlightPosition( Vector2::ZERO ),
     mActiveCursor( ACTIVE_CURSOR_NONE ),
     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
     mCursorBlinkDuration( 0.0f ),
@@ -247,7 +248,8 @@ struct Decorator::Impl : public ConnectionTracker
     mCursorBlinkStatus( true ),
     mPrimaryCursorVisible( false ),
     mSecondaryCursorVisible( false ),
-    mSwapSelectionHandles( false )
+    mSwapSelectionHandles( false ),
+    mNotifyEndOfScroll( false )
   {
   }
 
@@ -269,17 +271,6 @@ struct Decorator::Impl : public ConnectionTracker
       if( mPrimaryCursorVisible )
       {
         Vector2 position = cursor.position;
-        if( GRAB_HANDLE == mHandleScrolling )
-        {
-          if( mScrollDirection == SCROLL_RIGHT )
-          {
-            position.x = 0.f;
-          }
-          else
-          {
-            position.x = size.width;
-          }
-        }
 
         mPrimaryCursor.SetPosition( position.x,
                                     position.y );
@@ -306,18 +297,6 @@ struct Decorator::Impl : public ConnectionTracker
     {
       Vector2 position = grabHandle.position;
 
-      if( GRAB_HANDLE == mHandleScrolling )
-      {
-        if( mScrollDirection == SCROLL_RIGHT )
-        {
-          position.x = 0.f;
-        }
-        else
-        {
-          position.x = size.width;
-        }
-      }
-
       const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f );
 
       if( isVisible )
@@ -344,29 +323,6 @@ struct Decorator::Impl : public ConnectionTracker
       Vector2 primaryPosition = primary.position;
       Vector2 secondaryPosition = secondary.position;
 
-      if( LEFT_SELECTION_HANDLE == mHandleScrolling )
-      {
-        if( mScrollDirection == SCROLL_RIGHT )
-        {
-          primaryPosition.x = 0.f;
-        }
-        else
-        {
-          primaryPosition.x = size.width;
-        }
-      }
-      else if( RIGHT_SELECTION_HANDLE == mHandleScrolling )
-      {
-        if( mScrollDirection == SCROLL_RIGHT )
-        {
-          secondaryPosition.x = 0.f;
-        }
-        else
-        {
-          secondaryPosition.x = size.width;
-        }
-      }
-
       const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
       const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
 
@@ -866,8 +822,10 @@ struct Decorator::Impl : public ConnectionTracker
     else if( Gesture::Finished  == gesture.state ||
              Gesture::Cancelled == gesture.state )
     {
-      if( mScrollTimer && mScrollTimer.IsRunning() )
+      if( mScrollTimer &&
+          ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
       {
+        mNotifyEndOfScroll = false;
         mHandleScrolling = HANDLE_TYPE_COUNT;
         StopScrollTimer();
         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
@@ -1155,6 +1113,16 @@ struct Decorator::Impl : public ConnectionTracker
     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.
    *
@@ -1228,10 +1196,10 @@ struct Decorator::Impl : public ConnectionTracker
   CursorImpl          mCursor[CURSOR_COUNT];
   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
-  Vector2             mHighlightPosition;         ///< The position of the highlight actor.
 
   Rect<int>           mBoundingBox;
   Vector4             mHighlightColor;            ///< Color of the highlight
+  Vector2             mHighlightPosition;         ///< The position of the highlight actor.
 
   unsigned int        mActiveCursor;
   unsigned int        mCursorBlinkInterval;
@@ -1241,13 +1209,13 @@ struct Decorator::Impl : public ConnectionTracker
   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.
-  unsigned int        mScrollInterval;          ///< Time in milliseconds of a scroll interval.
 
   bool                mActiveCopyPastePopup   : 1;
   bool                mCursorBlinkStatus      : 1; ///< Flag to switch between blink on and blink off.
   bool                mPrimaryCursorVisible   : 1; ///< Whether the primary cursor is visible.
   bool                mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
   bool                mSwapSelectionHandles   : 1; ///< Whether to swap the selection handle images.
+  bool                mNotifyEndOfScroll      : 1; ///< Whether to notify the end of the scroll.
 };
 
 DecoratorPtr Decorator::New( ControllerInterface& controller,
@@ -1481,6 +1449,11 @@ float Decorator::GetScrollSpeed() const
   return mImpl->GetScrollSpeed();
 }
 
+void Decorator::NotifyEndOfScroll()
+{
+  mImpl->NotifyEndOfScroll();
+}
+
 Decorator::~Decorator()
 {
   delete mImpl;
index 893d981..f38c467 100644 (file)
@@ -467,18 +467,9 @@ public:
   float GetScrollSpeed() const;
 
   /**
-   * @brief Sets the scroll interval.
-   *
-   * @param[in] seconds The scroll interval in seconds.
-   */
-  void SetScrollTickInterval( float seconds );
-
-  /**
-   * @brief Retrieves the scroll interval.
-   *
-   * @return The scroll interval.
+   * @brief Notifies the decorator the whole text has been scrolled.
    */
-  float GetScrollTickInterval() const;
+  void NotifyEndOfScroll();
 
 protected:
 
index f05ffda..982b8d1 100644 (file)
@@ -569,7 +569,8 @@ void Controller::Impl::OnHandleEvent( const Event& event )
     {
       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
 
-      if( handleNewPosition != mEventData->mLeftSelectionPosition )
+      if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
+          ( handleNewPosition != mEventData->mRightSelectionPosition ) )
       {
         mEventData->mLeftSelectionPosition = handleNewPosition;
 
@@ -583,7 +584,8 @@ void Controller::Impl::OnHandleEvent( const Event& event )
     {
       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
 
-      if( handleNewPosition != mEventData->mRightSelectionPosition )
+      if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
+          ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
       {
         mEventData->mRightSelectionPosition = handleNewPosition;
 
@@ -625,12 +627,13 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       if( handleStopScrolling )
       {
-        mEventData->mUpdateLeftSelectionPosition = mEventData->mLeftSelectionPosition != handlePosition;
+        mEventData->mUpdateLeftSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition);
         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
-        mEventData->mLeftSelectionPosition = handlePosition;
 
         if( mEventData->mUpdateLeftSelectionPosition )
         {
+          mEventData->mLeftSelectionPosition = handlePosition;
+
           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
                                       mEventData->mRightSelectionPosition );
         }
@@ -642,12 +645,12 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
       if( handleStopScrolling )
       {
-        mEventData->mUpdateRightSelectionPosition = mEventData->mRightSelectionPosition != handlePosition;
+        mEventData->mUpdateRightSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
-        mEventData->mRightSelectionPosition = handlePosition;
 
         if( mEventData->mUpdateRightSelectionPosition )
         {
+          mEventData->mRightSelectionPosition = handlePosition;
           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
                                       mEventData->mRightSelectionPosition );
         }
@@ -660,50 +663,80 @@ void Controller::Impl::OnHandleEvent( const Event& event )
   {
     const float xSpeed = event.p2.mFloat;
     const Vector2& actualSize = mVisualModel->GetActualSize();
+    const Vector2 currentScrollPosition = mEventData->mScrollPosition;
 
     mEventData->mScrollPosition.x += xSpeed;
 
     ClampHorizontalScroll( actualSize );
 
-    const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
-    const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
-
-    if( Event::GRAB_HANDLE_EVENT == event.type )
+    if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
     {
-      ChangeState( EventData::GRAB_HANDLE_PANNING );
+      // Notify the decorator there is no more text to scroll.
+      // The decorator won't send more scroll events.
+      mEventData->mDecorator->NotifyEndOfScroll();
     }
-    else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
+    else
     {
-      // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
-      //       Think if something can be done to save power.
+      const bool scrollRightDirection = xSpeed > 0.f;
+      const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
+      const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
 
-      ChangeState( EventData::SELECTION_HANDLE_PANNING );
+      if( Event::GRAB_HANDLE_EVENT == event.type )
+      {
+        ChangeState( EventData::GRAB_HANDLE_PANNING );
 
-      const Vector2& position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
+        Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
 
-      // Get the new handle position.
-      // The selection handle's position is in decorator coords. Need to transforms to text coords.
-      const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
-                                                                   position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+        // Position the grag handle close to either the left or right edge.
+        position.x = scrollRightDirection ? 0.f : mControlSize.width;
 
-      if( leftSelectionHandleEvent )
-      {
-        mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
-        mEventData->mLeftSelectionPosition = handlePosition;
+        // Get the new handle position.
+        // The grab handle's position is in decorator coords. Need to transforms to text coords.
+        const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
+                                                                     position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+
+        mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
+        mEventData->mPrimaryCursorPosition = handlePosition;
       }
-      else
+      else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
       {
-        mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
-        mEventData->mRightSelectionPosition = handlePosition;
-      }
+        // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
+        //       Think if something can be done to save power.
 
-      if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
-      {
-        RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
-                                    mEventData->mRightSelectionPosition );
+        ChangeState( EventData::SELECTION_HANDLE_PANNING );
+
+        Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
+
+        // Position the selection handle close to either the left or right edge.
+        position.x = scrollRightDirection ? 0.f : mControlSize.width;
+
+        // Get the new handle position.
+        // The selection handle's position is in decorator coords. Need to transforms to text coords.
+        const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
+                                                                     position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+
+        if( leftSelectionHandleEvent )
+        {
+          mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
+          mEventData->mLeftSelectionPosition = handlePosition;
+        }
+        else
+        {
+          mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
+          mEventData->mRightSelectionPosition = handlePosition;
+        }
+
+        if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
+        {
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+
+          mEventData->mScrollAfterUpdatePosition = true;
+        }
       }
+      mEventData->mDecoratorUpdated = true;
     }
-    mEventData->mDecoratorUpdated = true;
   } // end ( HANDLE_SCROLLING == state )
 }
 
@@ -721,12 +754,17 @@ void Controller::Impl::OnSelectEvent( const Event& event )
     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
 
+    const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
+    const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
+
     RepositionSelectionHandles( xPosition,
                                 yPosition );
 
-    mEventData->mScrollAfterUpdatePosition = true;
-    mEventData->mUpdateLeftSelectionPosition = true;
-    mEventData->mUpdateRightSelectionPosition = true;
+    mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
+    mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
+
+    mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
+                                               ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
   }
 }