(Text Controller Impl) Moved event handling related methods into a separate struct 51/245751/5
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 15 Oct 2020 08:57:27 +0000 (09:57 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 22 Oct 2020 18:08:50 +0000 (19:08 +0100)
Change-Id: I0e9f942f7dce110467dd283c08859f6c97e2b4e6

dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp [new file with mode: 0644]
dali-toolkit/internal/text/text-controller-impl-event-handler.h [new file with mode: 0644]
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h

index 0d6b781..72a5626 100644 (file)
@@ -142,6 +142,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/text-controller.cpp
    ${toolkit_src_dir}/text/text-controller-event-handler.cpp
    ${toolkit_src_dir}/text/text-controller-impl.cpp
+   ${toolkit_src_dir}/text/text-controller-impl-event-handler.cpp
    ${toolkit_src_dir}/text/text-controller-input-font-handler.cpp
    ${toolkit_src_dir}/text/text-controller-placeholder-handler.cpp
    ${toolkit_src_dir}/text/text-effects-style.cpp
diff --git a/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp b/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
new file mode 100644 (file)
index 0000000..7b50a5c
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * 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-controller-impl-event-handler.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/key.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+
+using namespace Dali;
+
+namespace
+{
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+#endif
+
+} // namespace
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
+{
+  if( NULL == impl.mEventData || !impl.IsShowingRealText() )
+  {
+    // Nothing to do if there is no text input.
+    return;
+  }
+
+  int keyCode = event.p1.mInt;
+  bool isShiftModifier = event.p2.mBool;
+  EventData& eventData = *impl.mEventData;
+  ModelPtr& model = impl.mModel;
+  LogicalModelPtr& logicalModel = model->mLogicalModel;
+  VisualModelPtr& visualModel = model->mVisualModel;
+
+  CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
+  CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
+
+  if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
+  {
+    if( primaryCursorPosition > 0u )
+    {
+      if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
+      {
+        primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
+      else
+      {
+        primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u );
+      }
+    }
+  }
+  else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
+  {
+    if( logicalModel->mText.Count() > primaryCursorPosition )
+    {
+      if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
+      {
+        primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
+      else
+      {
+        primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition );
+      }
+    }
+  }
+  else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
+  {
+    // Ignore Shift-Up for text selection for now.
+
+    // Get first the line index of the current cursor position index.
+    CharacterIndex characterIndex = 0u;
+
+    if( primaryCursorPosition > 0u )
+    {
+      characterIndex = primaryCursorPosition - 1u;
+    }
+
+    const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
+    const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
+
+    // Retrieve the cursor position info.
+    CursorInfo cursorInfo;
+    impl.GetCursorPosition( primaryCursorPosition,
+                            cursorInfo );
+
+    // Get the line above.
+    const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex );
+
+    // Get the next hit 'y' point.
+    const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
+
+    // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+    bool matchedCharacter = false;
+    primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
+                                                         logicalModel,
+                                                         impl.mMetrics,
+                                                         eventData.mCursorHookPositionX,
+                                                         hitPointY,
+                                                         CharacterHitTest::TAP,
+                                                         matchedCharacter );
+  }
+  else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
+  {
+    // Ignore Shift-Down for text selection for now.
+
+    // Get first the line index of the current cursor position index.
+    CharacterIndex characterIndex = 0u;
+
+    if( primaryCursorPosition > 0u )
+    {
+      characterIndex = primaryCursorPosition - 1u;
+    }
+
+    const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
+
+    if( lineIndex + 1u < visualModel->mLines.Count() )
+    {
+      // Retrieve the cursor position info.
+      CursorInfo cursorInfo;
+      impl.GetCursorPosition( primaryCursorPosition, cursorInfo );
+
+      // Get the line below.
+      const LineRun& line = *( visualModel->mLines.Begin() + lineIndex + 1u );
+
+      // Get the next hit 'y' point.
+      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
+
+      // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
+      bool matchedCharacter = false;
+      primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
+                                                           logicalModel,
+                                                           impl.mMetrics,
+                                                           eventData.mCursorHookPositionX,
+                                                           hitPointY,
+                                                           CharacterHitTest::TAP,
+                                                           matchedCharacter );
+    }
+  }
+
+  if ( !isShiftModifier && eventData.mState != EventData::SELECTING )
+  {
+    // Update selection position after moving the cursor
+    eventData.mLeftSelectionPosition = primaryCursorPosition;
+    eventData.mRightSelectionPosition = primaryCursorPosition;
+  }
+
+  if ( isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag )
+  {
+    // Handle text selection
+    bool selecting = false;
+
+    if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
+    {
+      // Shift-Left/Right to select the text
+      int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
+      if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary
+      {
+        eventData.mRightSelectionPosition += cursorPositionDelta;
+      }
+      selecting = true;
+    }
+    else if ( eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition )
+    {
+      // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
+      selecting = true;
+    }
+
+    if ( selecting )
+    {
+      // Notify the cursor position to the InputMethodContext.
+      if( eventData.mInputMethodContext )
+      {
+        eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition );
+        eventData.mInputMethodContext.NotifyCursorPosition();
+      }
+
+      impl.ChangeState( EventData::SELECTING );
+
+      eventData.mUpdateLeftSelectionPosition = true;
+      eventData.mUpdateRightSelectionPosition = true;
+      eventData.mUpdateGrabHandlePosition = true;
+      eventData.mUpdateHighlightBox = true;
+
+      // Hide the text selection popup if select the text using keyboard instead of moving grab handles
+      if( eventData.mGrabHandlePopupEnabled )
+      {
+        eventData.mDecorator->SetPopupActive( false );
+      }
+    }
+  }
+  else
+  {
+    // Handle normal cursor move
+    impl.ChangeState( EventData::EDITING );
+    eventData.mUpdateCursorPosition = true;
+  }
+
+  eventData.mUpdateInputStyle = true;
+  eventData.mScrollAfterUpdatePosition = true;
+}
+
+void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
+{
+  if( impl.mEventData )
+  {
+    const unsigned int tapCount = event.p1.mUint;
+    EventData& eventData = *impl.mEventData;
+    ModelPtr& model = impl.mModel;
+    LogicalModelPtr& logicalModel = model->mLogicalModel;
+    VisualModelPtr& visualModel = model->mVisualModel;
+
+    if( 1u == tapCount )
+    {
+      if( impl.IsShowingRealText() )
+      {
+        // Convert from control's coords to text's coords.
+        const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
+        const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
+
+        // Keep the tap 'x' position. Used to move the cursor.
+        eventData.mCursorHookPositionX = xPosition;
+
+        // Whether to touch point hits on a glyph.
+        bool matchedCharacter = false;
+        eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
+                                                                        logicalModel,
+                                                                        impl.mMetrics,
+                                                                        xPosition,
+                                                                        yPosition,
+                                                                        CharacterHitTest::TAP,
+                                                                        matchedCharacter );
+
+        // When the cursor position is changing, delay cursor blinking
+        eventData.mDecorator->DelayCursorBlink();
+      }
+      else
+      {
+        eventData.mPrimaryCursorPosition = 0u;
+      }
+
+      // Update selection position after tapping
+      eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
+      eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
+
+      eventData.mUpdateCursorPosition = true;
+      eventData.mUpdateGrabHandlePosition = true;
+      eventData.mScrollAfterUpdatePosition = true;
+      eventData.mUpdateInputStyle = true;
+
+      // Notify the cursor position to the InputMethodContext.
+      if( eventData.mInputMethodContext )
+      {
+        eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition );
+        eventData.mInputMethodContext.NotifyCursorPosition();
+      }
+    }
+    else if( 2u == tapCount )
+    {
+      if( eventData.mSelectionEnabled )
+      {
+        // Convert from control's coords to text's coords.
+        const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
+        const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
+
+        // Calculates the logical position from the x,y coords.
+        impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mDoubleTapAction );
+      }
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
+{
+  if( impl.mEventData )
+  {
+    EventData& eventData = *impl.mEventData;
+    DecoratorPtr& decorator = eventData.mDecorator;
+
+    const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
+    const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
+
+    if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
+    {
+      // Nothing to do if scrolling is not enabled.
+      return;
+    }
+
+    const GestureState state = static_cast<GestureState>( event.p1.mInt );
+    switch( state )
+    {
+      case GestureState::STARTED:
+      {
+        // Will remove the cursor, handles or text's popup, ...
+        impl.ChangeState( EventData::TEXT_PANNING );
+        break;
+      }
+      case GestureState::CONTINUING:
+      {
+        ModelPtr& model = impl.mModel;
+
+        const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
+        Vector2& scrollPosition = model->mScrollPosition;
+        const Vector2 currentScroll = scrollPosition;
+
+        if( isHorizontalScrollEnabled )
+        {
+          const float displacementX = event.p2.mFloat;
+          scrollPosition.x += displacementX;
+
+          impl.ClampHorizontalScroll( layoutSize );
+        }
+
+        if( isVerticalScrollEnabled )
+        {
+          const float displacementY = event.p3.mFloat;
+          scrollPosition.y += displacementY;
+
+          impl.ClampVerticalScroll( layoutSize );
+        }
+
+        decorator->UpdatePositions( scrollPosition - currentScroll );
+        break;
+      }
+      case GestureState::FINISHED:
+      case GestureState::CANCELLED: // FALLTHROUGH
+      {
+        // Will go back to the previous state to show the cursor, handles, the text's popup, ...
+        impl.ChangeState( eventData.mPreviousState );
+        break;
+      }
+      default:
+        break;
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
+
+  if( impl.mEventData )
+  {
+    EventData& eventData = *impl.mEventData;
+
+    if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) )
+    {
+      impl.ChangeState( EventData::EDITING_WITH_POPUP );
+      eventData.mDecoratorUpdated = true;
+      eventData.mUpdateInputStyle = true;
+    }
+    else
+    {
+      if( eventData.mSelectionEnabled )
+      {
+        ModelPtr& model = impl.mModel;
+
+        // Convert from control's coords to text's coords.
+        const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
+        const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
+
+        // Calculates the logical position from the x,y coords.
+        impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mLongPressAction );
+      }
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
+{
+  if( impl.mEventData )
+  {
+    const unsigned int state = event.p1.mUint;
+    const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
+    const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
+
+    if( HANDLE_PRESSED == state )
+    {
+      OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
+    } // end ( HANDLE_PRESSED == state )
+    else if( ( HANDLE_RELEASED == state ) ||
+             handleStopScrolling )
+    {
+      OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
+    } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
+    else if( HANDLE_SCROLLING == state )
+    {
+      OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
+    } // end ( HANDLE_SCROLLING == state )
+  }
+}
+
+void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event )
+{
+  if( impl.mEventData && impl.mEventData->mSelectionEnabled )
+  {
+    ModelPtr& model = impl.mModel;
+    const Vector2& scrollPosition = model->mScrollPosition;
+
+    // Convert from control's coords to text's coords.
+    const float xPosition = event.p2.mFloat - scrollPosition.x;
+    const float yPosition = event.p3.mFloat - scrollPosition.y;
+
+    // Calculates the logical position from the x,y coords.
+    impl.RepositionSelectionHandles( xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT );
+  }
+}
+
+void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
+
+  if( impl.mEventData )
+  {
+    EventData& eventData = *impl.mEventData;
+    if( eventData.mSelectionEnabled )
+    {
+      ModelPtr& model = impl.mModel;
+      const Vector2& scrollPosition = model->mScrollPosition;
+
+      // Calculates the logical position from the start.
+      impl.RepositionSelectionHandles( 0.f - scrollPosition.x,
+                                       0.f - scrollPosition.y,
+                                       Controller::NoTextTap::HIGHLIGHT );
+
+      eventData.mLeftSelectionPosition = 0u;
+      eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
+
+  if( impl.mEventData )
+  {
+    EventData& eventData = *impl.mEventData;
+    if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
+    {
+      eventData.mPrimaryCursorPosition = 0u;
+      eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
+      impl.ChangeState( EventData::INACTIVE );
+      eventData.mUpdateCursorPosition = true;
+      eventData.mUpdateInputStyle = true;
+      eventData.mScrollAfterUpdatePosition = true;
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
+{
+  ModelPtr& model = impl.mModel;
+  const Vector2& scrollPosition = model->mScrollPosition;
+
+  // Convert from decorator's coords to text's coords.
+  const float xPosition = event.p2.mFloat - scrollPosition.x;
+  const float yPosition = event.p3.mFloat - scrollPosition.y;
+
+  // Need to calculate the handle's new position.
+  bool matchedCharacter = false;
+  const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( model->mVisualModel,
+                                                                        model->mLogicalModel,
+                                                                        impl.mMetrics,
+                                                                        xPosition,
+                                                                        yPosition,
+                                                                        CharacterHitTest::SCROLL,
+                                                                        matchedCharacter );
+
+  EventData& eventData = *impl.mEventData;
+
+  if( Event::GRAB_HANDLE_EVENT == event.type )
+  {
+    impl.ChangeState ( EventData::GRAB_HANDLE_PANNING );
+
+    if( handleNewPosition != eventData.mPrimaryCursorPosition )
+    {
+      // Updates the cursor position if the handle's new position is different than the current one.
+      eventData.mUpdateCursorPosition = true;
+      // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
+      eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
+      eventData.mPrimaryCursorPosition = handleNewPosition;
+    }
+
+    // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
+    eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
+  }
+  else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+  {
+    impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
+
+    if( ( handleNewPosition != eventData.mLeftSelectionPosition ) &&
+        ( handleNewPosition != eventData.mRightSelectionPosition ) )
+    {
+      // Updates the highlight box if the handle's new position is different than the current one.
+      eventData.mUpdateHighlightBox = true;
+      // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
+      eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
+      eventData.mLeftSelectionPosition = handleNewPosition;
+    }
+
+    // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
+    eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
+
+    // Will define the order to scroll the text to match the handle position.
+    eventData.mIsLeftHandleSelected = true;
+    eventData.mIsRightHandleSelected = false;
+  }
+  else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
+  {
+    impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
+
+    if( ( handleNewPosition != eventData.mRightSelectionPosition ) &&
+        ( handleNewPosition != eventData.mLeftSelectionPosition ) )
+    {
+      // Updates the highlight box if the handle's new position is different than the current one.
+      eventData.mUpdateHighlightBox = true;
+      // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
+      eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
+      eventData.mRightSelectionPosition = handleNewPosition;
+    }
+
+    // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
+    eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
+
+    // Will define the order to scroll the text to match the handle position.
+    eventData.mIsLeftHandleSelected = false;
+    eventData.mIsRightHandleSelected = true;
+  }
+}
+
+void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
+{
+  CharacterIndex handlePosition = 0u;
+  if( handleStopScrolling || isSmoothHandlePanEnabled )
+  {
+    ModelPtr& model = impl.mModel;
+    const Vector2& scrollPosition = model->mScrollPosition;
+
+    // Convert from decorator's coords to text's coords.
+    const float xPosition = event.p2.mFloat - scrollPosition.x;
+    const float yPosition = event.p3.mFloat - scrollPosition.y;
+
+    bool matchedCharacter = false;
+    handlePosition = Text::GetClosestCursorIndex( model->mVisualModel,
+                                                  model->mLogicalModel,
+                                                  impl.mMetrics,
+                                                  xPosition,
+                                                  yPosition,
+                                                  CharacterHitTest::SCROLL,
+                                                  matchedCharacter );
+  }
+
+  EventData& eventData = *impl.mEventData;
+
+  if( Event::GRAB_HANDLE_EVENT == event.type )
+  {
+    eventData.mUpdateCursorPosition = true;
+    eventData.mUpdateGrabHandlePosition = true;
+    eventData.mUpdateInputStyle = true;
+
+    if( !impl.IsClipboardEmpty() )
+    {
+      impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
+    }
+
+    if( handleStopScrolling || isSmoothHandlePanEnabled )
+    {
+      eventData.mScrollAfterUpdatePosition = true;
+      eventData.mPrimaryCursorPosition = handlePosition;
+    }
+  }
+  else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+  {
+    impl.ChangeState( EventData::SELECTING );
+
+    eventData.mUpdateHighlightBox = true;
+    eventData.mUpdateLeftSelectionPosition = true;
+    eventData.mUpdateRightSelectionPosition = true;
+
+    if( handleStopScrolling || isSmoothHandlePanEnabled )
+    {
+      eventData.mScrollAfterUpdatePosition = true;
+
+      if( ( handlePosition != eventData.mRightSelectionPosition ) &&
+          ( handlePosition != eventData.mLeftSelectionPosition ) )
+      {
+        eventData.mLeftSelectionPosition = handlePosition;
+      }
+    }
+  }
+  else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
+  {
+    impl.ChangeState( EventData::SELECTING );
+
+    eventData.mUpdateHighlightBox = true;
+    eventData.mUpdateRightSelectionPosition = true;
+    eventData.mUpdateLeftSelectionPosition = true;
+
+    if( handleStopScrolling || isSmoothHandlePanEnabled )
+    {
+      eventData.mScrollAfterUpdatePosition = true;
+      if( ( handlePosition != eventData.mRightSelectionPosition ) &&
+          ( handlePosition != eventData.mLeftSelectionPosition ) )
+      {
+        eventData.mRightSelectionPosition = handlePosition;
+      }
+    }
+  }
+
+  eventData.mDecoratorUpdated = true;
+}
+
+void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
+{
+  ModelPtr& model = impl.mModel;
+  Vector2& scrollPosition = model->mScrollPosition;
+  VisualModelPtr& visualModel = model->mVisualModel;
+
+  const float xSpeed = event.p2.mFloat;
+  const float ySpeed = event.p3.mFloat;
+  const Vector2& layoutSize = visualModel->GetLayoutSize();
+  const Vector2 currentScrollPosition = scrollPosition;
+
+  scrollPosition.x += xSpeed;
+  scrollPosition.y += ySpeed;
+
+  impl.ClampHorizontalScroll( layoutSize );
+  impl.ClampVerticalScroll( layoutSize );
+
+  EventData& eventData = *impl.mEventData;
+  DecoratorPtr& decorator = eventData.mDecorator;
+
+  bool endOfScroll = false;
+  if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) )
+  {
+    // Notify the decorator there is no more text to scroll.
+    // The decorator won't send more scroll events.
+    decorator->NotifyEndOfScroll();
+    // Still need to set the position of the handle.
+    endOfScroll = true;
+  }
+
+  // Set the position of the handle.
+  const bool scrollRightDirection = xSpeed > 0.f;
+  const bool scrollBottomDirection = ySpeed > 0.f;
+  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 )
+  {
+    impl.ChangeState( EventData::GRAB_HANDLE_PANNING );
+
+    // Get the grab handle position in decorator coords.
+    Vector2 position = decorator->GetPosition( GRAB_HANDLE );
+
+    if( decorator->IsHorizontalScrollEnabled() )
+    {
+      // Position the grag handle close to either the left or right edge.
+      position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
+    }
+
+    if( decorator->IsVerticalScrollEnabled() )
+    {
+      position.x = eventData.mCursorHookPositionX;
+
+      // Position the grag handle close to either the top or bottom edge.
+      position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
+    }
+
+    // Get the new handle position.
+    // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
+    bool matchedCharacter = false;
+    const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
+                                                                       impl.mModel->mLogicalModel,
+                                                                       impl.mMetrics,
+                                                                       position.x - scrollPosition.x,
+                                                                       position.y - scrollPosition.y,
+                                                                       CharacterHitTest::SCROLL,
+                                                                       matchedCharacter );
+
+    if( eventData.mPrimaryCursorPosition != handlePosition )
+    {
+      eventData.mUpdateCursorPosition = true;
+      eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
+      eventData.mScrollAfterUpdatePosition = true;
+      eventData.mPrimaryCursorPosition = handlePosition;
+    }
+    eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
+
+    // Updates the decorator if the soft handle panning is enabled.
+    eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
+  }
+  else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
+  {
+    impl.ChangeState( EventData::SELECTION_HANDLE_PANNING );
+
+    // Get the selection handle position in decorator coords.
+    Vector2 position = decorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
+
+    if( decorator->IsHorizontalScrollEnabled() )
+    {
+      // Position the selection handle close to either the left or right edge.
+      position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
+    }
+
+    if( decorator->IsVerticalScrollEnabled() )
+    {
+      position.x = eventData.mCursorHookPositionX;
+
+      // Position the grag handle close to either the top or bottom edge.
+      position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
+    }
+
+    // Get the new handle position.
+    // The selection handle's position is in decorator's coords. Need to transform to text's coords.
+    bool matchedCharacter = false;
+    const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
+                                                                       impl.mModel->mLogicalModel,
+                                                                       impl.mMetrics,
+                                                                       position.x - scrollPosition.x,
+                                                                       position.y - scrollPosition.y,
+                                                                       CharacterHitTest::SCROLL,
+                                                                       matchedCharacter );
+
+    if( leftSelectionHandleEvent )
+    {
+      const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition );
+
+      if( differentHandles || endOfScroll )
+      {
+        eventData.mUpdateHighlightBox = true;
+        eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
+        eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
+        eventData.mLeftSelectionPosition = handlePosition;
+      }
+    }
+    else
+    {
+      const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition );
+      if( differentHandles || endOfScroll )
+      {
+        eventData.mUpdateHighlightBox = true;
+        eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
+        eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
+        eventData.mRightSelectionPosition = handlePosition;
+      }
+    }
+
+    if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition )
+    {
+      impl.RepositionSelectionHandles();
+
+      eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
+    }
+  }
+  eventData.mDecoratorUpdated = true;
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/text-controller-impl-event-handler.h b/dali-toolkit/internal/text/text-controller-impl-event-handler.h
new file mode 100644 (file)
index 0000000..52ac94a
--- /dev/null
@@ -0,0 +1,139 @@
+#ifndef DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_H
+#define DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_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/text-controller-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+/**
+ * Contains all the event handling methods for Text::Controller::Impl
+ */
+struct ControllerImplEventHandler
+{
+  /**
+   * @brief Called by Controller::Impl when a cursor key event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnCursorKeyEvent(Controller::Impl& controllerImpl, const Event& event);
+
+  /**
+   * @brief Called by Controller::Impl when a tap event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnTapEvent(Controller::Impl& controllerImpl, const Event& event);
+
+  /**
+   * @brief Called by Controller::Impl when a pan event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnPanEvent(Controller::Impl& controllerImpl, const Event& event);
+
+  /**
+   * @brief Called by Controller::Impl when a long press event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnLongPressEvent(Controller::Impl& controllerImpl, const Event& event);
+
+  /**
+   * @brief Called by Controller::Impl when a handle event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnHandleEvent(Controller::Impl& controllerImpl, const Event& event);
+
+  /**
+   * @brief Called by Controller::Impl when a select event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnSelectEvent(Controller::Impl& controllerImpl, const Event& event );
+
+  /**
+   * @brief Called by Controller::Impl when a select all event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnSelectAllEvent(Controller::Impl& controllerImpl);
+
+  /**
+   * @brief Called by Controller::Impl when a select none event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnSelectNoneEvent(Controller::Impl& controllerImpl);
+
+private:
+
+  /**
+   * @brief Called by OnHandleEvent when we are in the Pressed state.
+   *
+   * @param impl A reference to Controller::Impl
+   * @param event The event
+   * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled
+   */
+  static void OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled);
+
+  /**
+   * @brief Called by OnHandleEvent when we are in the Released state.
+   *
+   * @param impl A reference to Controller::Impl
+   * @param event The event
+   * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled
+   * @param handleStopScrolling Whether we should handle stop scrolling or not
+   */
+  static void OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling);
+
+  /**
+   * @brief Called by OnHandleEvent when we are in the Scrolling state.
+   *
+   * @param impl A reference to Controller::Impl
+   * @param event The event
+   * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled
+   */
+  static void OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled);
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_H
index 719f220..0cd6adb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * 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.
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/text-controller-impl.h>
 
 // EXTERNAL INCLUDES
-#include <dali/public-api/adaptor-framework/key.h>
 #include <dali/public-api/rendering/renderer.h>
 #include <dali/integration-api/debug.h>
 #include <limits>
@@ -34,6 +33,7 @@
 #include <dali-toolkit/internal/text/segmentation.h>
 #include <dali-toolkit/internal/text/shaper.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>
 
 using namespace Dali;
@@ -53,7 +53,7 @@ struct SelectionBoxInfo
 };
 
 #if defined(DEBUG_ENABLED)
-  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
 const float MAX_FLOAT = std::numeric_limits<float>::max();
@@ -1341,704 +1341,42 @@ float Controller::Impl::GetDefaultFontLineHeight()
 
 void Controller::Impl::OnCursorKeyEvent( const Event& event )
 {
-  if( NULL == mEventData || !IsShowingRealText() )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  int keyCode = event.p1.mInt;
-  bool isShiftModifier = event.p2.mBool;
-
-  CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition;
-
-  if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
-  {
-    if( mEventData->mPrimaryCursorPosition > 0u )
-    {
-      if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
-      {
-        mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
-      }
-      else
-      {
-        mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
-      }
-    }
-  }
-  else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
-  {
-    if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
-    {
-      if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
-      {
-        mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
-      }
-      else
-      {
-        mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
-      }
-    }
-  }
-  else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
-  {
-    // Ignore Shift-Up for text selection for now.
-
-    // Get first the line index of the current cursor position index.
-    CharacterIndex characterIndex = 0u;
-
-    if( mEventData->mPrimaryCursorPosition > 0u )
-    {
-      characterIndex = mEventData->mPrimaryCursorPosition - 1u;
-    }
-
-    const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
-    const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
-
-    // Retrieve the cursor position info.
-    CursorInfo cursorInfo;
-    GetCursorPosition( mEventData->mPrimaryCursorPosition,
-                       cursorInfo );
-
-    // Get the line above.
-    const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
-
-    // Get the next hit 'y' point.
-    const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
-
-    // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
-    bool matchedCharacter = false;
-    mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                      mModel->mLogicalModel,
-                                                                      mMetrics,
-                                                                      mEventData->mCursorHookPositionX,
-                                                                      hitPointY,
-                                                                      CharacterHitTest::TAP,
-                                                                      matchedCharacter );
-  }
-  else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
-  {
-    // Ignore Shift-Down for text selection for now.
-
-    // Get first the line index of the current cursor position index.
-    CharacterIndex characterIndex = 0u;
-
-    if( mEventData->mPrimaryCursorPosition > 0u )
-    {
-      characterIndex = mEventData->mPrimaryCursorPosition - 1u;
-    }
-
-    const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
-
-    if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
-    {
-      // Retrieve the cursor position info.
-      CursorInfo cursorInfo;
-      GetCursorPosition( mEventData->mPrimaryCursorPosition,
-                         cursorInfo );
-
-      // Get the line below.
-      const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
-
-      // Get the next hit 'y' point.
-      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
-
-      // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
-      bool matchedCharacter = false;
-      mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                        mModel->mLogicalModel,
-                                                                        mMetrics,
-                                                                        mEventData->mCursorHookPositionX,
-                                                                        hitPointY,
-                                                                        CharacterHitTest::TAP,
-                                                                        matchedCharacter );
-    }
-  }
-
-  if ( !isShiftModifier && mEventData->mState != EventData::SELECTING )
-  {
-    // Update selection position after moving the cursor
-    mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
-    mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
-  }
-
-  if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
-  {
-    // Handle text selection
-    bool selecting = false;
-
-    if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
-    {
-      // Shift-Left/Right to select the text
-      int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition;
-      if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary
-      {
-        mEventData->mRightSelectionPosition += cursorPositionDelta;
-      }
-      selecting = true;
-    }
-    else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition )
-    {
-      // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
-      selecting = true;
-    }
-
-    if ( selecting )
-    {
-      // Notify the cursor position to the InputMethodContext.
-      if( mEventData->mInputMethodContext )
-      {
-        mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
-        mEventData->mInputMethodContext.NotifyCursorPosition();
-      }
-
-      ChangeState( EventData::SELECTING );
-
-      mEventData->mUpdateLeftSelectionPosition = true;
-      mEventData->mUpdateRightSelectionPosition = true;
-      mEventData->mUpdateGrabHandlePosition = true;
-      mEventData->mUpdateHighlightBox = true;
-
-      // Hide the text selection popup if select the text using keyboard instead of moving grab handles
-      if( mEventData->mGrabHandlePopupEnabled )
-      {
-        mEventData->mDecorator->SetPopupActive( false );
-      }
-    }
-  }
-  else
-  {
-    // Handle normal cursor move
-    ChangeState( EventData::EDITING );
-    mEventData->mUpdateCursorPosition = true;
-  }
-
-  mEventData->mUpdateInputStyle = true;
-  mEventData->mScrollAfterUpdatePosition = true;
+  ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
 }
 
 void Controller::Impl::OnTapEvent( const Event& event )
 {
-  if( NULL != mEventData )
-  {
-    const unsigned int tapCount = event.p1.mUint;
-
-    if( 1u == tapCount )
-    {
-      if( IsShowingRealText() )
-      {
-        // Convert from control's coords to text's coords.
-        const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-        const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-        // Keep the tap 'x' position. Used to move the cursor.
-        mEventData->mCursorHookPositionX = xPosition;
-
-        // Whether to touch point hits on a glyph.
-        bool matchedCharacter = false;
-        mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                          mModel->mLogicalModel,
-                                                                          mMetrics,
-                                                                          xPosition,
-                                                                          yPosition,
-                                                                          CharacterHitTest::TAP,
-                                                                          matchedCharacter );
-
-        // When the cursor position is changing, delay cursor blinking
-        mEventData->mDecorator->DelayCursorBlink();
-      }
-      else
-      {
-        mEventData->mPrimaryCursorPosition = 0u;
-      }
-
-      // Update selection position after tapping
-      mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition;
-      mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
-
-      mEventData->mUpdateCursorPosition = true;
-      mEventData->mUpdateGrabHandlePosition = true;
-      mEventData->mScrollAfterUpdatePosition = true;
-      mEventData->mUpdateInputStyle = true;
-
-      // Notify the cursor position to the InputMethodContext.
-      if( mEventData->mInputMethodContext )
-      {
-        mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
-        mEventData->mInputMethodContext.NotifyCursorPosition();
-      }
-    }
-    else if( 2u == tapCount )
-    {
-      if( mEventData->mSelectionEnabled )
-      {
-        // Convert from control's coords to text's coords.
-        const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-        const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-        // Calculates the logical position from the x,y coords.
-        RepositionSelectionHandles( xPosition,
-                                    yPosition,
-                                    mEventData->mDoubleTapAction );
-      }
-    }
-  }
+  ControllerImplEventHandler::OnTapEvent(*this, event);
 }
 
 void Controller::Impl::OnPanEvent( const Event& event )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
-  const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
-
-  if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
-  {
-    // Nothing to do if scrolling is not enabled.
-    return;
-  }
-
-  const GestureState state = static_cast<GestureState>( event.p1.mInt );
-  switch( state )
-  {
-    case GestureState::STARTED:
-    {
-      // Will remove the cursor, handles or text's popup, ...
-      ChangeState( EventData::TEXT_PANNING );
-      break;
-    }
-    case GestureState::CONTINUING:
-    {
-      const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
-      const Vector2 currentScroll = mModel->mScrollPosition;
-
-      if( isHorizontalScrollEnabled )
-      {
-        const float displacementX = event.p2.mFloat;
-        mModel->mScrollPosition.x += displacementX;
-
-        ClampHorizontalScroll( layoutSize );
-      }
-
-      if( isVerticalScrollEnabled )
-      {
-        const float displacementY = event.p3.mFloat;
-        mModel->mScrollPosition.y += displacementY;
-
-        ClampVerticalScroll( layoutSize );
-      }
-
-      mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
-      break;
-    }
-    case GestureState::FINISHED:
-    case GestureState::CANCELLED: // FALLTHROUGH
-    {
-      // Will go back to the previous state to show the cursor, handles, the text's popup, ...
-      ChangeState( mEventData->mPreviousState );
-      break;
-    }
-    default:
-      break;
-  }
+  ControllerImplEventHandler::OnPanEvent(*this, event);
 }
 
 void Controller::Impl::OnLongPressEvent( const Event& event )
 {
-  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
-
-  if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
-  {
-    ChangeState( EventData::EDITING_WITH_POPUP );
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateInputStyle = true;
-  }
-  else
-  {
-    if( mEventData->mSelectionEnabled )
-    {
-      // Convert from control's coords to text's coords.
-      const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-      const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-      // Calculates the logical position from the x,y coords.
-      RepositionSelectionHandles( xPosition,
-                                  yPosition,
-                                  mEventData->mLongPressAction );
-    }
-  }
+  ControllerImplEventHandler::OnLongPressEvent(*this, event);
 }
 
 void Controller::Impl::OnHandleEvent( const Event& event )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  const unsigned int state = event.p1.mUint;
-  const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
-  const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
-
-  if( HANDLE_PRESSED == state )
-  {
-    // Convert from decorator's coords to text's coords.
-    const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-    const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-    // Need to calculate the handle's new position.
-    bool matchedCharacter = false;
-    const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                          mModel->mLogicalModel,
-                                                                          mMetrics,
-                                                                          xPosition,
-                                                                          yPosition,
-                                                                          CharacterHitTest::SCROLL,
-                                                                          matchedCharacter );
-
-    if( Event::GRAB_HANDLE_EVENT == event.type )
-    {
-      ChangeState ( EventData::GRAB_HANDLE_PANNING );
-
-      if( handleNewPosition != mEventData->mPrimaryCursorPosition )
-      {
-        // Updates the cursor position if the handle's new position is different than the current one.
-        mEventData->mUpdateCursorPosition = true;
-        // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
-        mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
-        mEventData->mPrimaryCursorPosition = handleNewPosition;
-      }
-
-      // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
-      mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-    }
-    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
-    {
-      ChangeState ( EventData::SELECTION_HANDLE_PANNING );
-
-      if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
-          ( handleNewPosition != mEventData->mRightSelectionPosition ) )
-      {
-        // Updates the highlight box if the handle's new position is different than the current one.
-        mEventData->mUpdateHighlightBox = true;
-        // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
-        mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
-        mEventData->mLeftSelectionPosition = handleNewPosition;
-      }
-
-      // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
-      mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-
-      // Will define the order to scroll the text to match the handle position.
-      mEventData->mIsLeftHandleSelected = true;
-      mEventData->mIsRightHandleSelected = false;
-    }
-    else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
-    {
-      ChangeState ( EventData::SELECTION_HANDLE_PANNING );
-
-      if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
-          ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
-      {
-        // Updates the highlight box if the handle's new position is different than the current one.
-        mEventData->mUpdateHighlightBox = true;
-        // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
-        mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
-        mEventData->mRightSelectionPosition = handleNewPosition;
-      }
-
-      // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
-      mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-
-      // Will define the order to scroll the text to match the handle position.
-      mEventData->mIsLeftHandleSelected = false;
-      mEventData->mIsRightHandleSelected = true;
-    }
-  } // end ( HANDLE_PRESSED == state )
-  else if( ( HANDLE_RELEASED == state ) ||
-           handleStopScrolling )
-  {
-    CharacterIndex handlePosition = 0u;
-    if( handleStopScrolling || isSmoothHandlePanEnabled )
-    {
-      // Convert from decorator's coords to text's coords.
-      const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-      const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-      bool matchedCharacter = false;
-      handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                    mModel->mLogicalModel,
-                                                    mMetrics,
-                                                    xPosition,
-                                                    yPosition,
-                                                    CharacterHitTest::SCROLL,
-                                                    matchedCharacter );
-    }
-
-    if( Event::GRAB_HANDLE_EVENT == event.type )
-    {
-      mEventData->mUpdateCursorPosition = true;
-      mEventData->mUpdateGrabHandlePosition = true;
-      mEventData->mUpdateInputStyle = true;
-
-      if( !IsClipboardEmpty() )
-      {
-        ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
-      }
-
-      if( handleStopScrolling || isSmoothHandlePanEnabled )
-      {
-        mEventData->mScrollAfterUpdatePosition = true;
-        mEventData->mPrimaryCursorPosition = handlePosition;
-      }
-    }
-    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
-    {
-      ChangeState( EventData::SELECTING );
-
-      mEventData->mUpdateHighlightBox = true;
-      mEventData->mUpdateLeftSelectionPosition = true;
-      mEventData->mUpdateRightSelectionPosition = true;
-
-      if( handleStopScrolling || isSmoothHandlePanEnabled )
-      {
-        mEventData->mScrollAfterUpdatePosition = true;
-
-        if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
-            ( handlePosition != mEventData->mLeftSelectionPosition ) )
-        {
-          mEventData->mLeftSelectionPosition = handlePosition;
-        }
-      }
-    }
-    else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
-    {
-      ChangeState( EventData::SELECTING );
-
-      mEventData->mUpdateHighlightBox = true;
-      mEventData->mUpdateRightSelectionPosition = true;
-      mEventData->mUpdateLeftSelectionPosition = true;
-
-      if( handleStopScrolling || isSmoothHandlePanEnabled )
-      {
-        mEventData->mScrollAfterUpdatePosition = true;
-        if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
-            ( handlePosition != mEventData->mLeftSelectionPosition ) )
-        {
-          mEventData->mRightSelectionPosition = handlePosition;
-        }
-      }
-    }
-
-    mEventData->mDecoratorUpdated = true;
-  } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
-  else if( HANDLE_SCROLLING == state )
-  {
-    const float xSpeed = event.p2.mFloat;
-    const float ySpeed = event.p3.mFloat;
-    const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
-    const Vector2 currentScrollPosition = mModel->mScrollPosition;
-
-    mModel->mScrollPosition.x += xSpeed;
-    mModel->mScrollPosition.y += ySpeed;
-
-    ClampHorizontalScroll( layoutSize );
-    ClampVerticalScroll( layoutSize );
-
-    bool endOfScroll = false;
-    if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
-    {
-      // Notify the decorator there is no more text to scroll.
-      // The decorator won't send more scroll events.
-      mEventData->mDecorator->NotifyEndOfScroll();
-      // Still need to set the position of the handle.
-      endOfScroll = true;
-    }
-
-    // Set the position of the handle.
-    const bool scrollRightDirection = xSpeed > 0.f;
-    const bool scrollBottomDirection = ySpeed > 0.f;
-    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 );
-
-      // Get the grab handle position in decorator coords.
-      Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
-
-      if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
-      {
-        // Position the grag handle close to either the left or right edge.
-        position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
-      }
-
-      if( mEventData->mDecorator->IsVerticalScrollEnabled() )
-      {
-        position.x = mEventData->mCursorHookPositionX;
-
-        // Position the grag handle close to either the top or bottom edge.
-        position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
-      }
-
-      // Get the new handle position.
-      // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
-      bool matchedCharacter = false;
-      const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                         mModel->mLogicalModel,
-                                                                         mMetrics,
-                                                                         position.x - mModel->mScrollPosition.x,
-                                                                         position.y - mModel->mScrollPosition.y,
-                                                                         CharacterHitTest::SCROLL,
-                                                                         matchedCharacter );
-
-      if( mEventData->mPrimaryCursorPosition != handlePosition )
-      {
-        mEventData->mUpdateCursorPosition = true;
-        mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
-        mEventData->mScrollAfterUpdatePosition = true;
-        mEventData->mPrimaryCursorPosition = handlePosition;
-      }
-      mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
-
-      // Updates the decorator if the soft handle panning is enabled.
-      mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-    }
-    else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
-    {
-      ChangeState( EventData::SELECTION_HANDLE_PANNING );
-
-      // Get the selection handle position in decorator coords.
-      Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
-
-      if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
-      {
-        // Position the selection handle close to either the left or right edge.
-        position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
-      }
-
-      if( mEventData->mDecorator->IsVerticalScrollEnabled() )
-      {
-        position.x = mEventData->mCursorHookPositionX;
-
-        // Position the grag handle close to either the top or bottom edge.
-        position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
-      }
-
-      // Get the new handle position.
-      // The selection handle's position is in decorator's coords. Need to transform to text's coords.
-      bool matchedCharacter = false;
-      const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
-                                                                         mModel->mLogicalModel,
-                                                                         mMetrics,
-                                                                         position.x - mModel->mScrollPosition.x,
-                                                                         position.y - mModel->mScrollPosition.y,
-                                                                         CharacterHitTest::SCROLL,
-                                                                         matchedCharacter );
-
-      if( leftSelectionHandleEvent )
-      {
-        const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
-
-        if( differentHandles || endOfScroll )
-        {
-          mEventData->mUpdateHighlightBox = true;
-          mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
-          mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
-          mEventData->mLeftSelectionPosition = handlePosition;
-        }
-      }
-      else
-      {
-        const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
-        if( differentHandles || endOfScroll )
-        {
-          mEventData->mUpdateHighlightBox = true;
-          mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
-          mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
-          mEventData->mRightSelectionPosition = handlePosition;
-        }
-      }
-
-      if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
-      {
-        RepositionSelectionHandles();
-
-        mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
-      }
-    }
-    mEventData->mDecoratorUpdated = true;
-  } // end ( HANDLE_SCROLLING == state )
+  ControllerImplEventHandler::OnHandleEvent(*this, event);
 }
 
 void Controller::Impl::OnSelectEvent( const Event& event )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text.
-    return;
-  }
-
-  if( mEventData->mSelectionEnabled )
-  {
-    // Convert from control's coords to text's coords.
-    const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
-    const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
-    // Calculates the logical position from the x,y coords.
-    RepositionSelectionHandles( xPosition,
-                                yPosition,
-                                Controller::NoTextTap::HIGHLIGHT );
-  }
+  ControllerImplEventHandler::OnSelectEvent(*this, event);
 }
 
 void Controller::Impl::OnSelectAllEvent()
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
-
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text.
-    return;
-  }
-
-  if( mEventData->mSelectionEnabled )
-  {
-    // Calculates the logical position from the start.
-    RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
-                                0.f - mModel->mScrollPosition.y,
-                                Controller::NoTextTap::HIGHLIGHT );
-
-    mEventData->mLeftSelectionPosition = 0u;
-    mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
-  }
+  ControllerImplEventHandler::OnSelectAllEvent(*this);
 }
 
 void Controller::Impl::OnSelectNoneEvent()
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
-
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text.
-    return;
-  }
-
-  if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING)
-  {
-    mEventData->mPrimaryCursorPosition = 0u;
-    mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
-    ChangeState( EventData::INACTIVE );
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mUpdateInputStyle = true;
-    mEventData->mScrollAfterUpdatePosition = true;
-  }
+  ControllerImplEventHandler::OnSelectNoneEvent(*this);
 }
 
 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
index 3fc33b3..fc30d7b 100755 (executable)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_H
 
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * 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.
@@ -47,6 +47,7 @@ const float DEFAULT_TEXTFIT_STEP = 1.f;
 //Forward declarations
 struct CursorInfo;
 struct FontDefaults;
+struct ControllerImplEventHandler;
 
 class SelectableControlInterface;
 
@@ -823,6 +824,9 @@ public:
   float mTextFitMaxSize;                   ///< Maximum Font Size for text fit. Default 100
   float mTextFitStepSize;                  ///< Step Size for font intervalse. Default 1
   bool  mTextFitEnabled : 1;               ///< Whether the text's fit is enabled.
+
+private:
+  friend ControllerImplEventHandler;
 };
 
 } // namespace Text