Selection implementation. 88/40988/8
authorPaul Wisbey <p.wisbey@samsung.com>
Wed, 10 Jun 2015 14:24:11 +0000 (15:24 +0100)
committerVictor Cebollada <v.cebollada@samsung.com>
Wed, 17 Jun 2015 16:07:54 +0000 (17:07 +0100)
Change-Id: I06668e73f1cf1fba9c2dbf100b23a3668511fae0

dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/text/decorator/text-decorator.cpp
dali-toolkit/internal/text/decorator/text-decorator.h
dali-toolkit/internal/text/text-control-interface.h
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h

index 6f868e6..305d61c 100644 (file)
@@ -960,6 +960,24 @@ void TextField::OnRelayout( const Vector2& size, RelayoutContainer& container )
         Self().Add( mRenderableActor );
       }
     }
+
+    for( std::vector<Actor>::const_iterator it = mClippingDecorationActors.begin(),
+           endIt = mClippingDecorationActors.end();
+         it != endIt;
+         ++it )
+    {
+      Actor actor = *it;
+
+      if( mClipper )
+      {
+        mClipper->GetRootActor().Add( actor );
+      }
+      else
+      {
+        Self().Add( actor );
+      }
+    }
+    mClippingDecorationActors.clear();
   }
 }
 
@@ -1045,11 +1063,18 @@ bool TextField::OnKeyEvent( const KeyEvent& event )
   return mController->KeyEvent( event );
 }
 
-void TextField::AddDecoration( Actor& actor )
+void TextField::AddDecoration( Actor& actor, bool needsClipping )
 {
   if( actor )
   {
-    Self().Add( actor );
+    if( needsClipping )
+    {
+      mClippingDecorationActors.push_back( actor );
+    }
+    else
+    {
+      Self().Add( actor );
+    }
   }
 }
 
index 0e53053..876972d 100644 (file)
@@ -144,7 +144,7 @@ private: // From Control
   /**
    * @copydoc Text::ControlInterface::AddDecoration()
    */
-  virtual void AddDecoration( Actor& actor );
+  virtual void AddDecoration( Actor& actor, bool needsClipping );
 
   /**
    * @copydoc Text::ControlInterface::RequestTextRelayout()
@@ -215,6 +215,7 @@ private: // Data
   Text::RendererPtr mRenderer;
   Text::DecoratorPtr mDecorator;
   Text::ClipperPtr mClipper; ///< For EXCEED_POLICY_CLIP
+  std::vector<Actor> mClippingDecorationActors;   ///< Decoration actors which need clipping.
 
   RenderableActor mRenderableActor;
 
index de713ac..77da8f9 100644 (file)
@@ -503,7 +503,7 @@ void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container )
   }
 }
 
-void TextLabel::AddDecoration( Actor& actor )
+void TextLabel::AddDecoration( Actor& actor, bool needsClipping )
 {
   // TextLabel does not show decorations
 }
index 12dc56e..e45797a 100644 (file)
@@ -95,7 +95,7 @@ private: // From Control
   /**
    * @copydoc Text::ControlInterface::AddDecoration()
    */
-  virtual void AddDecoration( Actor& actor );
+  virtual void AddDecoration( Actor& actor, bool needsClipping );
 
   /**
    * @copydoc Text::ControlInterface::RequestTextRelayout()
index 2961c52..b2fcda0 100644 (file)
@@ -81,7 +81,7 @@ const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-h
 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
 
-const Dali::Vector4 LIGHT_BLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // The text highlight color.
+const Dali::Vector4 LIGHT_BLUE( (0xb2 / 255.0f), (0xeb / 255.0f), (0xf2 / 255.0f), 0.5f ); // The text highlight color.
 
 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
 const float TO_MILLISECONDS = 1000.f;
@@ -224,7 +224,8 @@ struct Decorator::Impl : public ConnectionTracker
     mActiveCopyPastePopup( false ),
     mCursorBlinkStatus( true ),
     mPrimaryCursorVisible( false ),
-    mSecondaryCursorVisible( false )
+    mSecondaryCursorVisible( false ),
+    mSwapSelectionHandles( false )
   {
   }
 
@@ -318,15 +319,65 @@ struct Decorator::Impl : public ConnectionTracker
     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
     if( primary.active || secondary.active )
     {
-      SetupTouchEvents();
+      Vector2 primaryPosition = primary.position;
+      Vector2 secondaryPosition = secondary.position;
 
-      CreateSelectionHandles();
+      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 );
+
+      if( isPrimaryVisible || isSecondaryVisible )
+      {
+        SetupTouchEvents();
+
+        CreateSelectionHandles();
+
+        if( isPrimaryVisible )
+        {
+          primary.actor.SetPosition( primaryPosition.x,
+                                     primaryPosition.y + primary.lineHeight );
+
+          const bool flip = mSwapSelectionHandles ^ primary.flipped;
+          primary.actor.SetImage( flip ? mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
 
-      primary.actor.SetPosition( primary.position.x,
-                                 primary.position.y + primary.lineHeight );
+          primary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
+        }
+
+        if( isSecondaryVisible )
+        {
+          secondary.actor.SetPosition( secondaryPosition.x,
+                                       secondaryPosition.y + secondary.lineHeight );
 
-      secondary.actor.SetPosition( secondary.position.x,
-                                   secondary.position.y + secondary.lineHeight );
+          const bool flip = mSwapSelectionHandles ^ secondary.flipped;
+
+          secondary.actor.SetImage( ( mSwapSelectionHandles ^ secondary.flipped ) ? mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
+          secondary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
+        }
+      }
+      primary.actor.SetVisible( isPrimaryVisible );
+      secondary.actor.SetVisible( isSecondaryVisible );
 
       CreateHighlight();
       UpdateHighlight();
@@ -367,8 +418,7 @@ struct Decorator::Impl : public ConnectionTracker
     mHandle[ GRAB_HANDLE ].position += scrollOffset;
     mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
     mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
-
-    // TODO Highlight box??
+    mHighlightPosition += scrollOffset;
   }
 
   void PopUpRelayoutComplete( Actor actor )
@@ -482,7 +532,8 @@ struct Decorator::Impl : public ConnectionTracker
       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
 
-      mController.AddDecoration( mActiveLayer );
+      // Add the active layer telling the controller it doesn't need clipping.
+      mController.AddDecoration( mActiveLayer, false );
     }
 
     mActiveLayer.RaiseToTop();
@@ -620,10 +671,12 @@ struct Decorator::Impl : public ConnectionTracker
       mHighlightMeshActor.SetName( "HighlightMeshActor" );
 #endif
       mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
-      mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
 
-      mController.AddDecoration( mHighlightMeshActor );
+      // Add the highlight box telling the controller it needs clipping.
+      mController.AddDecoration( mHighlightMeshActor, true );
     }
+
+    mHighlightMeshActor.SetPosition( mHighlightPosition.x, mHighlightPosition.y, DISPLAYED_HIGHLIGHT_Z_OFFSET );
   }
 
   void UpdateHighlight()
@@ -992,9 +1045,9 @@ struct Decorator::Impl : public ConnectionTracker
     if( HANDLE_TYPE_COUNT != mHandleScrolling )
     {
       mController.DecorationEvent( mHandleScrolling,
-                               HANDLE_SCROLLING,
-                               mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
-                               0.f );
+                                   HANDLE_SCROLLING,
+                                   mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
+                                   0.f );
     }
 
     return true;
@@ -1022,6 +1075,7 @@ 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
@@ -1040,6 +1094,7 @@ struct Decorator::Impl : public ConnectionTracker
   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.
 };
 
 DecoratorPtr Decorator::New( ControllerInterface& controller )
@@ -1196,6 +1251,16 @@ void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& h
   height = handle.lineHeight;
 }
 
+const Vector2& Decorator::GetPosition( HandleType handleType ) const
+{
+  return mImpl->mHandle[handleType].position;
+}
+
+void Decorator::SwapSelectionHandlesEnabled( bool enable )
+{
+  mImpl->mSwapSelectionHandles = enable;
+}
+
 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
 {
   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
@@ -1204,6 +1269,7 @@ void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
 void Decorator::ClearHighlights()
 {
   mImpl->mHighlightQuadList.clear();
+  mImpl->mHighlightPosition = Vector2::ZERO;
 }
 
 void Decorator::SetHighlightColor( const Vector4& color )
index b271b7b..873bdcc 100644 (file)
@@ -132,7 +132,7 @@ public:
      *
      * @param[in] decoration The actor displaying a decoration.
      */
-    virtual void AddDecoration( Actor& actor ) = 0;
+    virtual void AddDecoration( Actor& actor, bool needsClipping ) = 0;
 
     /**
      * @brief An input event from one of the handles.
@@ -355,6 +355,23 @@ public:
   void GetPosition( HandleType handleType, float& x, float& y, float& lineHeight ) const;
 
   /**
+   * @brief Retrieves the position of a selection handle.
+   *
+   * @param[in] handleType The handle to get.
+   *
+   * @return The position of the selection handle relative to the top-left of the parent control.
+   */
+  const Vector2& GetPosition( HandleType handleType ) const;
+
+  /**
+   * @brief Swaps the selection handle's images.
+   *
+   * This method is called by the text controller to swap the handles
+   * when the start index is bigger than the end one.
+   */
+  void SwapSelectionHandlesEnabled( bool enable );
+
+  /**
    * @brief Adds a quad to the existing selection highlights.
    *
    * @param[in] x1 The top-left x position.
index 4d74def..ede1531 100644 (file)
@@ -50,8 +50,9 @@ public:
    * @brief Add a decoration.
    *
    * @param[in] decoration The actor displaying a decoration.
+   * @param[in] needsClipping Whether the actor needs clipping.
    */
-  virtual void AddDecoration( Actor& actor ) = 0;
+  virtual void AddDecoration( Actor& actor, bool needsClipping ) = 0;
 
   /**
    * @brief Called to request a text relayout.
index 2d84c2d..f41bddd 100644 (file)
@@ -129,13 +129,13 @@ EventData::EventData( DecoratorPtr decorator )
   mCursorBlinkEnabled( true ),
   mGrabHandleEnabled( true ),
   mGrabHandlePopupEnabled( true ),
-  mSelectionEnabled( false ),
+  mSelectionEnabled( true ),
   mHorizontalScrollingEnabled( true ),
   mVerticalScrollingEnabled( false ),
   mUpdateCursorPosition( false ),
   mUpdateLeftSelectionPosition( false ),
   mUpdateRightSelectionPosition( false ),
-  mScrollAfterUpdateCursorPosition( false ),
+  mScrollAfterUpdatePosition( false ),
   mScrollAfterDelete( false )
 {}
 
@@ -193,10 +193,12 @@ bool Controller::Impl::ProcessInputEvents()
 
     UpdateCursorPosition();
 
-    if( mEventData->mScrollAfterUpdateCursorPosition )
+    if( mEventData->mScrollAfterUpdatePosition )
     {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
+      const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+      ScrollToMakePositionVisible( primaryCursorPosition );
+      mEventData->mScrollAfterUpdatePosition = false;
     }
 
     mEventData->mDecoratorUpdated = true;
@@ -208,39 +210,56 @@ bool Controller::Impl::ProcessInputEvents()
     mEventData->mDecoratorUpdated = true;
     mEventData->mScrollAfterDelete = false;
   }
-  else if( mEventData->mUpdateLeftSelectionPosition )
+  else
   {
-    UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
+    bool leftScroll = false;
+    bool rightScroll = false;
 
-    if( mEventData->mScrollAfterUpdateCursorPosition )
+    if( mEventData->mUpdateLeftSelectionPosition )
     {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
-    }
+      UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
 
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateLeftSelectionPosition = false;
-  }
-  else if( mEventData->mUpdateRightSelectionPosition )
-  {
-    UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+      if( mEventData->mScrollAfterUpdatePosition )
+      {
+        const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
+
+        ScrollToMakePositionVisible( leftHandlePosition );
+        leftScroll = true;
+      }
 
-    if( mEventData->mScrollAfterUpdateCursorPosition )
+      mEventData->mDecoratorUpdated = true;
+      mEventData->mUpdateLeftSelectionPosition = false;
+    }
+
+    if( mEventData->mUpdateRightSelectionPosition )
     {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
+      UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+
+      if( mEventData->mScrollAfterUpdatePosition )
+      {
+        const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
+
+        ScrollToMakePositionVisible( rightHandlePosition );
+        rightScroll = true;
+      }
+
+      mEventData->mDecoratorUpdated = true;
+      mEventData->mUpdateRightSelectionPosition = false;
     }
 
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateRightSelectionPosition = false;
+    if( leftScroll || rightScroll )
+    {
+      mEventData->mScrollAfterUpdatePosition = false;
+    }
   }
 
   mEventData->mEventQueue.clear();
 
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
 
-  bool decoratorUpdated = mEventData->mDecoratorUpdated;
+  const bool decoratorUpdated = mEventData->mDecoratorUpdated;
   mEventData->mDecoratorUpdated = false;
+
   return decoratorUpdated;
 }
 
@@ -438,7 +457,7 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
   }
 
   mEventData->mUpdateCursorPosition = true;
-  mEventData->mScrollAfterUpdateCursorPosition = true;
+  mEventData->mScrollAfterUpdatePosition = true;
 }
 
 void Controller::Impl::OnTapEvent( const Event& event )
@@ -463,12 +482,21 @@ void Controller::Impl::OnTapEvent( const Event& event )
       }
 
       mEventData->mUpdateCursorPosition = true;
-      mEventData->mScrollAfterUpdateCursorPosition = true;
+      mEventData->mScrollAfterUpdatePosition = true;
     }
     else if( mEventData->mSelectionEnabled &&
              ( 2u == tapCount ) )
     {
-      RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
+      // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+      const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+      const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+      RepositionSelectionHandles( xPosition,
+                                  yPosition );
+
+      mEventData->mScrollAfterUpdatePosition = true;
+      mEventData->mUpdateLeftSelectionPosition = true;
+      mEventData->mUpdateRightSelectionPosition = true;
     }
   }
 }
@@ -521,6 +549,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
   }
 
   const unsigned int state = event.p1.mUint;
+  const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
 
   if( HANDLE_PRESSED == state )
   {
@@ -547,6 +576,10 @@ void Controller::Impl::OnHandleEvent( const Event& event )
       if( handleNewPosition != mEventData->mLeftSelectionPosition )
       {
         mEventData->mLeftSelectionPosition = handleNewPosition;
+
+        RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                    mEventData->mRightSelectionPosition );
+
         mEventData->mUpdateLeftSelectionPosition = true;
       }
     }
@@ -557,36 +590,76 @@ void Controller::Impl::OnHandleEvent( const Event& event )
       if( handleNewPosition != mEventData->mRightSelectionPosition )
       {
         mEventData->mRightSelectionPosition = handleNewPosition;
+
+        RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                    mEventData->mRightSelectionPosition );
+
         mEventData->mUpdateRightSelectionPosition = true;
       }
     }
-  }
+  } // end ( HANDLE_PRESSED == state )
   else if( ( HANDLE_RELEASED == state ) ||
-           ( HANDLE_STOP_SCROLLING == state ) )
+           handleStopScrolling )
   {
+    CharacterIndex handlePosition = 0u;
+    if( handleStopScrolling )
+    {
+      // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+      const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+      const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+      handlePosition = GetClosestCursorIndex( xPosition, yPosition );
+    }
+
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
       mEventData->mUpdateCursorPosition = true;
 
       ChangeState( EventData::EDITING_WITH_POPUP );
 
-      if( HANDLE_STOP_SCROLLING == state )
+      if( handleStopScrolling )
       {
-        // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
-        const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
-        const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
-
-        mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
+        mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
+        mEventData->mPrimaryCursorPosition = handlePosition;
+      }
+    }
+    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+    {
+      ChangeState( EventData::SELECTING );
 
-        mEventData->mScrollAfterUpdateCursorPosition = true;
+      if( handleStopScrolling )
+      {
+        mEventData->mUpdateLeftSelectionPosition = mEventData->mLeftSelectionPosition != handlePosition;
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
+        mEventData->mLeftSelectionPosition = handlePosition;
+
+        if( mEventData->mUpdateLeftSelectionPosition )
+        {
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+        }
       }
     }
-    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
+    else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
     {
       ChangeState( EventData::SELECTING );
+
+      if( handleStopScrolling )
+      {
+        mEventData->mUpdateRightSelectionPosition = mEventData->mRightSelectionPosition != handlePosition;
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
+        mEventData->mRightSelectionPosition = handlePosition;
+
+        if( mEventData->mUpdateRightSelectionPosition )
+        {
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+        }
+      }
     }
+
     mEventData->mDecoratorUpdated = true;
-  }
+  } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
   else if( HANDLE_SCROLLING == state )
   {
     const float xSpeed = event.p2.mFloat;
@@ -596,17 +669,105 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
     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 )
     {
       ChangeState( EventData::GRAB_HANDLE_PANNING );
     }
-    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
+    else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
     {
+      // 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.
+
       ChangeState( EventData::SELECTION_HANDLE_PANNING );
-    }
 
+      const Vector2& position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_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 );
+
+      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->mDecoratorUpdated = true;
+  } // end ( HANDLE_SCROLLING == state )
+}
+
+void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
+{
+  mEventData->mDecorator->ClearHighlights();
+
+  mEventData->mLeftSelectionPosition = selectionStart;
+  mEventData->mRightSelectionPosition = selectionEnd;
+
+  const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+  const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
+  const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+
+  // TODO: Better algorithm to create the highlight box.
+  // TODO: Multi-line.
+
+  const Vector<LineRun>& lines = mVisualModel->mLines;
+  const float height = lines[0].ascender + -lines[0].descender;
+
+  const bool indicesSwapped = selectionStart > selectionEnd;
+  if( indicesSwapped )
+  {
+    std::swap( selectionStart, selectionEnd );
   }
+
+  GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+  GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
+
+  mEventData->mDecorator->SwapSelectionHandlesEnabled( indicesSwapped );
+
+  const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+
+  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+  {
+    const GlyphInfo& glyph = *( glyphsBuffer + index );
+    const Vector2& position = *( positionsBuffer + index );
+
+    const float xPosition = position.x - glyph.xBearing + offset.x;
+    mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
+  }
+
+  CursorInfo primaryCursorInfo;
+  GetCursorPosition( mEventData->mLeftSelectionPosition,
+                     primaryCursorInfo );
+
+  CursorInfo secondaryCursorInfo;
+  GetCursorPosition( mEventData->mRightSelectionPosition,
+                     secondaryCursorInfo );
+
+  const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
+  const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
+
+  mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
+
+  mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
+
+  // Set the flag to update the decorator.
+  mEventData->mDecoratorUpdated = true;
 }
 
 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
@@ -617,32 +778,35 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
     return;
   }
 
-  // TODO - Find which word was selected
-
-  const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
-  const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
-
-  const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
-  const Vector<Vector2>::SizeType positionCount = positions.Count();
-
-  // Guard against glyphs which did not fit inside the layout
-  const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
-
-  if( count )
+  if( IsShowingPlaceholderText() )
   {
-    float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
-    float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
+    // Nothing to do if there is the place-holder text.
+    return;
+  }
 
-    // TODO - multi-line selection
-    const Vector<LineRun>& lines = mVisualModel->mLines;
-    float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
+  const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
+  const Length numberOfLines  = mVisualModel->mLines.Count();
+  if( 0 == numberOfGlyphs ||
+      0 == numberOfLines )
+  {
+    // Nothing to do if there is no text.
+    return;
+  }
 
-    mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
-    mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
+  // Find which word was selected
+  CharacterIndex selectionStart( 0 );
+  CharacterIndex selectionEnd( 0 );
+  FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
 
-    mEventData->mDecorator->ClearHighlights();
-    mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
+  if( selectionStart == selectionEnd )
+  {
+    ChangeState( EventData::EDITING );
+    // Nothing to select. i.e. a white space, out of bounds
+    return;
   }
+
+  RepositionSelectionHandles( selectionStart, selectionEnd );
 }
 
 void Controller::Impl::ChangeState( EventData::State newState )
@@ -777,6 +941,41 @@ LineIndex Controller::Impl::GetClosestLine( float y ) const
   return lineIndex-1;
 }
 
+void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
+{
+  CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
+  if( hitCharacter >= mLogicalModel->mText.Count() )
+  {
+    // Selection out of bounds.
+    return;
+  }
+
+  startIndex = hitCharacter;
+  endIndex = hitCharacter;
+
+  if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
+  {
+    // Find the start and end of the text
+    for( startIndex = hitCharacter; startIndex > 0; --startIndex )
+    {
+      Character charCode = mLogicalModel->mText[ startIndex-1 ];
+      if( TextAbstraction::IsWhiteSpace( charCode ) )
+      {
+        break;
+      }
+    }
+    const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
+    for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
+    {
+      Character charCode = mLogicalModel->mText[ endIndex ];
+      if( TextAbstraction::IsWhiteSpace( charCode ) )
+      {
+        break;
+      }
+    }
+  }
+}
+
 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
                                                         float visualY )
 {
@@ -847,9 +1046,10 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
 
-    const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
+    // Find the mid-point of the area containing the glyph
+    const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
 
-    if( visualX < glyphX )
+    if( visualX < glyphCenter )
     {
       matched = true;
       break;
@@ -863,7 +1063,9 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
     visualIndex = endCharacter;
   }
 
-  return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+  logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
+  return logicalIndex;
 }
 
 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
@@ -1281,27 +1483,19 @@ void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
   }
 }
 
-void Controller::Impl::ScrollToMakeCursorVisible()
+void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
-
   Vector2 offset;
   bool updateDecorator = false;
-  if( primaryCursorPosition.x < 0.f )
+  if( position.x < 0.f )
   {
-    offset.x = -primaryCursorPosition.x;
+    offset.x = -position.x;
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
-  else if( primaryCursorPosition.x > mControlSize.width )
+  else if( position.x > mControlSize.width )
   {
-    offset.x = mControlSize.width - primaryCursorPosition.x;
+    offset.x = mControlSize.width - position.x;
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
index 1a797b3..81125da 100644 (file)
@@ -135,20 +135,20 @@ struct EventData
   CharacterIndex     mPreEditStartPosition;    ///< Used to remove the pre-edit text if necessary.
   Length             mPreEditLength;           ///< Used to remove the pre-edit text if necessary.
 
-  bool mIsShowingPlaceholderText           : 1;   ///< True if the place-holder text is being displayed.
-  bool mPreEditFlag                        : 1;   ///< True if the model contains text in pre-edit state.
-  bool mDecoratorUpdated                   : 1;   ///< True if the decorator was updated during event processing.
-  bool mCursorBlinkEnabled                 : 1;   ///< True if cursor should blink when active.
-  bool mGrabHandleEnabled                  : 1;   ///< True if grab handle is enabled.
-  bool mGrabHandlePopupEnabled             : 1;   ///< True if the grab handle popu-up should be shown.
-  bool mSelectionEnabled                   : 1;   ///< True if selection handles, highlight etc. are enabled.
-  bool mHorizontalScrollingEnabled         : 1;   ///< True if horizontal scrolling is enabled.
-  bool mVerticalScrollingEnabled           : 1;   ///< True if vertical scrolling is enabled.
-  bool mUpdateCursorPosition               : 1;   ///< True if the visual position of the cursor must be recalculated.
-  bool mUpdateLeftSelectionPosition        : 1;   ///< True if the visual position of the left selection handle must be recalculated.
-  bool mUpdateRightSelectionPosition       : 1;   ///< True if the visual position of the right selection handle must be recalculated.
-  bool mScrollAfterUpdateCursorPosition    : 1;   ///< Whether to scroll after the cursor position is updated.
-  bool mScrollAfterDelete                  : 1;   ///< Whether to scroll after delete characters.
+  bool mIsShowingPlaceholderText        : 1;   ///< True if the place-holder text is being displayed.
+  bool mPreEditFlag                     : 1;   ///< True if the model contains text in pre-edit state.
+  bool mDecoratorUpdated                : 1;   ///< True if the decorator was updated during event processing.
+  bool mCursorBlinkEnabled              : 1;   ///< True if cursor should blink when active.
+  bool mGrabHandleEnabled               : 1;   ///< True if grab handle is enabled.
+  bool mGrabHandlePopupEnabled          : 1;   ///< True if the grab handle popu-up should be shown.
+  bool mSelectionEnabled                : 1;   ///< True if selection handles, highlight etc. are enabled.
+  bool mHorizontalScrollingEnabled      : 1;   ///< True if horizontal scrolling is enabled.
+  bool mVerticalScrollingEnabled        : 1;   ///< True if vertical scrolling is enabled.
+  bool mUpdateCursorPosition            : 1;   ///< True if the visual position of the cursor must be recalculated.
+  bool mUpdateLeftSelectionPosition     : 1;   ///< True if the visual position of the left selection handle must be recalculated.
+  bool mUpdateRightSelectionPosition    : 1;   ///< True if the visual position of the right selection handle must be recalculated.
+  bool mScrollAfterUpdatePosition       : 1;   ///< Whether to scroll after the cursor position is updated.
+  bool mScrollAfterDelete               : 1;   ///< Whether to scroll after delete characters.
 };
 
 struct ModifyEvent
@@ -323,12 +323,15 @@ struct Controller::Impl
 
   void OnHandleEvent( const Event& event );
 
+  void RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd );
   void RepositionSelectionHandles( float visualX, float visualY );
 
   void ChangeState( EventData::State newState );
 
   LineIndex GetClosestLine( float y ) const;
 
+  void FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex );
+
   /**
    * @brief Retrieves the cursor's logical position for a given touch point x,y
    *
@@ -395,11 +398,16 @@ struct Controller::Impl
   void ClampVerticalScroll( const Vector2& actualSize );
 
   /**
-   * @brief Scrolls the text to make the cursor visible.
+   * @brief Scrolls the text to make a position visible.
+   *
+   * @pre mEventData must not be NULL. (there is a text-input or selection capabilities).
+   *
+   * @param[in] position A position in decorator coords.
    *
-   * This method is called after inserting text or moving the cursor with the keypad.
+   * This method is called after inserting text, moving the cursor with the grab handle or the keypad,
+   * or moving the selection handles.
    */
-  void ScrollToMakeCursorVisible();
+  void ScrollToMakePositionVisible( const Vector2& position );
 
   /**
    * @brief Scrolls the text to make the cursor visible.
index 26d7ef0..3b36ed3 100644 (file)
@@ -708,7 +708,7 @@ void Controller::ResetScrollPosition()
   {
     // Reset the scroll position.
     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
-    mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+    mImpl->mEventData->mScrollAfterUpdatePosition = true;
   }
 }
 
@@ -749,7 +749,7 @@ void Controller::TextInsertedEvent()
 
   // Queue a cursor reposition event; this must wait until after DoRelayout()
   mImpl->mEventData->mUpdateCursorPosition = true;
-  mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+  mImpl->mEventData->mScrollAfterUpdatePosition = true;
 }
 
 void Controller::TextDeletedEvent()
@@ -1305,12 +1305,12 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
 
   if( NULL != mImpl->mEventData )
   {
+    const bool isShowingPlaceholderText = mImpl->IsShowingPlaceholderText();
     if( 1u == tapCount )
     {
       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
 
-      if( ! mImpl->IsShowingPlaceholderText() &&
-          EventData::EDITING == mImpl->mEventData->mState )
+      if( !isShowingPlaceholderText && tapDuringEditMode )
       {
         // Grab handle is not shown until a tap is received whilst EDITING
         if( tapDuringEditMode )
@@ -1322,7 +1322,8 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
 
       mImpl->ChangeState( EventData::EDITING );
     }
-    else if( mImpl->mEventData->mSelectionEnabled &&
+    else if( !isShowingPlaceholderText &&
+             mImpl->mEventData->mSelectionEnabled &&
              ( 2u == tapCount ) )
     {
       mImpl->ChangeState( EventData::SELECTING );
@@ -1366,9 +1367,9 @@ void Controller::GetTargetSize( Vector2& targetSize )
   targetSize = mImpl->mControlSize;
 }
 
-void Controller::AddDecoration( Actor& actor )
+void Controller::AddDecoration( Actor& actor, bool needsClipping )
 {
-  mImpl->mControlInterface.AddDecoration( actor );
+  mImpl->mControlInterface.AddDecoration( actor, needsClipping );
 }
 
 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
index 8428699..66bf3a0 100644 (file)
@@ -532,7 +532,7 @@ public:
   /**
    * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::AddDecoration()
    */
-  virtual void AddDecoration( Actor& actor );
+  virtual void AddDecoration( Actor& actor, bool needsClipping );
 
   /**
    * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::DecorationEvent()