Add TextSelect() 09/261509/6
authorBowon Ryu <bowon.ryu@samsung.com>
Tue, 20 Jul 2021 08:39:16 +0000 (17:39 +0900)
committerBowon Ryu <bowon.ryu@samsung.com>
Thu, 5 Aug 2021 06:56:02 +0000 (15:56 +0900)
This api can replace the SELECTED_TEXT_START and SELECTED_TEXT_END properties.
The feature to select a range of text should be provided as a method like SelectWholeText(), SelectNone().

Currently, the SELECTED_TEXT_START and SELECTED_TEXT_END properties are causing several problems.
1. User can not set start and end at the same time.
2. Since it is not an event method, it does not work if it is called before text is rendered.

This patch adds SelectText().
And I will make a separate patch to remove the property.

Change-Id: I5a050cd88b9597b939766e1a6ec0e39edf96e2d4
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
18 files changed:
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/controls/text-controls/text-field-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-field-devel.h
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.h
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/internal/text/text-controller-event-handler.cpp
dali-toolkit/internal/text/text-controller-event-handler.h
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl-event-handler.h
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-selectable-control-interface.h

index 66d16ca..9b1d0ff 100644 (file)
@@ -3242,6 +3242,65 @@ int UtcDaliTextEditorSelectWholeText(void)
   END_TEST;
 }
 
+int UtcDaliTextEditorSelectText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorSelectText ");
+
+  TextEditor textEditor = TextEditor::New();
+
+  application.GetScene().Add( textEditor );
+
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 300.f, 50.f ) );
+  textEditor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  textEditor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  application.SendNotification();
+  application.Render();
+
+  DevelTextEditor::SelectText( textEditor ,0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  // Nothing is selected
+  std::string selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "", selectedText, TEST_LOCATION );
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Hello world" );
+
+  application.SendNotification();
+  application.Render();
+
+  DevelTextEditor::SelectText( textEditor, 0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "Hello", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_START ).Get<int>(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_END ).Get<int>(), 5, TEST_LOCATION );
+
+  // world is selected
+  DevelTextEditor::SelectText( textEditor, 6, 11 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "world", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_START ).Get<int>(), 6, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_END ).Get<int>(), 11, TEST_LOCATION );
+
+  END_TEST;
+}
+
 int UtcDaliTextEditorSelectNone(void)
 {
   ToolkitTestApplication application;
index eec0857..d89428a 100644 (file)
@@ -3482,6 +3482,66 @@ int UtcDaliTextFieldSelectWholeText(void)
   END_TEST;
 }
 
+int UtcDaliTextFieldSelectText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextFieldSelectText ");
+
+  TextField textField = TextField::New();
+
+  application.GetScene().Add( textField );
+
+  textField.SetProperty( Actor::Property::SIZE, Vector2( 300.f, 50.f ) );
+  textField.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  textField.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  application.SendNotification();
+  application.Render();
+
+  DevelTextField::SelectText( textField, 0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  // Nothing is selected
+  std::string selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "", selectedText, TEST_LOCATION );
+
+  textField.SetProperty( TextField::Property::TEXT, "Hello world" );
+
+  application.SendNotification();
+  application.Render();
+
+  // Hello is selected
+  DevelTextField::SelectText( textField, 0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "Hello", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_START ).Get<int>(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_END ).Get<int>(), 5, TEST_LOCATION );
+
+  // world is selected
+  DevelTextField::SelectText( textField, 6, 11 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "world", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_START ).Get<int>(), 6, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_END ).Get<int>(), 11, TEST_LOCATION );
+
+  END_TEST;
+}
+
 int UtcDaliTextFieldSelectNone(void)
 {
   ToolkitTestApplication application;
index a685aee..2e263ce 100644 (file)
@@ -55,6 +55,11 @@ void SelectNone(TextEditor textEditor)
   GetImpl(textEditor).SelectNone();
 }
 
+void SelectText(TextEditor textEditor, const uint32_t start, const uint32_t end)
+{
+  GetImpl(textEditor).SelectText(start, end);
+}
+
 void ScrollBy(TextEditor textEditor, Vector2 scroll)
 {
   GetImpl(textEditor).ScrollBy(scroll);
index 695ca1b..2d14542 100644 (file)
@@ -363,6 +363,21 @@ DALI_TOOLKIT_API void SelectWholeText(TextEditor textEditor);
 DALI_TOOLKIT_API void SelectNone(TextEditor textEditor);
 
 /**
+ * @brief Select the text from start index to end index of TextEditor.
+ * @note
+ * The selection index is based on the cursor index.
+ *
+ * text   H e l l o
+ * index 0 1 2 3 4 5
+ * if textEditor.SelectText(1, 4); is executed, "ell" is selected.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] start The start index of the text to select. (The starting point of start index is 0.)
+ * @param[in] end The end index of the text to select. (If end index > text's length, the end index is set to the length of the text.)
+ */
+DALI_TOOLKIT_API void SelectText(TextEditor textEditor, const uint32_t start, const uint32_t end);
+
+/**
  * @brief Scroll the TextEditor by specific amount.
  *
  * @param[in] textEditor The instance of TextEditor.
index 727b42a..a5d0428 100644 (file)
@@ -50,6 +50,11 @@ void SelectNone(TextField textField)
   GetImpl(textField).SelectNone();
 }
 
+void SelectText(TextField textField, const uint32_t start, const uint32_t end)
+{
+  GetImpl(textField).SelectText(start, end);
+}
+
 } // namespace DevelTextField
 
 } // namespace Toolkit
index 377b606..bd9c39a 100644 (file)
@@ -291,6 +291,21 @@ DALI_TOOLKIT_API void SelectWholeText(TextField textField);
  */
 DALI_TOOLKIT_API void SelectNone(TextField textField);
 
+/**
+ * @brief Select the text from start index to end index of TextField.
+ * @note
+ * The selection index is based on the cursor index.
+ *
+ * text   H e l l o
+ * index 0 1 2 3 4 5
+ * if textField.SelectText(1, 4); is executed, "ell" is selected.
+ *
+ * @param[in] textField The instance of TextField.
+ * @param[in] start The start index of the text to select. (The starting point of start index is 0.)
+ * @param[in] end The end index of the text to select. (If end index > text's length, the end index is set to the length of the text.)
+ */
+DALI_TOOLKIT_API void SelectText(TextField textField, const uint32_t start, const uint32_t end);
+
 } // namespace DevelTextField
 
 } // namespace Toolkit
index 75b90f3..dd67a85 100644 (file)
@@ -1245,6 +1245,15 @@ void TextEditor::SelectNone()
   }
 }
 
+void TextEditor::SelectText(const uint32_t start, const uint32_t end)
+{
+  if(mController && mController->IsShowingRealText())
+  {
+    mController->SelectText(start, end);
+    SetKeyInputFocus();
+  }
+}
+
 void TextEditor::ScrollBy(Vector2 scroll)
 {
   if(mController && mController->IsShowingRealText())
index 6b6c377..7ba1190 100644 (file)
@@ -273,6 +273,11 @@ public:
   void SelectNone() override;
 
   /**
+   * @copydoc Text::SelectableControlInterface::SelectText()
+   */
+  void SelectText(const uint32_t start, const uint32_t end) override;
+
+  /**
    * @copydoc Dali::Toolkit::DevelTextEditor::ScrollBy()
    */
   void ScrollBy(Vector2 Scroll);
index 07afb0e..d2bfc5c 100644 (file)
@@ -1176,6 +1176,15 @@ void TextField::SelectNone()
   }
 }
 
+void TextField::SelectText(const uint32_t start, const uint32_t end)
+{
+  if(mController && mController->IsShowingRealText())
+  {
+    mController->SelectText(start, end);
+    SetKeyInputFocus();
+  }
+}
+
 string TextField::GetSelectedText() const
 {
   string selectedText = "";
index 85d50ef..b1f5991 100644 (file)
@@ -264,6 +264,11 @@ public:
   void SelectNone() override;
 
   /**
+   * @copydoc Text::SelectableControlInterface::SelectText()
+   */
+  void SelectText(const uint32_t start, const uint32_t end) override;
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const override;
index a84747b..79a917e 100644 (file)
@@ -536,6 +536,27 @@ void Controller::EventHandler::SelectEvent(Controller& controller, float x, floa
   }
 }
 
+void Controller::EventHandler::SelectEvent(Controller& controller, const uint32_t start, const uint32_t end, SelectionType selectType)
+{
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::SelectEvent\n");
+
+  if(NULL != controller.mImpl->mEventData)
+  {
+    if(selectType == SelectionType::RANGE)
+    {
+      Event event(Event::SELECT_RANGE);
+      event.p2.mUint = start;
+      event.p3.mUint = end;
+      controller.mImpl->mEventData->mEventQueue.push_back(event);
+    }
+
+    controller.mImpl->mEventData->mCheckScrollAmount     = true;
+    controller.mImpl->mEventData->mIsLeftHandleSelected  = true;
+    controller.mImpl->mEventData->mIsRightHandleSelected = true;
+    controller.mImpl->RequestRelayout();
+  }
+}
+
 void Controller::EventHandler::ProcessModifyEvents(Controller& controller)
 {
   Vector<ModifyEvent>& events = controller.mImpl->mModifyEvents;
index 092ac58..6e6e068 100644 (file)
@@ -45,6 +45,7 @@ struct Controller::EventHandler
   static void PanEvent(Controller& controller, GestureState state, const Vector2& displacement);
   static void LongPressEvent(Controller& controller, GestureState state, float x, float y);
   static void SelectEvent(Controller& controller, float x, float y, SelectionType selectType);
+  static void SelectEvent(Controller& controller, const uint32_t start, const uint32_t end, SelectionType selectType);
   static void ProcessModifyEvents(Controller& controller);
   static void TextReplacedEvent(Controller& controller);
   static void TextInsertedEvent(Controller& controller);
index c9a197f..2aafff7 100644 (file)
@@ -104,6 +104,11 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
           OnSelectNoneEvent(impl);
           break;
         }
+        case Event::SELECT_RANGE:
+        {
+          OnSelectRangeEvent(impl, *iter);
+          break;
+        }
       }
     }
   }
@@ -693,6 +698,29 @@ void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
   }
 }
 
+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)
+    {
+      // 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;
+    }
+  }
+}
+
 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
 {
   ModelPtr&      model          = impl.mModel;
index e1e9dbb..82e5c78 100644 (file)
@@ -104,6 +104,14 @@ struct ControllerImplEventHandler
    */
   static void OnSelectNoneEvent(Controller::Impl& controllerImpl);
 
+  /**
+   * @brief Called by Controller::Impl when a select range event is received.
+   *
+   * @param controllerImpl A reference to Controller::Impl
+   * @param event The event
+   */
+  static void OnSelectRangeEvent(Controller::Impl& controllerImpl, const Event& event);
+
 private:
   /**
    * @brief Called by OnHandleEvent when we are in the Pressed state.
index d3ed77a..84d6193 100644 (file)
@@ -66,6 +66,7 @@ struct Event
     SELECT,
     SELECT_ALL,
     SELECT_NONE,
+    SELECT_RANGE,
   };
 
   union Param
index 61071b1..dce67ff 100644 (file)
@@ -1822,6 +1822,11 @@ void Controller::SelectEvent(float x, float y, SelectionType selectType)
   EventHandler::SelectEvent(*this, x, y, selectType);
 }
 
+void Controller::SelectEvent(const uint32_t start, const uint32_t end, SelectionType selectType)
+{
+  EventHandler::SelectEvent(*this, start, end, selectType);
+}
+
 void Controller::SetTextSelectionRange(const uint32_t* start, const uint32_t* end)
 {
   if(mImpl->mEventData)
@@ -1872,6 +1877,11 @@ void Controller::SelectNone()
   SelectEvent(0.f, 0.f, SelectionType::NONE);
 }
 
+void Controller::SelectText(const uint32_t start, const uint32_t end)
+{
+  SelectEvent(start, end, SelectionType::RANGE);
+}
+
 string Controller::GetSelectedText() const
 {
   string text;
index 4824813..8c388b9 100644 (file)
@@ -52,9 +52,10 @@ class RenderingController;
    */
 enum SelectionType
 {
-  INTERACTIVE = 0x0000,
-  ALL         = 0x0001,
-  NONE        = 0x0002
+  INTERACTIVE = 0x0000, ///< Select the word where the cursor is located.
+  ALL         = 0x0001, ///< Select the whole text.
+  NONE        = 0x0002, ///< Unselect the whole text.
+  RANGE       = 0x0003  ///< Select the range text.
 };
 
 typedef IntrusivePtr<Controller> ControllerPtr;
@@ -1605,6 +1606,18 @@ public: // Text-input Event Queuing.
   void SelectEvent(float x, float y, SelectionType selection);
 
   /**
+   * @brief Creates a selection event with a selection index.
+   *
+   * It could be called from the SelectText().
+   * The start and end parameters are passed through the event.
+   *
+   * @param[in] start The start selection position.
+   * @param[in] end The end selection position.
+   * @param[in] selection type like the range.
+   */
+  void SelectEvent(const uint32_t start, const uint32_t end, SelectionType selection);
+
+  /**
    * @copydoc Text::SelectableControlInterface::SetTextSelectionRange()
    */
   void SetTextSelectionRange(const uint32_t* start, const uint32_t* end);
@@ -1625,6 +1638,11 @@ public: // Text-input Event Queuing.
   void SelectNone();
 
   /**
+   * @copydoc Text::SelectableControlInterface::SelectText()
+   */
+  void SelectText(const uint32_t start, const uint32_t end);
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const;
index da19108..3d5feef 100644 (file)
@@ -61,6 +61,13 @@ public:
   virtual void SelectNone() = 0;
 
   /**
+   * @brief Called to set the selection postions in the texts.
+   * @param start start selection position.
+   * @param end end selection position.
+   */
+  virtual void SelectText(const uint32_t start, const uint32_t end) = 0;
+
+  /**
    * @brief Retrive Selected text.
    * @return The seletced text.
    */