Merge "add Padding parameter at RendererParameters" into devel/master
authorjoogab yun <joogab.yun@samsung.com>
Tue, 8 Dec 2020 01:54:31 +0000 (01:54 +0000)
committerGerrit Code Review <gerrit@review>
Tue, 8 Dec 2020 01:54:31 +0000 (01:54 +0000)
15 files changed:
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl-event-handler.h
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-selection-handle-controller.cpp [new file with mode: 0644]
dali-toolkit/internal/text/text-selection-handle-controller.h [new file with mode: 0644]
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

index dd7827f..534aeb6 100644 (file)
@@ -180,7 +180,8 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
              .Add( "loopCount", 3 )
              .Add( "playRange", playRange )
              .Add( "stopBehavior", DevelImageVisual::StopBehavior::FIRST_FRAME )
-             .Add( "loopingMode", DevelImageVisual::LoopingMode::AUTO_REVERSE );
+             .Add( "loopingMode", DevelImageVisual::LoopingMode::AUTO_REVERSE )
+             .Add( "redrawInScalingDown", false );
 
   Visual::Base visual = VisualFactory::Get().CreateVisual( propertyMap );
   DALI_TEST_CHECK( visual );
@@ -232,6 +233,10 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
   DALI_TEST_CHECK( value );
   DALI_TEST_CHECK( value->Get< int >() == DevelImageVisual::LoopingMode::AUTO_REVERSE );
 
+  value = resultMap.Find( DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, Property::BOOLEAN );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get< bool >() == false );
+
   actor.Unparent( );
   DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
 
@@ -308,6 +313,10 @@ int UtcDaliAnimatedVectorImageVisualGetPropertyMap01(void)
   value = resultMap.Find( DevelImageVisual::Property::CONTENT_INFO, Property::MAP );
   DALI_TEST_CHECK( value );
 
+  value = resultMap.Find( DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, Property::BOOLEAN );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get< bool >() == true );    // Check default value
+
   // request AnimatedVectorImageVisual with an URL
   Visual::Base visual2 = factory.CreateVisual( TEST_VECTOR_IMAGE_FILE_NAME, ImageDimensions() );
 
index 63efcb3..a10a3f8 100644 (file)
@@ -138,7 +138,14 @@ enum Type
    * And the array contains 2 integer values which are the frame numbers, the start frame number and the end frame number of the layer.
    * @note This property is read-only.
    */
-  CONTENT_INFO = ORIENTATION_CORRECTION + 10
+  CONTENT_INFO = ORIENTATION_CORRECTION + 10,
+
+  /**
+   * @brief Whether to redraw the image when the visual is scaled down.
+   * @details Name "redrawInScalingDown", type Property::BOOLEAN.
+   * @note It is used in the AnimatedVectorImageVisual. The default is true.
+   */
+  REDRAW_IN_SCALING_DOWN
 };
 
 } //namespace Property
index 0081833..29ec224 100644 (file)
@@ -152,6 +152,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/text-io.cpp
    ${toolkit_src_dir}/text/text-model.cpp
    ${toolkit_src_dir}/text/text-scroller.cpp
+   ${toolkit_src_dir}/text/text-selection-handle-controller.cpp
    ${toolkit_src_dir}/text/text-vertical-scroller.cpp
    ${toolkit_src_dir}/text/text-view.cpp
    ${toolkit_src_dir}/text/text-view-interface.cpp
index 7b50a5c..475f3fc 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-editable-control-interface.h>
 
 using namespace Dali;
 
@@ -45,6 +46,225 @@ namespace Toolkit
 namespace Text
 {
 
+bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
+
+  EventData*& eventData = impl.mEventData;
+  if( NULL == eventData )
+  {
+    // Nothing to do if there is no text input.
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
+    return false;
+  }
+
+  if( eventData->mDecorator )
+  {
+    for( std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
+         iter != eventData->mEventQueue.end();
+         ++iter )
+    {
+      switch( iter->type )
+      {
+        case Event::CURSOR_KEY_EVENT:
+        {
+          OnCursorKeyEvent(impl, *iter);
+          break;
+        }
+        case Event::TAP_EVENT:
+        {
+          OnTapEvent(impl, *iter);
+          break;
+        }
+        case Event::LONG_PRESS_EVENT:
+        {
+          OnLongPressEvent(impl, *iter);
+          break;
+        }
+        case Event::PAN_EVENT:
+        {
+          OnPanEvent(impl, *iter);
+          break;
+        }
+        case Event::GRAB_HANDLE_EVENT:
+        case Event::LEFT_SELECTION_HANDLE_EVENT:
+        case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
+        {
+          OnHandleEvent(impl, *iter);
+          break;
+        }
+        case Event::SELECT:
+        {
+          OnSelectEvent(impl, *iter);
+          break;
+        }
+        case Event::SELECT_ALL:
+        {
+          OnSelectAllEvent(impl);
+          break;
+        }
+        case Event::SELECT_NONE:
+        {
+          OnSelectNoneEvent(impl);
+          break;
+        }
+      }
+    }
+  }
+
+  if( eventData->mUpdateCursorPosition ||
+      eventData->mUpdateHighlightBox )
+  {
+    impl.NotifyInputMethodContext();
+  }
+
+  // The cursor must also be repositioned after inserts into the model
+  if( eventData->mUpdateCursorPosition )
+  {
+    // Updates the cursor position and scrolls the text to make it visible.
+    CursorInfo cursorInfo;
+    // Calculate the cursor position from the new cursor index.
+    impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
+
+    if(nullptr != impl.mEditableControlInterface)
+    {
+      impl.mEditableControlInterface->CaretMoved( eventData->mPrimaryCursorPosition );
+    }
+
+    if( eventData->mUpdateCursorHookPosition )
+    {
+      // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
+      eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
+      eventData->mUpdateCursorHookPosition = false;
+    }
+
+    // Scroll first the text after delete ...
+    if( eventData->mScrollAfterDelete )
+    {
+      impl.ScrollTextToMatchCursor(cursorInfo);
+    }
+
+    // ... then, text can be scrolled to make the cursor visible.
+    if( eventData->mScrollAfterUpdatePosition )
+    {
+      const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
+      impl.ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
+    }
+    eventData->mScrollAfterUpdatePosition = false;
+    eventData->mScrollAfterDelete = false;
+
+    impl.UpdateCursorPosition( cursorInfo );
+
+    eventData->mDecoratorUpdated = true;
+    eventData->mUpdateCursorPosition = false;
+    eventData->mUpdateGrabHandlePosition = false;
+  }
+  else
+  {
+    CursorInfo leftHandleInfo;
+    CursorInfo rightHandleInfo;
+
+    if( eventData->mUpdateHighlightBox )
+    {
+      impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
+
+      impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
+
+      if( eventData->mScrollAfterUpdatePosition && ( eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition ) )
+      {
+        if( eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected )
+        {
+          CursorInfo& infoLeft = leftHandleInfo;
+
+          const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
+          impl.ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
+
+          CursorInfo& infoRight = rightHandleInfo;
+
+          const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
+          impl.ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
+        }
+        else
+        {
+          CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
+
+          const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
+          impl. ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
+        }
+      }
+    }
+
+    if( eventData->mUpdateLeftSelectionPosition )
+    {
+      impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
+
+      impl.SetPopupButtons();
+      eventData->mDecoratorUpdated = true;
+      eventData->mUpdateLeftSelectionPosition = false;
+    }
+
+    if( eventData->mUpdateRightSelectionPosition )
+    {
+      impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
+
+      impl.SetPopupButtons();
+      eventData->mDecoratorUpdated = true;
+      eventData->mUpdateRightSelectionPosition = false;
+    }
+
+    if( eventData->mUpdateHighlightBox )
+    {
+      impl.RepositionSelectionHandles();
+
+      eventData->mUpdateLeftSelectionPosition = false;
+      eventData->mUpdateRightSelectionPosition = false;
+      eventData->mUpdateHighlightBox = false;
+      eventData->mIsLeftHandleSelected = false;
+      eventData->mIsRightHandleSelected = false;
+    }
+
+    eventData->mScrollAfterUpdatePosition = false;
+  }
+
+  if( eventData->mUpdateInputStyle )
+  {
+    // Keep a copy of the current input style.
+    InputStyle currentInputStyle;
+    currentInputStyle.Copy( eventData->mInputStyle );
+
+    // Set the default style first.
+    impl.RetrieveDefaultInputStyle( eventData->mInputStyle );
+
+    // Get the character index from the cursor index.
+    const CharacterIndex styleIndex = ( eventData->mPrimaryCursorPosition > 0u ) ? eventData->mPrimaryCursorPosition - 1u : 0u;
+
+    // Retrieve the style from the style runs stored in the logical model.
+    impl.mModel->mLogicalModel->RetrieveStyle( styleIndex, eventData->mInputStyle );
+
+    // Compare if the input style has changed.
+    const bool hasInputStyleChanged = !currentInputStyle.Equal( eventData->mInputStyle );
+
+    if( hasInputStyleChanged )
+    {
+      const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( eventData->mInputStyle );
+      // Queue the input style changed signal.
+      eventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+    }
+
+    eventData->mUpdateInputStyle = false;
+  }
+
+  eventData->mEventQueue.clear();
+
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
+
+  const bool decoratorUpdated = eventData->mDecoratorUpdated;
+  eventData->mDecoratorUpdated = false;
+
+  return decoratorUpdated;
+}
+
+
 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
 {
   if( NULL == impl.mEventData || !impl.IsShowingRealText() )
index 52ac94a..7399fdd 100644 (file)
@@ -36,6 +36,14 @@ namespace Text
 struct ControllerImplEventHandler
 {
   /**
+   * @brief Processes input events
+   *
+   * @param[in] impl A reference to Controller::Impl
+   * @return True if the decorator has been updated
+   */
+  static bool ProcessInputEvents(Controller::Impl& impl);
+
+  /**
    * @brief Called by Controller::Impl when a cursor key event is received.
    *
    * @param controllerImpl A reference to Controller::Impl
index d5edfc3..6554a2c 100644 (file)
@@ -21,7 +21,6 @@
 // EXTERNAL INCLUDES
 #include <dali/public-api/rendering/renderer.h>
 #include <dali/integration-api/debug.h>
-#include <limits>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/internal/text/text-control-interface.h>
 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
 #include <dali-toolkit/internal/text/text-run-container.h>
-#include <dali-toolkit/internal/text/text-editable-control-interface.h>
+#include <dali-toolkit/internal/text/text-selection-handle-controller.h>
 
 using namespace Dali;
 
 namespace
 {
 
-/**
- * @brief Struct used to calculate the selection box.
- */
-struct SelectionBoxInfo
-{
-  float lineOffset;
-  float lineHeight;
-  float minX;
-  float maxX;
-};
-
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
-const float MAX_FLOAT = std::numeric_limits<float>::max();
-const float MIN_FLOAT = std::numeric_limits<float>::min();
-const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
-
 #define MAKE_SHADER(A)#A
 
 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
@@ -165,228 +149,9 @@ EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodCon
 {
 }
 
-EventData::~EventData()
-{}
-
 bool Controller::Impl::ProcessInputEvents()
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
-    return false;
-  }
-
-  if( mEventData->mDecorator )
-  {
-    for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
-         iter != mEventData->mEventQueue.end();
-         ++iter )
-    {
-      switch( iter->type )
-      {
-        case Event::CURSOR_KEY_EVENT:
-        {
-          OnCursorKeyEvent( *iter );
-          break;
-        }
-        case Event::TAP_EVENT:
-        {
-          OnTapEvent( *iter );
-          break;
-        }
-        case Event::LONG_PRESS_EVENT:
-        {
-          OnLongPressEvent( *iter );
-          break;
-        }
-        case Event::PAN_EVENT:
-        {
-          OnPanEvent( *iter );
-          break;
-        }
-        case Event::GRAB_HANDLE_EVENT:
-        case Event::LEFT_SELECTION_HANDLE_EVENT:
-        case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
-        {
-          OnHandleEvent( *iter );
-          break;
-        }
-        case Event::SELECT:
-        {
-          OnSelectEvent( *iter );
-          break;
-        }
-        case Event::SELECT_ALL:
-        {
-          OnSelectAllEvent();
-          break;
-        }
-        case Event::SELECT_NONE:
-        {
-          OnSelectNoneEvent();
-          break;
-        }
-      }
-    }
-  }
-
-  if( mEventData->mUpdateCursorPosition ||
-      mEventData->mUpdateHighlightBox )
-  {
-    NotifyInputMethodContext();
-  }
-
-  // The cursor must also be repositioned after inserts into the model
-  if( mEventData->mUpdateCursorPosition )
-  {
-    // Updates the cursor position and scrolls the text to make it visible.
-    CursorInfo cursorInfo;
-    // Calculate the cursor position from the new cursor index.
-    GetCursorPosition( mEventData->mPrimaryCursorPosition,
-                       cursorInfo );
-
-    if( NULL != mEditableControlInterface )
-    {
-      mEditableControlInterface->CaretMoved( mEventData->mPrimaryCursorPosition );
-    }
-
-    if( mEventData->mUpdateCursorHookPosition )
-    {
-      // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
-      mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
-      mEventData->mUpdateCursorHookPosition = false;
-    }
-
-    // Scroll first the text after delete ...
-    if( mEventData->mScrollAfterDelete )
-    {
-      ScrollTextToMatchCursor( cursorInfo );
-    }
-
-    // ... then, text can be scrolled to make the cursor visible.
-    if( mEventData->mScrollAfterUpdatePosition )
-    {
-      const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
-      ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
-    }
-    mEventData->mScrollAfterUpdatePosition = false;
-    mEventData->mScrollAfterDelete = false;
-
-    UpdateCursorPosition( cursorInfo );
-
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateCursorPosition = false;
-    mEventData->mUpdateGrabHandlePosition = false;
-  }
-  else
-  {
-    CursorInfo leftHandleInfo;
-    CursorInfo rightHandleInfo;
-
-    if( mEventData->mUpdateHighlightBox )
-    {
-      GetCursorPosition( mEventData->mLeftSelectionPosition,
-                         leftHandleInfo );
-
-      GetCursorPosition( mEventData->mRightSelectionPosition,
-                         rightHandleInfo );
-
-      if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
-      {
-        if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
-        {
-          CursorInfo& infoLeft = leftHandleInfo;
-
-          const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
-
-          CursorInfo& infoRight = rightHandleInfo;
-
-          const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
-        }
-        else
-        {
-          CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
-
-          const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
-        }
-      }
-    }
-
-    if( mEventData->mUpdateLeftSelectionPosition )
-    {
-      UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
-                             leftHandleInfo );
-
-      SetPopupButtons();
-      mEventData->mDecoratorUpdated = true;
-      mEventData->mUpdateLeftSelectionPosition = false;
-    }
-
-    if( mEventData->mUpdateRightSelectionPosition )
-    {
-      UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
-                             rightHandleInfo );
-
-      SetPopupButtons();
-      mEventData->mDecoratorUpdated = true;
-      mEventData->mUpdateRightSelectionPosition = false;
-    }
-
-    if( mEventData->mUpdateHighlightBox )
-    {
-      RepositionSelectionHandles();
-
-      mEventData->mUpdateLeftSelectionPosition = false;
-      mEventData->mUpdateRightSelectionPosition = false;
-      mEventData->mUpdateHighlightBox = false;
-      mEventData->mIsLeftHandleSelected = false;
-      mEventData->mIsRightHandleSelected = false;
-    }
-
-    mEventData->mScrollAfterUpdatePosition = false;
-  }
-
-  if( mEventData->mUpdateInputStyle )
-  {
-    // Keep a copy of the current input style.
-    InputStyle currentInputStyle;
-    currentInputStyle.Copy( mEventData->mInputStyle );
-
-    // Set the default style first.
-    RetrieveDefaultInputStyle( mEventData->mInputStyle );
-
-    // Get the character index from the cursor index.
-    const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
-
-    // Retrieve the style from the style runs stored in the logical model.
-    mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
-
-    // Compare if the input style has changed.
-    const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
-
-    if( hasInputStyleChanged )
-    {
-      const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
-      // Queue the input style changed signal.
-      mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
-    }
-
-    mEventData->mUpdateInputStyle = false;
-  }
-
-  mEventData->mEventQueue.clear();
-
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
-
-  const bool decoratorUpdated = mEventData->mDecoratorUpdated;
-  mEventData->mDecoratorUpdated = false;
-
-  return decoratorUpdated;
+  return ControllerImplEventHandler::ProcessInputEvents(*this);
 }
 
 void Controller::Impl::NotifyInputMethodContext()
@@ -1345,46 +1110,6 @@ float Controller::Impl::GetDefaultFontLineHeight()
   return( fontMetrics.ascender - fontMetrics.descender );
 }
 
-void Controller::Impl::OnCursorKeyEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
-}
-
-void Controller::Impl::OnTapEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnTapEvent(*this, event);
-}
-
-void Controller::Impl::OnPanEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnPanEvent(*this, event);
-}
-
-void Controller::Impl::OnLongPressEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnLongPressEvent(*this, event);
-}
-
-void Controller::Impl::OnHandleEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnHandleEvent(*this, event);
-}
-
-void Controller::Impl::OnSelectEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnSelectEvent(*this, event);
-}
-
-void Controller::Impl::OnSelectAllEvent()
-{
-  ControllerImplEventHandler::OnSelectAllEvent(*this);
-}
-
-void Controller::Impl::OnSelectNoneEvent()
-{
-  ControllerImplEventHandler::OnSelectNoneEvent(*this);
-}
-
 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
 {
   if( nullptr == mEventData )
@@ -1614,477 +1339,11 @@ void Controller::Impl::RequestGetTextFromClipboard()
 
 void Controller::Impl::RepositionSelectionHandles()
 {
-  CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
-  CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
-
-  if( selectionStart == selectionEnd )
-  {
-    // Nothing to select if handles are in the same place.
-    // So, deactive Highlight box.
-    mEventData->mDecorator->SetHighlightActive( false );
-    return;
-  }
-
-  mEventData->mDecorator->ClearHighlights();
-
-  const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
-  const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
-  const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
-  const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
-  const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
-  const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
-  const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
-
-  const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
-  const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
-  const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
-
-  // Swap the indices if the start is greater than the end.
-  const bool indicesSwapped = selectionStart > selectionEnd;
-
-  // Tell the decorator to flip the selection handles if needed.
-  mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
-
-  if( indicesSwapped )
-  {
-    std::swap( selectionStart, selectionEnd );
-  }
-
-  // Get the indices to the first and last selected glyphs.
-  const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
-  const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
-  const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
-  const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
-
-  // Get the lines where the glyphs are laid-out.
-  const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
-
-  LineIndex lineIndex = 0u;
-  Length numberOfLines = 0u;
-  mModel->mVisualModel->GetNumberOfLines( glyphStart,
-                                          1u + glyphEnd - glyphStart,
-                                          lineIndex,
-                                          numberOfLines );
-  const LineIndex firstLineIndex = lineIndex;
-
-  // Create the structure to store some selection box info.
-  Vector<SelectionBoxInfo> selectionBoxLinesInfo;
-  selectionBoxLinesInfo.Resize( numberOfLines );
-
-  SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
-  selectionBoxInfo->minX = MAX_FLOAT;
-  selectionBoxInfo->maxX = MIN_FLOAT;
-
-  // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
-  float minHighlightX = std::numeric_limits<float>::max();
-  float maxHighlightX = std::numeric_limits<float>::min();
-  Size highLightSize;
-  Vector2 highLightPosition; // The highlight position in decorator's coords.
-
-  // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
-
-  // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
-  selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
-                                                      firstLineIndex );
-
-  // Transform to decorator's (control) coords.
-  selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
-
-  lineRun += firstLineIndex;
-
-  // The line height is the addition of the line ascender and the line descender.
-  // However, the line descender has a negative value, hence the subtraction.
-  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
-
-  GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
-
-  // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
-  const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
-  bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
-
-  // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
-  const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
-  bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
-
-  // The number of quads of the selection box.
-  const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
-  mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
-
-  // Count the actual number of quads.
-  unsigned int actualNumberOfQuads = 0u;
-  Vector4 quad;
-
-  // Traverse the glyphs.
-  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
-  {
-    const GlyphInfo& glyph = *( glyphsBuffer + index );
-    const Vector2& position = *( positionsBuffer + index );
-
-    if( splitStartGlyph )
-    {
-      // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
-
-      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
-      const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
-      // Get the direction of the character.
-      CharacterDirection isCurrentRightToLeft = false;
-      if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
-      {
-        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
-      }
-
-      // The end point could be in the middle of the ligature.
-      // Calculate the number of characters selected.
-      const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
-
-      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
-      quad.y = selectionBoxInfo->lineOffset;
-      quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
-      quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
-
-      // Store the min and max 'x' for each line.
-      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
-      ++actualNumberOfQuads;
-
-      splitStartGlyph = false;
-      continue;
-    }
-
-    if( splitEndGlyph && ( index == glyphEnd ) )
-    {
-      // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
-
-      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
-      const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
-      // Get the direction of the character.
-      CharacterDirection isCurrentRightToLeft = false;
-      if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
-      {
-        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
-      }
-
-      const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
-
-      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
-      quad.y = selectionBoxInfo->lineOffset;
-      quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
-      quad.w = quad.y + selectionBoxInfo->lineHeight;
-
-      // Store the min and max 'x' for each line.
-      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      splitEndGlyph = false;
-      continue;
-    }
-
-    quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
-    quad.y = selectionBoxInfo->lineOffset;
-    quad.z = quad.x + glyph.advance;
-    quad.w = quad.y + selectionBoxInfo->lineHeight;
-
-    // Store the min and max 'x' for each line.
-    selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-    selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-    mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                          quad );
-    ++actualNumberOfQuads;
-
-    // Whether to retrieve the next line.
-    if( index == lastGlyphOfLine )
-    {
-      ++lineIndex;
-      if( lineIndex < firstLineIndex + numberOfLines )
-      {
-        // Retrieve the next line.
-        ++lineRun;
-
-        // Get the last glyph of the new line.
-        lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
-
-        // Keep the offset and height of the current selection box.
-        const float currentLineOffset = selectionBoxInfo->lineOffset;
-        const float currentLineHeight = selectionBoxInfo->lineHeight;
-
-        // Get the selection box info for the next line.
-        ++selectionBoxInfo;
-
-        selectionBoxInfo->minX = MAX_FLOAT;
-        selectionBoxInfo->maxX = MIN_FLOAT;
-
-        // Update the line's vertical offset.
-        selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
-
-        // The line height is the addition of the line ascender and the line descender.
-        // However, the line descender has a negative value, hence the subtraction.
-        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
-      }
-    }
-  }
-
-  // Traverses all the lines and updates the min and max 'x' positions and the total height.
-  // The final width is calculated after 'boxifying' the selection.
-  for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
-         endIt = selectionBoxLinesInfo.End();
-       it != endIt;
-       ++it )
-  {
-    const SelectionBoxInfo& info = *it;
-
-    // Update the size of the highlighted text.
-    highLightSize.height += info.lineHeight;
-    minHighlightX = std::min( minHighlightX, info.minX );
-    maxHighlightX = std::max( maxHighlightX, info.maxX );
-  }
-
-  // Add extra geometry to 'boxify' the selection.
-
-  if( 1u < numberOfLines )
-  {
-    // Boxify the first line.
-    lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
-    const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
-
-    bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
-    bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
-
-    if( boxifyBegin )
-    {
-      quad.x = 0.f;
-      quad.y = firstSelectionBoxLineInfo.lineOffset;
-      quad.z = firstSelectionBoxLineInfo.minX;
-      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the beginning of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-    }
-
-    if( boxifyEnd )
-    {
-      quad.x = firstSelectionBoxLineInfo.maxX;
-      quad.y = firstSelectionBoxLineInfo.lineOffset;
-      quad.z = mModel->mVisualModel->mControlSize.width;
-      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the end of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-
-    // Boxify the central lines.
-    if( 2u < numberOfLines )
-    {
-      for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
-             endIt = selectionBoxLinesInfo.End() - 1u;
-           it != endIt;
-           ++it )
-      {
-        const SelectionBoxInfo& info = *it;
-
-        quad.x = 0.f;
-        quad.y = info.lineOffset;
-        quad.z = info.minX;
-        quad.w = info.lineOffset + info.lineHeight;
-
-        mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                              quad );
-        ++actualNumberOfQuads;
-
-        quad.x = info.maxX;
-        quad.y = info.lineOffset;
-        quad.z = mModel->mVisualModel->mControlSize.width;
-        quad.w = info.lineOffset + info.lineHeight;
-
-        mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                              quad );
-        ++actualNumberOfQuads;
-      }
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-
-    // Boxify the last line.
-    lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
-    const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
-
-    boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
-    boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
-
-    if( boxifyBegin )
-    {
-      quad.x = 0.f;
-      quad.y = lastSelectionBoxLineInfo.lineOffset;
-      quad.z = lastSelectionBoxLineInfo.minX;
-      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the beginning of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-    }
-
-    if( boxifyEnd )
-    {
-      quad.x = lastSelectionBoxLineInfo.maxX;
-      quad.y = lastSelectionBoxLineInfo.lineOffset;
-      quad.z = mModel->mVisualModel->mControlSize.width;
-      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the end of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-  }
-
-  // Set the actual number of quads.
-  mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
-
-  // Sets the highlight's size and position. In decorator's coords.
-  // The highlight's height has been calculated above (before 'boxifying' the highlight).
-  highLightSize.width = maxHighlightX - minHighlightX;
-
-  highLightPosition.x = minHighlightX;
-  const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
-  highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
-
-  mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
-
-  if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
-  {
-    CursorInfo primaryCursorInfo;
-    GetCursorPosition( mEventData->mLeftSelectionPosition,
-                       primaryCursorInfo );
-
-    const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
-
-    mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
-                                         primaryPosition.x,
-                                         primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                         primaryCursorInfo.lineHeight );
-
-    CursorInfo secondaryCursorInfo;
-    GetCursorPosition( mEventData->mRightSelectionPosition,
-                       secondaryCursorInfo );
-
-    const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
-
-    mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
-                                         secondaryPosition.x,
-                                         secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                         secondaryCursorInfo.lineHeight );
-  }
-
-  // Set the flag to update the decorator.
-  mEventData->mDecoratorUpdated = true;
+  SelectionHandleController::Reposition(*this);
 }
-
 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  if( IsShowingPlaceholderText() )
-  {
-    // Nothing to do if there is the place-holder text.
-    return;
-  }
-
-  const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
-  const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
-  if( ( 0 == numberOfGlyphs ) ||
-      ( 0 == numberOfLines ) )
-  {
-    // Nothing to do if there is no text.
-    return;
-  }
-
-  // Find which word was selected
-  CharacterIndex selectionStart( 0 );
-  CharacterIndex selectionEnd( 0 );
-  CharacterIndex noTextHitIndex( 0 );
-  const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
-                                                  mModel->mLogicalModel,
-                                                  mMetrics,
-                                                  visualX,
-                                                  visualY,
-                                                  selectionStart,
-                                                  selectionEnd,
-                                                  noTextHitIndex );
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
-
-  if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
-  {
-    ChangeState( EventData::SELECTING );
-
-    mEventData->mLeftSelectionPosition = selectionStart;
-    mEventData->mRightSelectionPosition = selectionEnd;
-
-    mEventData->mUpdateLeftSelectionPosition = true;
-    mEventData->mUpdateRightSelectionPosition = true;
-    mEventData->mUpdateHighlightBox = true;
-
-    // It may happen an InputMethodContext commit event arrives before the selection event
-    // if the InputMethodContext is in pre-edit state. The commit event will set the
-    // mEventData->mUpdateCursorPosition flag to true. If it's not set back
-    // to false, the highlight box won't be updated.
-    mEventData->mUpdateCursorPosition = false;
-
-    mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
-
-    // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
-    mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  }
-  else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
-  {
-    // Nothing to select. i.e. a white space, out of bounds
-    ChangeState( EventData::EDITING_WITH_POPUP );
-
-    mEventData->mPrimaryCursorPosition = noTextHitIndex;
-
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mUpdateGrabHandlePosition = true;
-    mEventData->mScrollAfterUpdatePosition = true;
-    mEventData->mUpdateInputStyle = true;
-  }
-  else if( Controller::NoTextTap::NO_ACTION == action )
-  {
-    // Nothing to select. i.e. a white space, out of bounds
-    mEventData->mPrimaryCursorPosition = noTextHitIndex;
-
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mUpdateGrabHandlePosition = true;
-    mEventData->mScrollAfterUpdatePosition = true;
-    mEventData->mUpdateInputStyle = true;
-  }
+  SelectionHandleController::Reposition(*this, visualX, visualY, action);
 }
 
 void Controller::Impl::SetPopupButtons()
@@ -2582,24 +1841,7 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
                                               const CursorInfo& cursorInfo )
 {
-  if( ( LEFT_SELECTION_HANDLE != handleType ) &&
-      ( RIGHT_SELECTION_HANDLE != handleType ) )
-  {
-    return;
-  }
-
-  const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
-
-  // Sets the handle's position.
-  mEventData->mDecorator->SetPosition( handleType,
-                                       cursorPosition.x,
-                                       cursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                       cursorInfo.lineHeight );
-
-  // If selection handle at start of the text and other at end of the text then all text is selected.
-  const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
+  SelectionHandleController::Update(*this, handleType, cursorInfo);
 }
 
 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
index 63a897c..db02721 100755 (executable)
@@ -49,6 +49,7 @@ const float DEFAULT_FONT_SIZE_SCALE = 1.f;
 struct CursorInfo;
 struct FontDefaults;
 struct ControllerImplEventHandler;
+struct SelectionHandleController;
 
 class SelectableControlInterface;
 
@@ -109,7 +110,7 @@ struct EventData
 
   EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext );
 
-  ~EventData();
+  ~EventData() = default;
 
   static bool IsEditingState( State stateToCheck )
   {
@@ -174,7 +175,7 @@ struct EventData
   bool mPlaceholderEllipsisFlag         : 1;   ///< True if the text controller sets the placeholder ellipsis.
   bool mShiftSelectionFlag              : 1;   ///< True if the text selection using Shift key is enabled.
   bool mUpdateAlignment                 : 1;   ///< True if the whole text needs to be full aligned..
-  bool mEditingEnabled                   : 1;   ///< True if the editing is enabled, false otherwise.
+  bool mEditingEnabled                  : 1;   ///< True if the editing is enabled, false otherwise.
 };
 
 struct ModifyEvent
@@ -614,22 +615,6 @@ struct Controller::Impl
    */
   float GetDefaultFontLineHeight();
 
-  void OnCursorKeyEvent( const Event& event );
-
-  void OnTapEvent( const Event& event );
-
-  void OnPanEvent( const Event& event );
-
-  void OnLongPressEvent( const Event& event );
-
-  void OnHandleEvent( const Event& event );
-
-  void OnSelectEvent( const Event& event );
-
-  void OnSelectAllEvent();
-
-  void OnSelectNoneEvent();
-
   /**
    * @copydoc Text::Controller::GetPrimaryCursorPosition()
    */
@@ -849,6 +834,7 @@ public:
 
 private:
   friend ControllerImplEventHandler;
+  friend SelectionHandleController;
 };
 
 } // namespace Text
diff --git a/dali-toolkit/internal/text/text-selection-handle-controller.cpp b/dali-toolkit/internal/text/text-selection-handle-controller.cpp
new file mode 100644 (file)
index 0000000..664b59e
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/text-selection-handle-controller.h>
+
+#include <dali/integration-api/debug.h>
+#include <limits>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
+
+using namespace Dali;
+
+namespace
+{
+
+/**
+ * @brief Struct used to calculate the selection box.
+ */
+struct SelectionBoxInfo
+{
+  float lineOffset;
+  float lineHeight;
+  float minX;
+  float maxX;
+};
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+#endif
+
+const float MAX_FLOAT = std::numeric_limits<float>::max();
+const float MIN_FLOAT = std::numeric_limits<float>::min();
+const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
+
+} // namespace
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+void SelectionHandleController::Reposition(Controller::Impl& impl)
+{
+  EventData*& eventData = impl.mEventData;
+
+  CharacterIndex selectionStart = eventData->mLeftSelectionPosition;
+  CharacterIndex selectionEnd = eventData->mRightSelectionPosition;
+
+  DecoratorPtr& decorator = eventData->mDecorator;
+
+  if( selectionStart == selectionEnd )
+  {
+    // Nothing to select if handles are in the same place.
+    // So, deactive Highlight box.
+    decorator->SetHighlightActive( false );
+    return;
+  }
+
+  decorator->ClearHighlights();
+
+  ModelPtr& model = impl.mModel;
+  VisualModelPtr& visualModel = model->mVisualModel;
+  LogicalModelPtr& logicalModel = model->mLogicalModel;
+
+  const GlyphIndex* const charactersToGlyphBuffer = visualModel->mCharactersToGlyph.Begin();
+  const Length* const glyphsPerCharacterBuffer = visualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const glyphsBuffer = visualModel->mGlyphs.Begin();
+  const Vector2* const positionsBuffer = visualModel->mGlyphPositions.Begin();
+  const Length* const charactersPerGlyphBuffer = visualModel->mCharactersPerGlyph.Begin();
+  const CharacterIndex* const glyphToCharacterBuffer = visualModel->mGlyphsToCharacters.Begin();
+  const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != logicalModel->mCharacterDirections.Count() ) ? logicalModel->mCharacterDirections.Begin() : NULL;
+
+  const bool isLastCharacter = selectionEnd >= logicalModel->mText.Count();
+  const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
+  const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
+
+  // Swap the indices if the start is greater than the end.
+  const bool indicesSwapped = selectionStart > selectionEnd;
+
+  // Tell the decorator to flip the selection handles if needed.
+  decorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
+
+  if( indicesSwapped )
+  {
+    std::swap( selectionStart, selectionEnd );
+  }
+
+  // Get the indices to the first and last selected glyphs.
+  const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
+  const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+  const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
+  const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
+
+  // Get the lines where the glyphs are laid-out.
+  const LineRun* lineRun = visualModel->mLines.Begin();
+
+  LineIndex lineIndex = 0u;
+  Length numberOfLines = 0u;
+  visualModel->GetNumberOfLines( glyphStart,
+                                 1u + glyphEnd - glyphStart,
+                                 lineIndex,
+                                 numberOfLines );
+  const LineIndex firstLineIndex = lineIndex;
+
+  // Create the structure to store some selection box info.
+  Vector<SelectionBoxInfo> selectionBoxLinesInfo;
+  selectionBoxLinesInfo.Resize( numberOfLines );
+
+  SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
+  selectionBoxInfo->minX = MAX_FLOAT;
+  selectionBoxInfo->maxX = MIN_FLOAT;
+
+  // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
+  float minHighlightX = std::numeric_limits<float>::max();
+  float maxHighlightX = std::numeric_limits<float>::min();
+  Size highLightSize;
+  Vector2 highLightPosition; // The highlight position in decorator's coords.
+
+  // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
+
+  // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
+  selectionBoxInfo->lineOffset = CalculateLineOffset( visualModel->mLines,
+                                                      firstLineIndex );
+
+  // Transform to decorator's (control) coords.
+  selectionBoxInfo->lineOffset += model->mScrollPosition.y;
+
+  lineRun += firstLineIndex;
+
+  // The line height is the addition of the line ascender and the line descender.
+  // However, the line descender has a negative value, hence the subtraction.
+  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+
+  GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+
+  // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+  const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
+  bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionStart ) );
+
+  // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+  const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
+  bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionEndMinusOne ) );
+
+  // The number of quads of the selection box.
+  const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
+  decorator->ResizeHighlightQuads( numberOfQuads );
+
+  // Count the actual number of quads.
+  unsigned int actualNumberOfQuads = 0u;
+  Vector4 quad;
+
+  // Traverse the glyphs.
+  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+  {
+    const GlyphInfo& glyph = *( glyphsBuffer + index );
+    const Vector2& position = *( positionsBuffer + index );
+
+    if( splitStartGlyph )
+    {
+      // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
+      const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
+      // Get the direction of the character.
+      CharacterDirection isCurrentRightToLeft = false;
+      if( nullptr != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+      {
+        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
+      }
+
+      // The end point could be in the middle of the ligature.
+      // Calculate the number of characters selected.
+      const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
+
+      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
+      quad.y = selectionBoxInfo->lineOffset;
+      quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
+      quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
+
+      // Store the min and max 'x' for each line.
+      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+      decorator->AddHighlight( actualNumberOfQuads, quad );
+      ++actualNumberOfQuads;
+
+      splitStartGlyph = false;
+      continue;
+    }
+
+    if( splitEndGlyph && ( index == glyphEnd ) )
+    {
+      // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
+      const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
+      // Get the direction of the character.
+      CharacterDirection isCurrentRightToLeft = false;
+      if( nullptr != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+      {
+        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
+      }
+
+      const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
+
+      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
+      quad.y = selectionBoxInfo->lineOffset;
+      quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
+      quad.w = quad.y + selectionBoxInfo->lineHeight;
+
+      // Store the min and max 'x' for each line.
+      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      splitEndGlyph = false;
+      continue;
+    }
+
+    quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x;
+    quad.y = selectionBoxInfo->lineOffset;
+    quad.z = quad.x + glyph.advance;
+    quad.w = quad.y + selectionBoxInfo->lineHeight;
+
+    // Store the min and max 'x' for each line.
+    selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+    selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+    decorator->AddHighlight( actualNumberOfQuads,
+                                          quad );
+    ++actualNumberOfQuads;
+
+    // Whether to retrieve the next line.
+    if( index == lastGlyphOfLine )
+    {
+      ++lineIndex;
+      if( lineIndex < firstLineIndex + numberOfLines )
+      {
+        // Retrieve the next line.
+        ++lineRun;
+
+        // Get the last glyph of the new line.
+        lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+
+        // Keep the offset and height of the current selection box.
+        const float currentLineOffset = selectionBoxInfo->lineOffset;
+        const float currentLineHeight = selectionBoxInfo->lineHeight;
+
+        // Get the selection box info for the next line.
+        ++selectionBoxInfo;
+
+        selectionBoxInfo->minX = MAX_FLOAT;
+        selectionBoxInfo->maxX = MIN_FLOAT;
+
+        // Update the line's vertical offset.
+        selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
+
+        // The line height is the addition of the line ascender and the line descender.
+        // However, the line descender has a negative value, hence the subtraction.
+        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+      }
+    }
+  }
+
+  // Traverses all the lines and updates the min and max 'x' positions and the total height.
+  // The final width is calculated after 'boxifying' the selection.
+  for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
+         endIt = selectionBoxLinesInfo.End();
+       it != endIt;
+       ++it )
+  {
+    const SelectionBoxInfo& info = *it;
+
+    // Update the size of the highlighted text.
+    highLightSize.height += info.lineHeight;
+    minHighlightX = std::min( minHighlightX, info.minX );
+    maxHighlightX = std::max( maxHighlightX, info.maxX );
+  }
+
+  // Add extra geometry to 'boxify' the selection.
+
+  if( 1u < numberOfLines )
+  {
+    // Boxify the first line.
+    lineRun = visualModel->mLines.Begin() + firstLineIndex;
+    const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
+
+    bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
+    bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
+
+    if( boxifyBegin )
+    {
+      quad.x = 0.f;
+      quad.y = firstSelectionBoxLineInfo.lineOffset;
+      quad.z = firstSelectionBoxLineInfo.minX;
+      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the beginning of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+    }
+
+    if( boxifyEnd )
+    {
+      quad.x = firstSelectionBoxLineInfo.maxX;
+      quad.y = firstSelectionBoxLineInfo.lineOffset;
+      quad.z = visualModel->mControlSize.width;
+      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the end of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+
+    // Boxify the central lines.
+    if( 2u < numberOfLines )
+    {
+      for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
+             endIt = selectionBoxLinesInfo.End() - 1u;
+           it != endIt;
+           ++it )
+      {
+        const SelectionBoxInfo& info = *it;
+
+        quad.x = 0.f;
+        quad.y = info.lineOffset;
+        quad.z = info.minX;
+        quad.w = info.lineOffset + info.lineHeight;
+
+        decorator->AddHighlight( actualNumberOfQuads,
+                                              quad );
+        ++actualNumberOfQuads;
+
+        quad.x = info.maxX;
+        quad.y = info.lineOffset;
+        quad.z = visualModel->mControlSize.width;
+        quad.w = info.lineOffset + info.lineHeight;
+
+        decorator->AddHighlight( actualNumberOfQuads,
+                                              quad );
+        ++actualNumberOfQuads;
+      }
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+
+    // Boxify the last line.
+    lineRun = visualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
+    const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
+
+    boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
+    boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
+
+    if( boxifyBegin )
+    {
+      quad.x = 0.f;
+      quad.y = lastSelectionBoxLineInfo.lineOffset;
+      quad.z = lastSelectionBoxLineInfo.minX;
+      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the beginning of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+    }
+
+    if( boxifyEnd )
+    {
+      quad.x = lastSelectionBoxLineInfo.maxX;
+      quad.y = lastSelectionBoxLineInfo.lineOffset;
+      quad.z = visualModel->mControlSize.width;
+      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the end of the line.
+      decorator->AddHighlight(actualNumberOfQuads, quad);
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+  }
+
+  // Set the actual number of quads.
+  decorator->ResizeHighlightQuads( actualNumberOfQuads );
+
+  // Sets the highlight's size and position. In decorator's coords.
+  // The highlight's height has been calculated above (before 'boxifying' the highlight).
+  highLightSize.width = maxHighlightX - minHighlightX;
+
+  highLightPosition.x = minHighlightX;
+  const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
+  highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
+
+  decorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( model->GetOutlineWidth() ) );
+
+  if( !decorator->IsSmoothHandlePanEnabled() )
+  {
+    CursorInfo primaryCursorInfo;
+    impl.GetCursorPosition(eventData->mLeftSelectionPosition, primaryCursorInfo);
+
+    const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + model->mScrollPosition;
+
+    decorator->SetPosition(LEFT_SELECTION_HANDLE,
+                           primaryPosition.x,
+                           primaryCursorInfo.lineOffset + model->mScrollPosition.y,
+                           primaryCursorInfo.lineHeight );
+
+    CursorInfo secondaryCursorInfo;
+    impl.GetCursorPosition(eventData->mRightSelectionPosition, secondaryCursorInfo);
+
+    const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + model->mScrollPosition;
+
+    decorator->SetPosition(RIGHT_SELECTION_HANDLE,
+                           secondaryPosition.x,
+                           secondaryCursorInfo.lineOffset + model->mScrollPosition.y,
+                           secondaryCursorInfo.lineHeight );
+  }
+
+  // Set the flag to update the decorator.
+  eventData->mDecoratorUpdated = true;
+}
+
+void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action)
+{
+  EventData*& eventData = impl.mEventData;
+  if(nullptr == eventData)
+  {
+    // Nothing to do if there is no text input.
+    return;
+  }
+
+  if(impl.IsShowingPlaceholderText())
+  {
+    // Nothing to do if there is the place-holder text.
+    return;
+  }
+
+  ModelPtr& model = impl.mModel;
+  VisualModelPtr& visualModel = model->mVisualModel;
+  const Length numberOfGlyphs = visualModel->mGlyphs.Count();
+  const Length numberOfLines  = visualModel->mLines.Count();
+  if( ( 0 == numberOfGlyphs ) ||
+      ( 0 == numberOfLines ) )
+  {
+    // Nothing to do if there is no text.
+    return;
+  }
+
+  // Find which word was selected
+  CharacterIndex selectionStart( 0 );
+  CharacterIndex selectionEnd( 0 );
+  CharacterIndex noTextHitIndex( 0 );
+  const bool characterHit = FindSelectionIndices( visualModel,
+                                                  model->mLogicalModel,
+                                                  impl.mMetrics,
+                                                  visualX,
+                                                  visualY,
+                                                  selectionStart,
+                                                  selectionEnd,
+                                                  noTextHitIndex );
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", &impl, selectionStart, selectionEnd );
+
+  if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
+  {
+    impl.ChangeState( EventData::SELECTING );
+
+    eventData->mLeftSelectionPosition = selectionStart;
+    eventData->mRightSelectionPosition = selectionEnd;
+
+    eventData->mUpdateLeftSelectionPosition = true;
+    eventData->mUpdateRightSelectionPosition = true;
+    eventData->mUpdateHighlightBox = true;
+
+    // It may happen an InputMethodContext commit event arrives before the selection event
+    // if the InputMethodContext is in pre-edit state. The commit event will set the
+    // eventData->mUpdateCursorPosition flag to true. If it's not set back
+    // to false, the highlight box won't be updated.
+    eventData->mUpdateCursorPosition = false;
+
+    eventData->mScrollAfterUpdatePosition = ( eventData->mLeftSelectionPosition != eventData->mRightSelectionPosition );
+
+    // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
+    eventData->mPrimaryCursorPosition = std::max( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  }
+  else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
+  {
+    // Nothing to select. i.e. a white space, out of bounds
+    impl.ChangeState( EventData::EDITING_WITH_POPUP );
+
+    eventData->mPrimaryCursorPosition = noTextHitIndex;
+
+    eventData->mUpdateCursorPosition = true;
+    eventData->mUpdateGrabHandlePosition = true;
+    eventData->mScrollAfterUpdatePosition = true;
+    eventData->mUpdateInputStyle = true;
+  }
+  else if( Controller::NoTextTap::NO_ACTION == action )
+  {
+    // Nothing to select. i.e. a white space, out of bounds
+    eventData->mPrimaryCursorPosition = noTextHitIndex;
+
+    eventData->mUpdateCursorPosition = true;
+    eventData->mUpdateGrabHandlePosition = true;
+    eventData->mScrollAfterUpdatePosition = true;
+    eventData->mUpdateInputStyle = true;
+  }
+}
+
+void SelectionHandleController::Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo)
+{
+  if( ( LEFT_SELECTION_HANDLE != handleType ) &&
+      ( RIGHT_SELECTION_HANDLE != handleType ) )
+  {
+    return;
+  }
+
+  ModelPtr& model = impl.mModel;
+  const Vector2 cursorPosition = cursorInfo.primaryPosition + model->mScrollPosition;
+
+  // Sets the handle's position.
+  EventData*& eventData = impl.mEventData;
+  eventData->mDecorator->SetPosition(handleType,
+                                     cursorPosition.x,
+                                     cursorInfo.lineOffset + model->mScrollPosition.y,
+                                     cursorInfo.lineHeight );
+
+  // If selection handle at start of the text and other at end of the text then all text is selected.
+  const CharacterIndex startOfSelection = std::min( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  const CharacterIndex endOfSelection = std::max ( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  eventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == model->mLogicalModel->mText.Count() );
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/text-selection-handle-controller.h b/dali-toolkit/internal/text/text-selection-handle-controller.h
new file mode 100644 (file)
index 0000000..2988c85
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
+#define DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
+
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/decorator/text-decorator.h>
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-controller.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+/**
+ * @brief Updates the Selection Handles.
+ */
+struct SelectionHandleController
+{
+  /// @copydoc Controller::Impl::RepositionSelectionHandles
+  /// @param[in] impl The Text controller Impl.
+  static void Reposition(Controller::Impl& impl);
+
+  /// @copydoc Controller::Impl::RepositionSelectionHandles
+  /// @param[in] impl The Text controller Impl.
+  static void Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action);
+
+  /// @copydoc Controller::Impl::UpdateSelectionHandle
+  /// @param[in] impl The Text controller Impl.
+  static void Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo);
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
index 93c0966..a2d102a 100644 (file)
@@ -95,7 +95,8 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual( VisualFactoryCache& factor
   mPlayState( DevelImageVisual::PlayState::STOPPED ),
   mEventCallback( nullptr ),
   mRendererAdded( false ),
-  mCoreShutdown(false)
+  mCoreShutdown(false),
+  mRedrawInScalingDown(true)
 {
   // the rasterized image is with pre-multiplied alpha format
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
@@ -172,6 +173,7 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap( Property::Map& map ) const
 
   map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mAnimationData.stopBehavior );
   map.Insert( Toolkit::DevelImageVisual::Property::LOOPING_MODE, mAnimationData.loopingMode );
+  map.Insert( Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, mRedrawInScalingDown );
 
   Property::Map layerInfo;
   mVectorAnimationTask->GetLayerInfo( layerInfo );
@@ -211,6 +213,10 @@ void AnimatedVectorImageVisual::DoSetProperties( const Property::Map& propertyMa
        {
           DoSetProperty( Toolkit::DevelImageVisual::Property::LOOPING_MODE, keyValue.second );
        }
+       else if( keyValue.first == REDRAW_IN_SCALING_DOWN_NAME )
+       {
+          DoSetProperty( Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, keyValue.second );
+       }
     }
   }
 
@@ -261,6 +267,15 @@ void AnimatedVectorImageVisual::DoSetProperty( Property::Index index, const Prop
       }
       break;
     }
+    case Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN:
+    {
+      bool redraw;
+      if( value.Get( redraw ) )
+      {
+        mRedrawInScalingDown = redraw;
+      }
+      break;
+    }
   }
 }
 
@@ -531,15 +546,19 @@ void AnimatedVectorImageVisual::OnScaleNotification( PropertyNotification& sourc
   if( actor )
   {
     Vector3 scale = actor.GetProperty< Vector3 >( Actor::Property::WORLD_SCALE );
-    mVisualScale.width = scale.width;
-    mVisualScale.height = scale.height;
 
-    DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnScaleNotification: scale = %f, %f [%p]\n", mVisualScale.width, mVisualScale.height, this );
+    if(mRedrawInScalingDown || scale.width >= 1.0f || scale.height >= 1.0f)
+    {
+      mVisualScale.width = scale.width;
+      mVisualScale.height = scale.height;
 
-    SetVectorImageSize();
-    SendAnimationData();
+      DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnScaleNotification: scale = %f, %f [%p]\n", mVisualScale.width, mVisualScale.height, this );
 
-    Stage::GetCurrent().KeepRendering( 0.0f );  // Trigger event processing
+      SetVectorImageSize();
+      SendAnimationData();
+
+      Stage::GetCurrent().KeepRendering( 0.0f );  // Trigger event processing
+    }
   }
 }
 
index 6196e39..5bf0f4e 100644 (file)
@@ -229,6 +229,7 @@ private:
   CallbackBase*                                mEventCallback;    // Not owned
   bool                                         mRendererAdded;
   bool                                         mCoreShutdown;
+  bool                                         mRedrawInScalingDown;
 };
 
 } // namespace Internal
index fa1b1fd..b9b78d0 100644 (file)
@@ -120,6 +120,7 @@ const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
 const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
 const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
 const char * const ALPHA_MASK_URL("alphaMaskUrl");
+const char * const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown");
 
 // Text visual
 const char * const TEXT_PROPERTY( "text" );
index f972f37..c30033a 100644 (file)
@@ -104,6 +104,7 @@ extern const char * const IMAGE_SAMPLING_MODE;
 extern const char * const IMAGE_DESIRED_WIDTH;
 extern const char * const IMAGE_DESIRED_HEIGHT;
 extern const char * const ALPHA_MASK_URL;
+extern const char * const REDRAW_IN_SCALING_DOWN_NAME;
 
 // Text visual
 extern const char * const TEXT_PROPERTY;
index 9fa27ae..8ac2007 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 3;
+const unsigned int TOOLKIT_MICRO_VERSION = 4;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index fd08b0b..f035215 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.3
+Version:    2.0.4
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT