Fix key event propagation in text controller
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-event-handler.cpp
index 79a917e..59f0b8d 100644 (file)
@@ -26,6 +26,8 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
 #include <dali-toolkit/internal/text/text-controller-impl.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-text-updater.h>
 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
 
 namespace
@@ -66,7 +68,7 @@ void Controller::EventHandler::KeyboardFocusGainEvent(Controller& controller)
     if(controller.mImpl->IsShowingPlaceholderText())
     {
       // Show alternative placeholder-text when editing
-      controller.ShowPlaceholderText();
+      PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
     }
 
     controller.mImpl->RequestRelayout();
@@ -84,8 +86,17 @@ void Controller::EventHandler::KeyboardFocusLostEvent(Controller& controller)
       // Init selection position
       if(controller.mImpl->mEventData->mState == EventData::SELECTING)
       {
+        uint32_t oldStart, oldEnd;
+        oldStart = controller.mImpl->mEventData->mLeftSelectionPosition;
+        oldEnd   = controller.mImpl->mEventData->mRightSelectionPosition;
+
         controller.mImpl->mEventData->mLeftSelectionPosition  = controller.mImpl->mEventData->mPrimaryCursorPosition;
         controller.mImpl->mEventData->mRightSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
+
+        if(controller.mImpl->mSelectableControlInterface != nullptr)
+        {
+          controller.mImpl->mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, controller.mImpl->mEventData->mPrimaryCursorPosition, controller.mImpl->mEventData->mPrimaryCursorPosition);
+        }
       }
 
       controller.mImpl->ChangeState(EventData::INACTIVE);
@@ -93,7 +104,7 @@ void Controller::EventHandler::KeyboardFocusLostEvent(Controller& controller)
       if(!controller.mImpl->IsShowingRealText())
       {
         // Revert to regular placeholder-text when not editing
-        controller.ShowPlaceholderText();
+        PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
       }
     }
   }
@@ -106,6 +117,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
 
   bool textChanged    = false;
   bool relayoutNeeded = false;
+  bool isEditable     = controller.IsEditable() && controller.IsUserInteractionEnabled();
 
   if((NULL != controller.mImpl->mEventData) &&
      (keyEvent.GetState() == KeyEvent::DOWN))
@@ -137,7 +149,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
             (Dali::DALI_KEY_CURSOR_DOWN == keyCode))
     {
       // If don't have any text, do nothing.
-      if(!controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters)
+      if(!controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters || !isEditable)
       {
         return false;
       }
@@ -158,12 +170,22 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
         // Release the active highlight.
         if(controller.mImpl->mEventData->mState == EventData::SELECTING)
         {
+          uint32_t oldStart, oldEnd;
+          oldStart = controller.mImpl->mEventData->mLeftSelectionPosition;
+          oldEnd   = controller.mImpl->mEventData->mRightSelectionPosition;
+
           controller.mImpl->ChangeState(EventData::EDITING);
 
           // Update selection position.
           controller.mImpl->mEventData->mLeftSelectionPosition  = controller.mImpl->mEventData->mPrimaryCursorPosition;
           controller.mImpl->mEventData->mRightSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
           controller.mImpl->mEventData->mUpdateCursorPosition   = true;
+
+          if(controller.mImpl->mSelectableControlInterface != nullptr)
+          {
+            controller.mImpl->mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, controller.mImpl->mEventData->mLeftSelectionPosition, controller.mImpl->mEventData->mRightSelectionPosition);
+          }
+
           controller.mImpl->RequestRelayout();
         }
         return false;
@@ -186,7 +208,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
       // Do nothing
       return false;
     }
-    else if(keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier())
+    else if(keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier() && isEditable)
     {
       bool consumed = false;
       if(keyName == KEY_C_NAME || keyName == KEY_INSERT_NAME || logicalKey == KEY_C_NAME || logicalKey == KEY_INSERT_NAME)
@@ -218,7 +240,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
     else if((Dali::DALI_KEY_BACKSPACE == keyCode) ||
             (Dali::DevelKey::DALI_KEY_DELETE == keyCode))
     {
-      textChanged = controller.DeleteEvent(keyCode);
+      textChanged = DeleteEvent(controller, keyCode);
 
       // Will request for relayout.
       relayoutNeeded = true;
@@ -252,7 +274,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
     else
     {
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", &controller, keyString.c_str());
-      if(!controller.IsEditable()) return false;
+      if(!isEditable) return false;
 
       std::string refinedKey = keyString;
       if(controller.mImpl->mInputFilter != NULL && !refinedKey.empty())
@@ -283,7 +305,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
         // InputMethodContext is no longer handling key-events
         controller.mImpl->ClearPreEditFlag();
 
-        controller.InsertText(refinedKey, COMMIT);
+        TextUpdater::InsertText(controller, refinedKey, COMMIT);
 
         textChanged = true;
 
@@ -314,6 +336,15 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
       controller.mImpl->RequestRelayout();
     }
   }
+  else if((NULL != controller.mImpl->mEventData) && (keyEvent.GetState() == KeyEvent::UP))
+  {
+    // Handles specific keys that require event propagation.
+    if(Dali::DALI_KEY_BACK == keyEvent.GetKeyCode())
+    {
+      // Do nothing
+      return false;
+    }
+  }
 
   if(textChanged &&
      (NULL != controller.mImpl->mEditableControlInterface))
@@ -400,7 +431,7 @@ void Controller::EventHandler::TapEvent(Controller& controller, unsigned int tap
         if(controller.mImpl->IsShowingPlaceholderText() && !controller.mImpl->IsFocusedPlaceholderAvailable())
         {
           // Hide placeholder text
-          controller.ResetText();
+          TextUpdater::ResetText(controller);
         }
 
         if(EventData::INACTIVE == state)
@@ -579,30 +610,39 @@ void Controller::EventHandler::ProcessModifyEvents(Controller& controller)
       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
       DALI_ASSERT_DEBUG(it == events.Begin() && "Unexpected TEXT_REPLACED event");
 
-      controller.TextReplacedEvent();
+      TextReplacedEvent(controller);
     }
     else if(ModifyEvent::TEXT_INSERTED == event.type)
     {
-      controller.TextInsertedEvent();
+      TextInsertedEvent(controller);
     }
     else if(ModifyEvent::TEXT_DELETED == event.type)
     {
       // Placeholder-text cannot be deleted
       if(!controller.mImpl->IsShowingPlaceholderText())
       {
-        controller.TextDeletedEvent();
+        TextDeletedEvent(controller);
       }
     }
   }
 
   if(NULL != controller.mImpl->mEventData)
   {
+    uint32_t oldStart, oldEnd;
+    oldStart = controller.mImpl->mEventData->mLeftSelectionPosition;
+    oldEnd   = controller.mImpl->mEventData->mRightSelectionPosition;
+
     // When the text is being modified, delay cursor blinking
     controller.mImpl->mEventData->mDecorator->DelayCursorBlink();
 
     // Update selection position after modifying the text
     controller.mImpl->mEventData->mLeftSelectionPosition  = controller.mImpl->mEventData->mPrimaryCursorPosition;
     controller.mImpl->mEventData->mRightSelectionPosition = controller.mImpl->mEventData->mPrimaryCursorPosition;
+
+    if(controller.mImpl->mSelectableControlInterface != nullptr && controller.mImpl->mEventData->mState == EventData::SELECTING)
+    {
+      controller.mImpl->mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, controller.mImpl->mEventData->mLeftSelectionPosition, controller.mImpl->mEventData->mRightSelectionPosition);
+    }
   }
 
   // DISCARD temporary text
@@ -683,22 +723,18 @@ bool Controller::EventHandler::DeleteEvent(Controller& controller, int keyCode)
 
   if(EventData::SELECTING == controller.mImpl->mEventData->mState)
   {
-    removed = controller.RemoveSelectedText();
+    removed = TextUpdater::RemoveSelectedText(controller);
   }
   else if((controller.mImpl->mEventData->mPrimaryCursorPosition > 0) && (keyCode == Dali::DALI_KEY_BACKSPACE))
   {
     // Remove the character before the current cursor position
-    removed = controller.RemoveText(-1,
-                                    1,
-                                    UPDATE_INPUT_STYLE);
+    removed = TextUpdater::RemoveText(controller, -1, 1, UPDATE_INPUT_STYLE);
   }
   else if((controller.mImpl->mEventData->mPrimaryCursorPosition < controller.mImpl->mModel->mLogicalModel->mText.Count()) &&
           (keyCode == Dali::DevelKey::DALI_KEY_DELETE))
   {
     // Remove the character after the current cursor position
-    removed = controller.RemoveText(0,
-                                    1,
-                                    UPDATE_INPUT_STYLE);
+    removed = TextUpdater::RemoveText(controller, 0, 1, UPDATE_INPUT_STYLE);
   }
 
   if(removed)
@@ -710,7 +746,7 @@ bool Controller::EventHandler::DeleteEvent(Controller& controller, int keyCode)
     }
     else
     {
-      controller.ShowPlaceholderText();
+      PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
     }
     controller.mImpl->mEventData->mUpdateCursorPosition = true;
     controller.mImpl->mEventData->mScrollAfterDelete    = true;
@@ -732,23 +768,24 @@ InputMethodContext::CallbackData Controller::EventHandler::OnInputMethodContextE
   {
     case InputMethodContext::COMMIT:
     {
-      controller.InsertText(inputMethodContextEvent.predictiveString, Text::Controller::COMMIT);
+      TextUpdater::InsertText(controller, inputMethodContextEvent.predictiveString, Text::Controller::COMMIT);
       requestRelayout = true;
       retrieveCursor  = true;
       break;
     }
     case InputMethodContext::PRE_EDIT:
     {
-      controller.InsertText(inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT);
+      TextUpdater::InsertText(controller, inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT);
       requestRelayout = true;
       retrieveCursor  = true;
       break;
     }
     case InputMethodContext::DELETE_SURROUNDING:
     {
-      const bool textDeleted = controller.RemoveText(inputMethodContextEvent.cursorOffset,
-                                                     inputMethodContextEvent.numberOfChars,
-                                                     DONT_UPDATE_INPUT_STYLE);
+      const bool textDeleted = TextUpdater::RemoveText(controller,
+                                                       inputMethodContextEvent.cursorOffset,
+                                                       inputMethodContextEvent.numberOfChars,
+                                                       DONT_UPDATE_INPUT_STYLE);
 
       if(textDeleted)
       {
@@ -759,7 +796,7 @@ InputMethodContext::CallbackData Controller::EventHandler::OnInputMethodContextE
         }
         else
         {
-          controller.ShowPlaceholderText();
+          PlaceholderHandler::ShowPlaceholderText(*controller.mImpl);
         }
         controller.mImpl->mEventData->mUpdateCursorPosition = true;
         controller.mImpl->mEventData->mScrollAfterDelete    = true;
@@ -781,6 +818,21 @@ InputMethodContext::CallbackData Controller::EventHandler::OnInputMethodContextE
       retrieveCursor = true;
       break;
     }
+    case InputMethodContext::SELECTION_SET:
+    {
+      uint32_t start = static_cast<uint32_t>(inputMethodContextEvent.startIndex);
+      uint32_t end = static_cast<uint32_t>(inputMethodContextEvent.endIndex);
+      if(start == end)
+      {
+        controller.SetPrimaryCursorPosition(start, true);
+      }
+      else
+      {
+        controller.SelectText(start, end);
+      }
+
+      break;
+    }
     case InputMethodContext::VOID:
     {
       // do nothing
@@ -854,7 +906,7 @@ void Controller::EventHandler::PasteClipboardItemEvent(Controller& controller)
   controller.mImpl->SetClipboardHideEnable(false);
 
   // Paste
-  controller.PasteText(stringToPaste);
+  TextUpdater::PasteText(controller, stringToPaste);
 
   controller.mImpl->SetClipboardHideEnable(true);
 }
@@ -924,43 +976,17 @@ void Controller::EventHandler::TextPopupButtonTouched(Controller& controller, Da
   {
     case Toolkit::TextSelectionPopup::CUT:
     {
-      if(!controller.IsEditable()) return;
-      controller.mImpl->SendSelectionToClipboard(true); // Synchronous call to modify text
-      controller.mImpl->mOperationsPending = ALL_OPERATIONS;
-
-      if((0u != controller.mImpl->mModel->mLogicalModel->mText.Count()) ||
-         !controller.mImpl->IsPlaceholderAvailable())
-      {
-        controller.mImpl->QueueModifyEvent(ModifyEvent::TEXT_DELETED);
-      }
-      else
-      {
-        controller.ShowPlaceholderText();
-      }
-
-      controller.mImpl->mEventData->mUpdateCursorPosition = true;
-      controller.mImpl->mEventData->mScrollAfterDelete    = true;
-
-      controller.mImpl->RequestRelayout();
-
-      if(NULL != controller.mImpl->mEditableControlInterface)
-      {
-        controller.mImpl->mEditableControlInterface->TextChanged(true);
-      }
+      controller.CutText();
       break;
     }
     case Toolkit::TextSelectionPopup::COPY:
     {
-      controller.mImpl->SendSelectionToClipboard(false); // Text not modified
-
-      controller.mImpl->mEventData->mUpdateCursorPosition = true;
-
-      controller.mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
+      controller.CopyText();
       break;
     }
     case Toolkit::TextSelectionPopup::PASTE:
     {
-      controller.mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
+      controller.PasteText();
       break;
     }
     case Toolkit::TextSelectionPopup::SELECT:
@@ -970,14 +996,14 @@ void Controller::EventHandler::TextPopupButtonTouched(Controller& controller, Da
       if(controller.mImpl->mEventData->mSelectionEnabled)
       {
         // Creates a SELECT event.
-        controller.SelectEvent(currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE);
+        SelectEvent(controller, currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE);
       }
       break;
     }
     case Toolkit::TextSelectionPopup::SELECT_ALL:
     {
       // Creates a SELECT_ALL event
-      controller.SelectEvent(0.f, 0.f, SelectionType::ALL);
+      SelectEvent(controller, 0.f, 0.f, SelectionType::ALL);
       break;
     }
     case Toolkit::TextSelectionPopup::CLIPBOARD: