Merge "fix issue in negative line spacing with key arrow down" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl-event-handler.cpp
index 22525db..1ed8c7f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -54,6 +54,8 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
     return false;
   }
 
+  unsigned int oldPos = eventData->mPrimaryCursorPosition;
+
   if(eventData->mDecorator)
   {
     for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
@@ -104,6 +106,11 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
           OnSelectNoneEvent(impl);
           break;
         }
+        case Event::SELECT_RANGE:
+        {
+          OnSelectRangeEvent(impl, *iter);
+          break;
+        }
       }
     }
   }
@@ -119,12 +126,14 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
   {
     // 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)
+    //only emit the event if the cursor is moved in current function.
+    if(nullptr != impl.mEditableControlInterface && eventData->mEventQueue.size() > 0)
     {
-      impl.mEditableControlInterface->CaretMoved(eventData->mPrimaryCursorPosition);
+      impl.mEditableControlInterface->CursorPositionChanged(oldPos, eventData->mPrimaryCursorPosition);
     }
 
     if(eventData->mUpdateCursorHookPosition)
@@ -155,7 +164,10 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
     eventData->mUpdateCursorPosition     = false;
     eventData->mUpdateGrabHandlePosition = false;
   }
-  else
+
+  if(eventData->mUpdateHighlightBox ||
+     eventData->mUpdateLeftSelectionPosition ||
+     eventData->mUpdateRightSelectionPosition)
   {
     CursorInfo leftHandleInfo;
     CursorInfo rightHandleInfo;
@@ -274,6 +286,8 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
   ModelPtr&        model           = impl.mModel;
   LogicalModelPtr& logicalModel    = model->mLogicalModel;
   VisualModelPtr&  visualModel     = model->mVisualModel;
+  uint32_t         oldSelStart     = eventData.mLeftSelectionPosition;
+  uint32_t         oldSelEnd       = eventData.mRightSelectionPosition;
 
   CharacterIndex& primaryCursorPosition         = eventData.mPrimaryCursorPosition;
   CharacterIndex  previousPrimaryCursorPosition = primaryCursorPosition;
@@ -320,6 +334,8 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
 
     const LineIndex lineIndex         = visualModel->GetLineOfCharacter(characterIndex);
     const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
+    const LineIndex lastLineIndex     = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
+    const bool      isLastLine        = (previousLineIndex == lastLineIndex);
 
     // Retrieve the cursor position info.
     CursorInfo cursorInfo;
@@ -330,7 +346,7 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
     const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
 
     // Get the next hit 'y' point.
-    const float hitPointY = cursorInfo.lineOffset - 0.5f * (line.ascender - line.descender);
+    const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line, isLastLine);
 
     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
     bool matchedCharacter = false;
@@ -363,10 +379,15 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
 
       // Get the line below.
-      const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
+      const LineRun& nextline = *(visualModel->mLines.Begin() + lineIndex + 1u);
+      const LineRun& currline = *(visualModel->mLines.Begin() + lineIndex);
+
+      // Get last line index
+      const LineIndex lastLineIndex = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
+      const bool      isLastLine    = (lineIndex + 1u == lastLineIndex);
 
       // Get the next hit 'y' point.
-      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * (line.ascender - line.descender);
+      const float hitPointY = cursorInfo.lineOffset + GetLineHeight(currline, false) + 0.5f * GetLineHeight(nextline, isLastLine);
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
       bool matchedCharacter = false;
@@ -385,6 +406,11 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
     // Update selection position after moving the cursor
     eventData.mLeftSelectionPosition  = primaryCursorPosition;
     eventData.mRightSelectionPosition = primaryCursorPosition;
+
+    if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
+    {
+      impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+    }
   }
 
   if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
@@ -399,8 +425,23 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
       {
         eventData.mRightSelectionPosition += cursorPositionDelta;
+        eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
+
+        if(impl.mSelectableControlInterface != nullptr)
+        {
+          impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+        }
+      }
+
+      if(impl.mSelectableControlInterface != nullptr && eventData.mLeftSelectionPosition == eventData.mRightSelectionPosition)
+      {
+        // If left selection position and right selection position are the same, the selection is canceled.
+        selecting = false;
+      }
+      else
+      {
+        selecting = true;
       }
-      selecting = true;
     }
     else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
     {
@@ -430,6 +471,12 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
         eventData.mDecorator->SetPopupActive(false);
       }
     }
+    else
+    {
+      // If no selection, set a normal cursor.
+      impl.ChangeState(EventData::EDITING);
+      eventData.mUpdateCursorPosition = true;
+    }
   }
   else
   {
@@ -457,8 +504,10 @@ void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event&
       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;
+        const float xPosition   = event.p2.mFloat - model->mScrollPosition.x;
+        const float yPosition   = event.p3.mFloat - model->mScrollPosition.y;
+        uint32_t    oldSelStart = eventData.mLeftSelectionPosition;
+        uint32_t    oldSelEnd   = eventData.mRightSelectionPosition;
 
         // Keep the tap 'x' position. Used to move the cursor.
         eventData.mCursorHookPositionX = xPosition;
@@ -473,6 +522,11 @@ void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event&
                                                                        CharacterHitTest::TAP,
                                                                        matchedCharacter);
 
+        if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
+        {
+          impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition);
+        }
+
         // When the cursor position is changing, delay cursor blinking
         eventData.mDecorator->DelayCursorBlink();
       }
@@ -655,7 +709,7 @@ void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
   if(impl.mEventData)
   {
     EventData& eventData = *impl.mEventData;
-    if(eventData.mSelectionEnabled)
+    if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
     {
       ModelPtr&      model          = impl.mModel;
       const Vector2& scrollPosition = model->mScrollPosition;
@@ -665,8 +719,17 @@ void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
                                       0.f - scrollPosition.y,
                                       Controller::NoTextTap::HIGHLIGHT);
 
+      uint32_t oldStart = eventData.mLeftSelectionPosition;
+      uint32_t oldEnd   = eventData.mRightSelectionPosition;
+
       eventData.mLeftSelectionPosition  = 0u;
       eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
+      eventData.mPrimaryCursorPosition  = eventData.mRightSelectionPosition;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
     }
   }
 }
@@ -680,12 +743,51 @@ void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
     EventData& eventData = *impl.mEventData;
     if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
     {
-      eventData.mPrimaryCursorPosition = 0u;
+      uint32_t oldStart = eventData.mLeftSelectionPosition;
+      uint32_t oldEnd   = eventData.mRightSelectionPosition;
+
       eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
-      impl.ChangeState(EventData::INACTIVE);
+      impl.ChangeState(EventData::EDITING);
       eventData.mUpdateCursorPosition      = true;
       eventData.mUpdateInputStyle          = true;
       eventData.mScrollAfterUpdatePosition = true;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
+    }
+  }
+}
+
+void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
+{
+  if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
+  {
+    ModelPtr&      model          = impl.mModel;
+    const Vector2& scrollPosition = model->mScrollPosition;
+
+    // Calculate the selection index.
+    const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
+    const uint32_t start  = std::min(event.p2.mUint, length);
+    const uint32_t end    = std::min(event.p3.mUint, length);
+
+    if(start != end)
+    {
+      uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
+      uint32_t oldEnd   = impl.mEventData->mRightSelectionPosition;
+
+      // Calculates the logical position from the x,y coords.
+      impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
+
+      impl.mEventData->mLeftSelectionPosition  = start;
+      impl.mEventData->mRightSelectionPosition = end;
+      impl.mEventData->mPrimaryCursorPosition  = end;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
+      }
     }
   }
 }
@@ -710,6 +812,8 @@ void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const E
                                                                        matchedCharacter);
 
   EventData& eventData = *impl.mEventData;
+  uint32_t   oldStart  = eventData.mLeftSelectionPosition;
+  uint32_t   oldEnd    = eventData.mRightSelectionPosition;
 
   if(Event::GRAB_HANDLE_EVENT == event.type)
   {
@@ -769,6 +873,11 @@ void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const E
     eventData.mIsLeftHandleSelected  = false;
     eventData.mIsRightHandleSelected = true;
   }
+
+  if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
+  {
+    impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+  }
 }
 
 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
@@ -794,6 +903,8 @@ void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const
   }
 
   EventData& eventData = *impl.mEventData;
+  uint32_t   oldStart  = eventData.mLeftSelectionPosition;
+  uint32_t   oldEnd    = eventData.mRightSelectionPosition;
 
   if(Event::GRAB_HANDLE_EVENT == event.type)
   {
@@ -850,6 +961,11 @@ void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const
     }
   }
 
+  if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
+  {
+    impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+  }
+
   eventData.mDecoratorUpdated = true;
 }
 
@@ -964,6 +1080,8 @@ void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const
                                                                       position.y - scrollPosition.y,
                                                                       CharacterHitTest::SCROLL,
                                                                       matchedCharacter);
+    uint32_t             oldStart         = eventData.mLeftSelectionPosition;
+    uint32_t             oldEnd           = eventData.mRightSelectionPosition;
 
     if(leftSelectionHandleEvent)
     {
@@ -994,6 +1112,11 @@ void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const
       impl.RepositionSelectionHandles();
 
       eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
     }
   }
   eventData.mDecoratorUpdated = true;