Merge "Remove unused custom shader prefix method" into devel/master
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Tue, 24 Aug 2021 18:01:28 +0000 (18:01 +0000)
committerGerrit Code Review <gerrit@review>
Tue, 24 Aug 2021 18:01:28 +0000 (18:01 +0000)
19 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-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-text-updater.cpp
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-editable-control-interface.h
dali-toolkit/internal/text/text-selectable-control-interface.h
dali-toolkit/internal/text/text-selection-handle-controller.cpp

index d9a70d2..0d0b8bc 100644 (file)
@@ -133,6 +133,9 @@ const char* HANDLE_RIGHT_SELECTION_FILE_NAME = TEST_RESOURCE_DIR "/selection_han
 
 const std::string DEFAULT_DEVICE_NAME("hwKeyboard");
 
+static bool gSelectionChangedCallbackCalled;
+static uint32_t oldSelectionStart;
+static uint32_t oldSelectionEnd;
 static bool gAnchorClickedCallBackCalled;
 static bool gAnchorClickedCallBackNotCalled;
 static bool gTextChangedCallBackCalled;
@@ -158,6 +161,15 @@ struct CallbackFunctor
   bool* mCallbackFlag;
 };
 
+static void TestSelectionChangedCallback(TextEditor control, uint32_t oldStart, uint32_t oldEnd)
+{
+  tet_infoline(" TestSelectionChangedCallback");
+
+  gSelectionChangedCallbackCalled = true;
+  oldSelectionStart = oldStart;
+  oldSelectionEnd   = oldEnd;
+}
+
 static void TestAnchorClickedCallback(TextEditor control, const char* href, unsigned int hrefLength)
 {
   tet_infoline(" TestAnchorClickedCallback");
@@ -3992,6 +4004,297 @@ int UtcDaliToolkitTextEditorEllipsisPositionProperty(void)
   END_TEST;
 }
 
+int UtcDaliTextEditorCopyText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorCopyText ");
+
+  TextEditor textEditor = TextEditor::New();
+
+  std::string selectedText = "";
+  std::string copiedText = "";
+
+  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();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Hello world" );
+
+  application.SendNotification();
+  application.Render();
+
+  // Hello is selected
+  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 );
+
+  // Hello is copied
+  copiedText = DevelTextEditor::CopyText( textEditor );
+  DALI_TEST_EQUALS( "Hello", copiedText, 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 );
+
+  // world is copied
+  copiedText = DevelTextEditor::CopyText( textEditor );
+  DALI_TEST_EQUALS( "world", copiedText, TEST_LOCATION );
+
+  // "lo wo" is selected
+  DevelTextEditor::SelectText( textEditor, 3, 8 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "lo wo", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_START ).Get<int>(), 3, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_END ).Get<int>(), 8, TEST_LOCATION );
+
+  // "lo wo" is copied
+  copiedText = DevelTextEditor::CopyText( textEditor );
+  DALI_TEST_EQUALS( "lo wo", copiedText, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliTextEditorCutText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorCutText ");
+
+  TextEditor textEditor = TextEditor::New();
+
+  std::string selectedText = "";
+
+  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();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Hello world" );
+
+  application.SendNotification();
+  application.Render();
+
+  // Hello is selected
+  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 );
+
+  // Hello is cut
+  DALI_TEST_EQUALS( "Hello", DevelTextEditor::CutText( textEditor ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), " world", TEST_LOCATION );
+
+  // " w" is selected
+  DevelTextEditor::SelectText( textEditor, 0, 2 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( " w", 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>(), 2, TEST_LOCATION );
+
+  // " w" is cut
+  DALI_TEST_EQUALS( " w", DevelTextEditor::CutText( textEditor ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "orld", TEST_LOCATION );
+
+  // Test Cut from the middle
+
+  // "rl" is selected
+  DevelTextEditor::SelectText( textEditor, 1, 3 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "rl", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_START ).Get<int>(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_END ).Get<int>(), 3, TEST_LOCATION );
+
+  // "rl" is cut
+  DALI_TEST_EQUALS( "rl", DevelTextEditor::CutText( textEditor ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "od", TEST_LOCATION );
+
+  // Test Cut from the end
+
+  // "d" is selected
+  DevelTextEditor::SelectText( textEditor, 1, 2 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "d", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_START ).Get<int>(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::SELECTED_TEXT_END ).Get<int>(), 2, TEST_LOCATION );
+
+  // "d" is cut
+  DALI_TEST_EQUALS( "d", DevelTextEditor::CutText( textEditor ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( TextEditor::Property::TEXT ).Get<std::string>(), "o", TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliTextEditorPasteText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorPasteText ");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  std::string cutText = "";
+  std::string copiedText = "";
+
+  editor.SetProperty( TextEditor::Property::TEXT, "Hello\nworld\nHello world" );
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 10.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text editor
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Move to second line of the text.
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Select some text in the right of the current cursor position
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_SHIFT_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Cut the selected text
+  cutText = DevelTextEditor::CutText(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( "wor", cutText, TEST_LOCATION );
+  DALI_TEST_EQUALS( "Hello\nld\nHello world", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
+
+  // Select some text in the left of the current cursor position
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_SHIFT_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_LEFT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_LEFT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN,  "",DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_LEFT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Copy the selected text
+  copiedText = DevelTextEditor::CopyText(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( "lo\n", copiedText, TEST_LOCATION );
+  DALI_TEST_EQUALS( "Hello\nld\nHello world", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
+
+
+  // Move the cursor to the third line
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Paste the selected text at the current cursor position
+  DevelTextEditor::PasteText(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( "Hello\nld\nHello lo\nworld", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
+
+  END_TEST;
+}
 int UtcDaliTextEditorLineSpacing(void)
 {
   ToolkitTestApplication application;
@@ -4142,4 +4445,137 @@ int utcDaliTextEditorCursorPositionChangedSignal(void)
   DALI_TEST_EQUALS(oldCursorPos, 5, TEST_LOCATION);
 
   END_TEST;
+}
+
+int utcDaliTextEditorSelectionChangedSignal(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorSelectionChangedSignal");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // connect to the selection changed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextEditor::SelectionChangedSignal(editor).Connect(&TestSelectionChangedCallback);
+  bool selectionChangedSignal = false;
+  editor.ConnectSignal( testTracker, "selectionChanged",   CallbackFunctor(&selectionChangedSignal) );
+
+  editor.SetProperty( TextEditor::Property::TEXT, "Hello\nworld\nHello world" );
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 10.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  editor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  editor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text editor
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Move to second line of the text.
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Select some text in the right of the current cursor position
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_SHIFT_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 6, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 7, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_ESCAPE, 0, 0, Integration::KeyEvent::UP, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 6, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 8, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+  editor.SetKeyInputFocus();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DevelTextEditor::SelectText( editor ,0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  editor.SetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 3);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 5, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  // select all text
+  DevelTextEditor::SelectWholeText(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  // select none
+  DevelTextEditor::SelectNone(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 23, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index d824c04..7e9c6eb 100644 (file)
@@ -120,9 +120,13 @@ const std::string DEFAULT_FONT_DIR( "/resources/fonts" );
 const int KEY_RETURN_CODE = 36;
 const int KEY_A_CODE = 38;
 const int KEY_D_CODE = 40;
+const int KEY_SHIFT_MODIFIER = 257;
 
 const std::string DEFAULT_DEVICE_NAME("hwKeyboard");
 
+static bool gSelectionChangedCallbackCalled;
+static uint32_t oldSelectionStart;
+static uint32_t oldSelectionEnd;
 static bool gAnchorClickedCallBackCalled;
 static bool gAnchorClickedCallBackNotCalled;
 static bool gTextChangedCallBackCalled;
@@ -214,6 +218,15 @@ struct CallbackFunctor
   bool* mCallbackFlag;
 };
 
+static void TestSelectionChangedCallback(TextField control, uint32_t oldStart, uint32_t oldEnd)
+{
+  tet_infoline(" TestSelectionChangedCallback");
+
+  gSelectionChangedCallbackCalled = true;
+  oldSelectionStart = oldStart;
+  oldSelectionEnd   = oldEnd;
+}
+
 static void TestAnchorClickedCallback(TextField control, const char* href, unsigned int hrefLength)
 {
   tet_infoline(" TestAnchorClickedCallback");
@@ -3988,6 +4001,286 @@ int UtcDaliToolkitTextFieldEllipsisPositionProperty(void)
   END_TEST;
 }
 
+int UtcDaliTextFieldCopyText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextFieldCopyText ");
+
+  TextField textField = TextField::New();
+
+  std::string selectedText = "";
+  std::string copiedText = "";
+
+  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();
+
+  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 );
+
+  // Hello is copied
+  copiedText = DevelTextField::CopyText( textField );
+  DALI_TEST_EQUALS( "Hello", copiedText, 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 );
+
+  // world is copied
+  copiedText = DevelTextField::CopyText( textField );
+  DALI_TEST_EQUALS( "world", copiedText, TEST_LOCATION );
+
+  // "lo wo" is selected
+  DevelTextField::SelectText( textField, 3, 8 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "lo wo", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_START ).Get<int>(), 3, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_END ).Get<int>(), 8, TEST_LOCATION );
+
+  // "lo wo" is copied
+  copiedText = DevelTextField::CopyText( textField );
+  DALI_TEST_EQUALS( "lo wo", copiedText, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliTextFieldCutText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextFieldCutText ");
+
+  TextField textField = TextField::New();
+
+  std::string selectedText = "";
+
+  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();
+
+  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 );
+
+  // Hello is cut
+  DALI_TEST_EQUALS( "Hello", DevelTextField::CutText( textField ), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), " world", TEST_LOCATION );
+
+  // " w" is selected
+  DevelTextField::SelectText( textField, 0, 2 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( " w", 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>(), 2, TEST_LOCATION );
+
+  // " w" is cut
+  DALI_TEST_EQUALS( " w", DevelTextField::CutText( textField ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), "orld", TEST_LOCATION );
+
+  // Test Cut from the middle
+
+  // "rl" is selected
+  DevelTextField::SelectText( textField, 1, 3 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "rl", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_START ).Get<int>(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_END ).Get<int>(), 3, TEST_LOCATION );
+
+  // "rl" is cut
+  DALI_TEST_EQUALS( "rl", DevelTextField::CutText( textField ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), "od", TEST_LOCATION );
+
+  // Test Cut from the end
+
+  // "d" is selected
+  DevelTextField::SelectText( textField, 1, 2 );
+
+  application.SendNotification();
+  application.Render();
+
+  selectedText = textField.GetProperty( DevelTextField::Property::SELECTED_TEXT ).Get<std::string>();
+  DALI_TEST_EQUALS( "d", selectedText, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_START ).Get<int>(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( DevelTextField::Property::SELECTED_TEXT_END ).Get<int>(), 2, TEST_LOCATION );
+
+  // "d" is cut
+  DALI_TEST_EQUALS( "d", DevelTextField::CutText( textField ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), "o", TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliTextFieldPasteText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextFieldPasteText ");
+
+  TextField textField = TextField::New();
+
+  application.GetScene().Add( textField );
+
+  std::string cutText = "";
+  std::string copiedText = "";
+
+  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();
+
+  textField.SetProperty( TextField::Property::TEXT, "Hello World" );
+
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text editor
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Select some text in the right of the current cursor position
+  DevelTextField::SelectText( textField, 0, 3 );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Cut the selected text
+  cutText = DevelTextField::CutText(textField);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( "Hel", cutText, TEST_LOCATION );
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), "lo World", TEST_LOCATION );
+
+  DevelTextField::SelectText( textField, 0, 3 );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Copy the selected text
+  copiedText = DevelTextField::CopyText(textField);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( "lo ", copiedText, TEST_LOCATION );
+  DALI_TEST_EQUALS( "lo World", textField.GetProperty<std::string>( TextField::Property::TEXT ), TEST_LOCATION );
+
+  // Move the cursor to the end of the line
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Paste the selected text at the current cursor position
+  DevelTextField::PasteText(textField);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textField.GetProperty( TextField::Property::TEXT ).Get<std::string>(), "lo Worldlo ", TEST_LOCATION );
+
+  END_TEST;
+}
 int utcDaliTextFieldCursorPositionChangedSignal(void)
 {
   ToolkitTestApplication application;
@@ -4088,4 +4381,130 @@ int utcDaliTextFieldCursorPositionChangedSignal(void)
   DALI_TEST_EQUALS(oldCursorPos, 5, TEST_LOCATION);
 
   END_TEST;
+}
+
+int utcDaliTextFieldSelectionChangedSignal(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldSelectionChangedSignal");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  // connect to the selection changed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextField::SelectionChangedSignal(field).Connect(&TestSelectionChangedCallback);
+  bool selectionChangedSignal = false;
+  field.ConnectSignal( testTracker, "selectionChanged",   CallbackFunctor(&selectionChangedSignal) );
+
+  field.SetProperty( TextField::Property::TEXT, "Hello world Hello world" );
+  field.SetProperty( TextField::Property::POINT_SIZE, 10.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text field
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Select some text in the right of the current cursor position
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_SHIFT_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_RIGHT, KEY_SHIFT_MODIFIER, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 1, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_ESCAPE, 0, 0, Integration::KeyEvent::UP, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 2, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+  field.SetKeyInputFocus();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DevelTextField::SelectText( field ,0, 5 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  field.SetProperty( DevelTextField::Property::PRIMARY_CURSOR_POSITION, 3);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 5, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  // select all text
+  DevelTextField::SelectWholeText(field);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, oldSelectionEnd, TEST_LOCATION);
+
+  gSelectionChangedCallbackCalled = false;
+
+  // select none
+  DevelTextField::SelectNone(field);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gSelectionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldSelectionStart, 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(oldSelectionEnd, 23, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index 563bc02..74a57e6 100644 (file)
@@ -50,6 +50,11 @@ InputFilteredSignalType& InputFilteredSignal(TextEditor textEditor)
   return GetImpl(textEditor).InputFilteredSignal();
 }
 
+SelectionChangedSignalType& SelectionChangedSignal(TextEditor textEditor)
+{
+  return GetImpl(textEditor).SelectionChangedSignal();
+}
+
 void SelectWholeText(TextEditor textEditor)
 {
   GetImpl(textEditor).SelectWholeText();
@@ -70,6 +75,21 @@ void ScrollBy(TextEditor textEditor, Vector2 scroll)
   GetImpl(textEditor).ScrollBy(scroll);
 }
 
+string CopyText(TextEditor textEditor)
+{
+  return GetImpl(textEditor).CopyText();
+}
+
+string CutText(TextEditor textEditor)
+{
+  return GetImpl(textEditor).CutText();
+}
+
+void PasteText(TextEditor textEditor)
+{
+  GetImpl(textEditor).PasteText();
+}
+
 } // namespace DevelTextEditor
 
 } // namespace Toolkit
index d9c2db6..345368d 100644 (file)
@@ -376,6 +376,27 @@ using InputFilteredSignalType = Signal<void(TextEditor, Toolkit::InputFilter::Pr
 DALI_TOOLKIT_API InputFilteredSignalType& InputFilteredSignal(TextEditor textEditor);
 
 /**
+ * @brief selection changed signal type.
+ *
+ * @note Signal
+ *  - uint32_t  : selection start before the change.
+ *  - uint32_t  : selection end before the change.
+ */
+using SelectionChangedSignalType = Signal<void(TextEditor, uint32_t, uint32_t)>;
+
+/**
+ * @brief This signal is emitted when the selection has been changed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName( TextEditor textEditor, uint32_t oldStart, uint32_t oldEnd);
+ * @endcode
+ * @param[in] textEditor The instance of TextEditor.
+ * @return The signal to connect to
+ */
+DALI_TOOLKIT_API SelectionChangedSignalType& SelectionChangedSignal(TextEditor textEditor);
+
+/**
  * @brief Select the whole text of TextEditor.
  *
  * @param[in] textEditor The instance of TextEditor.
@@ -412,6 +433,29 @@ DALI_TOOLKIT_API void SelectText(TextEditor textEditor, const uint32_t start, co
  */
 DALI_TOOLKIT_API void ScrollBy(TextEditor textEditor, Vector2 scroll);
 
+/**
+ * @brief Copy and return the selected text of TextEditor.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @return The copied text.
+ */
+DALI_TOOLKIT_API std::string CopyText(TextEditor textEditor);
+
+/**
+ * @brief Cut and return the selected text of TextEditor.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @return The cut text.
+ */
+DALI_TOOLKIT_API std::string CutText(TextEditor textEditor);
+
+/**
+ * @brief Paste the most recent clipboard text item into the TextEditor.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ */
+DALI_TOOLKIT_API void PasteText(TextEditor textEditor);
+
 } // namespace DevelTextEditor
 
 } // namespace Toolkit
index dd9bd51..e9a609f 100644 (file)
@@ -45,6 +45,11 @@ InputFilteredSignalType& InputFilteredSignal(TextField textField)
   return GetImpl(textField).InputFilteredSignal();
 }
 
+SelectionChangedSignalType& SelectionChangedSignal(TextField textField)
+{
+  return GetImpl(textField).SelectionChangedSignal();
+}
+
 void SelectWholeText(TextField textField)
 {
   GetImpl(textField).SelectWholeText();
@@ -60,6 +65,21 @@ void SelectText(TextField textField, const uint32_t start, const uint32_t end)
   GetImpl(textField).SelectText(start, end);
 }
 
+string CopyText(TextField textField)
+{
+  return GetImpl(textField).CopyText();
+}
+
+string CutText(TextField textField)
+{
+  return GetImpl(textField).CutText();
+}
+
+void PasteText(TextField textField)
+{
+  GetImpl(textField).PasteText();
+}
+
 } // namespace DevelTextField
 
 } // namespace Toolkit
index 3f35f9a..4dff21d 100644 (file)
@@ -298,6 +298,27 @@ using InputFilteredSignalType = Signal<void(TextField, Toolkit::InputFilter::Pro
 DALI_TOOLKIT_API InputFilteredSignalType& InputFilteredSignal(TextField textField);
 
 /**
+ * @brief selection changed signal type.
+ *
+ * @note Signal
+ *  - uint32_t  : selection start before the change.
+ *  - uint32_t  : selection end before the change.
+ */
+using SelectionChangedSignalType = Signal<void(TextField, uint32_t, uint32_t)>;
+
+/**
+ * @brief This signal is emitted when the selection has been changed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName( TextField textField, uint32_t oldStart, uint32_t oldEnd);
+ * @endcode
+ * @param[in] textField The instance of TextField.
+ * @return The signal to connect to
+ */
+DALI_TOOLKIT_API SelectionChangedSignalType& SelectionChangedSignal(TextField textField);
+
+/**
  * @brief Select the whole text of TextField.
  *
  * @param[in] textField The instance of TextField.
@@ -326,6 +347,29 @@ DALI_TOOLKIT_API void SelectNone(TextField textField);
  */
 DALI_TOOLKIT_API void SelectText(TextField textField, const uint32_t start, const uint32_t end);
 
+/**
+ * @brief Copy and return the selected text of TextField.
+ *
+ * @param[in] textField The instance of TextField.
+ * @return The copied text.
+ */
+DALI_TOOLKIT_API std::string CopyText(TextField textField);
+
+/**
+ * @brief Cut and return the selected text of TextField.
+ *
+ * @param[in] textField The instance of TextField.
+ * @return The cut text.
+ */
+DALI_TOOLKIT_API std::string CutText(TextField textField);
+
+/**
+ * @brief Paste the most recent clipboard text item into the TextField.
+ *
+ * @param[in] textField The instance of TextField.
+ */
+DALI_TOOLKIT_API void PasteText(TextField textField);
+
 } // namespace DevelTextField
 
 } // namespace Toolkit
index 090bf9a..7a3b4ba 100644 (file)
@@ -159,7 +159,7 @@ DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "maxLengthReached",      SIGNAL_MA
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "anchorClicked",         SIGNAL_ANCHOR_CLICKED         )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputFiltered",         SIGNAL_INPUT_FILTERED         )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "cursorPositionChanged", SIGNAL_CURSOR_POSITION_CHANGED)
-
+DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "selectionChanged",      SIGNAL_SELECTION_CHANGED      )
 
 DALI_TYPE_REGISTRATION_END()
 // clang-format on
@@ -1263,6 +1263,35 @@ void TextEditor::SelectText(const uint32_t start, const uint32_t end)
   }
 }
 
+string TextEditor::CopyText()
+{
+  string copiedText = "";
+  if(mController && mController->IsShowingRealText())
+  {
+    copiedText = mController->CopyText();
+  }
+  return copiedText;
+}
+
+string TextEditor::CutText()
+{
+  string cutText = "";
+  if(mController && mController->IsShowingRealText())
+  {
+    cutText = mController->CutText();
+  }
+  return cutText;
+}
+
+void TextEditor::PasteText()
+{
+  if(mController)
+  {
+    SetKeyInputFocus(); //Giving focus to the editor that was passed to the PasteText in case the passed editor (current editor) doesn't have focus.
+    mController->PasteText();
+  }
+}
+
 void TextEditor::ScrollBy(Vector2 scroll)
 {
   if(mController && mController->IsShowingRealText())
@@ -1324,6 +1353,11 @@ DevelTextEditor::InputFilteredSignalType& TextEditor::InputFilteredSignal()
   return mInputFilteredSignal;
 }
 
+DevelTextEditor::SelectionChangedSignalType& TextEditor::SelectionChangedSignal()
+{
+  return mSelectionChangedSignal;
+}
+
 Text::ControllerPtr TextEditor::GetTextController()
 {
   return mController;
@@ -1376,6 +1410,14 @@ bool TextEditor::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface*
       editorImpl.InputFilteredSignal().Connect(tracker, functor);
     }
   }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_SELECTION_CHANGED))
+  {
+    if(editor)
+    {
+      Internal::TextEditor& editorImpl(GetImpl(editor));
+      editorImpl.SelectionChangedSignal().Connect(tracker, functor);
+    }
+  }
   else
   {
     // signalName does not match any signal
@@ -1613,6 +1655,11 @@ void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
     EmitCursorPositionChangedSignal();
   }
 
+  if(mSelectionChanged)
+  {
+    EmitSelectionChangedSignal();
+  }
+
   // The text-editor emits signals when the input style changes. These changes of style are
   // detected during the relayout process (size negotiation), i.e after the cursor has been moved. Signals
   // can't be emitted during the size negotiation as the callbacks may update the UI.
@@ -1985,6 +2032,31 @@ void TextEditor::InputFiltered(Toolkit::InputFilter::Property::Type type)
   mInputFilteredSignal.Emit(handle, type);
 }
 
+void TextEditor::EmitSelectionChangedSignal()
+{
+  Dali::Toolkit::TextEditor handle(GetOwner());
+  mSelectionChangedSignal.Emit(handle, mOldSelectionStart, mOldSelectionEnd);
+  mSelectionChanged = false;
+}
+
+void TextEditor::SelectionChanged(uint32_t oldStart, uint32_t oldEnd, uint32_t newStart, uint32_t newEnd)
+{
+  if(((oldStart != newStart) || (oldEnd != newEnd)) && !mSelectionChanged)
+  {
+    mSelectionChanged  = true;
+    mOldSelectionStart = oldStart;
+    mOldSelectionEnd   = oldEnd;
+
+    if(mOldSelectionStart > mOldSelectionEnd)
+    {
+      //swap
+      uint32_t temp      = mOldSelectionStart;
+      mOldSelectionStart = mOldSelectionEnd;
+      mOldSelectionEnd   = temp;
+    }
+  }
+}
+
 void TextEditor::AddDecoration(Actor& actor, bool needsClipping)
 {
   if(actor)
@@ -2263,7 +2335,8 @@ TextEditor::TextEditor()
   mScrollBarEnabled(false),
   mScrollStarted(false),
   mTextChanged(false),
-  mCursorPositionChanged(false)
+  mCursorPositionChanged(false),
+  mSelectionChanged(false)
 {
 }
 
index 9b22782..aff671a 100644 (file)
@@ -103,6 +103,11 @@ public:
   DevelTextEditor::InputFilteredSignalType& InputFilteredSignal();
 
   /**
+   * @copydoc Dali::Toollkit::TextEditor::SelectionChangedSignal()
+   */
+  DevelTextEditor::SelectionChangedSignalType& SelectionChangedSignal();
+
+  /**
    * Connects a callback function with the object's signals.
    * @param[in] object The object providing the signal.
    * @param[in] tracker Used to disconnect the signal.
@@ -241,6 +246,11 @@ private: // From Control
   void InputStyleChanged(Text::InputStyle::Mask inputStyleMask) override;
 
   /**
+   * @copydoc Text::SelectableControlInterface::SelectionChanged()
+   */
+  void SelectionChanged(uint32_t oldStart, uint32_t oldEnd, uint32_t newStart, uint32_t newEnd) override;
+
+  /**
    * @copydoc Text::EditableControlInterface::AddDecoration()
    */
   void AddDecoration(Actor& actor, bool needsClipping) override;
@@ -316,6 +326,21 @@ public:
    */
   void SetEditable(bool editable) override;
 
+  /**
+   * @copydoc Text::EditableControlInterface::CopyText()
+   */
+  string CopyText() override;
+
+  /**
+   * @copydoc Text::EditableControlInterface::CutText()
+   */
+  string CutText() override;
+
+  /**
+   * @copydoc Text::EditableControlInterface::PasteText()
+   */
+  void PasteText() override;
+
   // From AnchorControlInterface
 
   /**
@@ -383,6 +408,11 @@ private: // Implementation
   void EmitTextChangedSignal();
 
   /**
+   * @brief Emits SelectionChanged signal.
+   */
+  void EmitSelectionChangedSignal();
+
+  /**
    * @brief set RenderActor's position with new scrollPosition
    *
    * Apply updated scroll position or start scroll animation if VerticalScrollAnimation is enabled
@@ -442,6 +472,7 @@ private: // Data
   Toolkit::DevelTextEditor::AnchorClickedSignalType         mAnchorClickedSignal;
   Toolkit::DevelTextEditor::InputFilteredSignalType         mInputFilteredSignal;
   Toolkit::DevelTextEditor::CursorPositionChangedSignalType mCursorPositionChangedSignal;
+  Toolkit::DevelTextEditor::SelectionChangedSignalType      mSelectionChangedSignal;
 
   InputMethodContext            mInputMethodContext;
   Text::ControllerPtr           mController;
@@ -470,10 +501,15 @@ private: // Data
   bool  mScrollStarted : 1;
   bool  mTextChanged : 1;           ///< If true, emits TextChangedSignal in next OnRelayout().
   bool  mCursorPositionChanged : 1; ///< If true, emits CursorPositionChangedSignal at the end of OnRelayout().
+  bool  mSelectionChanged : 1;      ///< If true, emits SelectionChangedSignal at the end of OnRelayout().
 
   //args for cursor PositionChanged event
   unsigned int mOldPosition;
 
+  //args for selection changed event
+  uint32_t mOldSelectionStart;
+  uint32_t mOldSelectionEnd;
+
   /**
    * @brief This structure is to connect TextEditor with Accessible functions.
    */
index ab9878d..c6fd205 100644 (file)
@@ -146,6 +146,7 @@ DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "inputStyleChanged",     SIGNAL_INP
 DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "anchorClicked",         SIGNAL_ANCHOR_CLICKED         )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "inputFiltered",         SIGNAL_INPUT_FILTERED         )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "cursorPositionChanged", SIGNAL_CURSOR_POSITION_CHANGED)
+DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "selectionChanged",      SIGNAL_SELECTION_CHANGED      )
 
 DALI_TYPE_REGISTRATION_END()
 // clang-format on
@@ -1215,6 +1216,35 @@ Uint32Pair TextField::GetTextSelectionRange() const
   return range;
 }
 
+string TextField::CopyText()
+{
+  string copiedText = "";
+  if(mController && mController->IsShowingRealText())
+  {
+    copiedText = mController->CopyText();
+  }
+  return copiedText;
+}
+
+string TextField::CutText()
+{
+  string cutText = "";
+  if(mController && mController->IsShowingRealText())
+  {
+    cutText = mController->CutText();
+  }
+  return cutText;
+}
+
+void TextField::PasteText()
+{
+  if(mController)
+  {
+    SetKeyInputFocus(); //Giving focus to the field that was passed to the PasteText in case the passed field (current field) doesn't have focus.
+    mController->PasteText();
+  }
+}
+
 InputMethodContext TextField::GetInputMethodContext()
 {
   return mInputMethodContext;
@@ -1263,6 +1293,14 @@ bool TextField::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface*
       fieldImpl.InputFilteredSignal().Connect(tracker, functor);
     }
   }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_SELECTION_CHANGED))
+  {
+    if(field)
+    {
+      Internal::TextField& fieldImpl(GetImpl(field));
+      fieldImpl.SelectionChangedSignal().Connect(tracker, functor);
+    }
+  }
   else
   {
     // signalName does not match any signal
@@ -1302,6 +1340,11 @@ DevelTextField::InputFilteredSignalType& TextField::InputFilteredSignal()
   return mInputFilteredSignal;
 }
 
+DevelTextField::SelectionChangedSignalType& TextField::SelectionChangedSignal()
+{
+  return mSelectionChangedSignal;
+}
+
 void TextField::OnInitialize()
 {
   Actor self = Self();
@@ -1503,6 +1546,11 @@ void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
     EmitCursorPositionChangedSignal();
   }
 
+  if(mSelectionChanged)
+  {
+    EmitSelectionChangedSignal();
+  }
+
   // The text-field emits signals when the input style changes. These changes of style are
   // detected during the relayout process (size negotiation), i.e after the cursor has been moved. Signals
   // can't be emitted during the size negotiation as the callbacks may update the UI.
@@ -1892,6 +1940,31 @@ void TextField::InputFiltered(Toolkit::InputFilter::Property::Type type)
   mInputFilteredSignal.Emit(handle, type);
 }
 
+void TextField::EmitSelectionChangedSignal()
+{
+  Dali::Toolkit::TextField handle(GetOwner());
+  mSelectionChangedSignal.Emit(handle, mOldSelectionStart, mOldSelectionEnd);
+  mSelectionChanged = false;
+}
+
+void TextField::SelectionChanged(uint32_t oldStart, uint32_t oldEnd, uint32_t newStart, uint32_t newEnd)
+{
+  if(((oldStart != newStart) || (oldEnd != newEnd)) && !mSelectionChanged)
+  {
+    mSelectionChanged  = true;
+    mOldSelectionStart = oldStart;
+    mOldSelectionEnd   = oldEnd;
+
+    if(mOldSelectionStart > mOldSelectionEnd)
+    {
+      //swap
+      uint32_t temp      = mOldSelectionStart;
+      mOldSelectionStart = mOldSelectionEnd;
+      mOldSelectionEnd   = temp;
+    }
+  }
+}
+
 void TextField::AddDecoration(Actor& actor, bool needsClipping)
 {
   if(actor)
@@ -2029,7 +2102,8 @@ TextField::TextField()
   mExceedPolicy(Dali::Toolkit::TextField::EXCEED_POLICY_CLIP),
   mHasBeenStaged(false),
   mTextChanged(false),
-  mCursorPositionChanged(false)
+  mCursorPositionChanged(false),
+  mSelectionChanged(false)
 {
 }
 
index dfd12a2..0b24ff0 100644 (file)
@@ -126,6 +126,11 @@ public:
    */
   DevelTextField::InputFilteredSignalType& InputFilteredSignal();
 
+  /**
+   * @copydoc TextField::SelectionChangedSignal()
+   */
+  DevelTextField::SelectionChangedSignalType& SelectionChangedSignal();
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
@@ -232,6 +237,11 @@ private: // From Control
   void InputStyleChanged(Text::InputStyle::Mask inputStyleMask) override;
 
   /**
+   * @copydoc Text::SelectableControlInterface::SelectionChanged()
+   */
+  void SelectionChanged(uint32_t oldStart, uint32_t oldEnd, uint32_t newStart, uint32_t newEnd) override;
+
+  /**
    * @copydoc Text::EditableControlInterface::AddDecoration()
    */
   void AddDecoration(Actor& actor, bool needsClipping) override;
@@ -288,6 +298,21 @@ public:
    */
   void SetEditable(bool editable) override;
 
+  /**
+   * @copydoc Dali::EditableControlInterface::CopyText()
+   */
+  string CopyText() override;
+
+  /**
+   * @copydoc Dali::EditableControlInterface::CutText()
+   */
+  string CutText() override;
+
+  /**
+   * @copydoc Text::EditableControlInterface::PasteText()
+   */
+  void PasteText() override;
+
   // From AnchorControlInterface
 
   /**
@@ -353,6 +378,11 @@ private: // Implementation
   void EmitCursorPositionChangedSignal();
 
   /**
+   * @brief Emits SelectionChanged signal.
+   */
+  void EmitSelectionChangedSignal();
+
+  /**
    * @brief Callback function for when the layout is changed.
    * @param[in] actor The actor whose layoutDirection is changed.
    * @param[in] type  The layoutDirection.
@@ -397,6 +427,7 @@ private: // Data
   Toolkit::DevelTextField::AnchorClickedSignalType         mAnchorClickedSignal;
   Toolkit::DevelTextField::InputFilteredSignalType         mInputFilteredSignal;
   Toolkit::DevelTextField::CursorPositionChangedSignalType mCursorPositionChangedSignal;
+  Toolkit::DevelTextField::SelectionChangedSignalType      mSelectionChangedSignal;
 
   InputMethodContext       mInputMethodContext;
   Text::ControllerPtr      mController;
@@ -417,10 +448,15 @@ private: // Data
   bool  mHasBeenStaged : 1;
   bool  mTextChanged : 1;           ///< If true, emits TextChangedSignal in next OnRelayout().
   bool  mCursorPositionChanged : 1; ///< If true, emits CursorPositionChangedSignal at the end of OnRelayout().
+  bool  mSelectionChanged : 1;      ///< If true, emits SelectionChangedSignal at the end of OnRelayout().
 
   //args for cursor position changed event
   unsigned int mOldPosition;
 
+  //args for selection changed event
+  uint32_t mOldSelectionStart;
+  uint32_t mOldSelectionEnd;
+
 protected:
   /**
    * @brief This structure is to connect TextField with Accessible functions.
index 79a917e..5d78405 100644 (file)
@@ -84,8 +84,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);
@@ -158,12 +167,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;
@@ -597,12 +616,21 @@ void Controller::EventHandler::ProcessModifyEvents(Controller& 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
@@ -924,43 +952,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:
index 9574f8b..e24e8aa 100644 (file)
@@ -410,7 +410,15 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
       if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
       {
+        uint32_t oldStart = eventData.mLeftSelectionPosition;
+        uint32_t oldEnd   = eventData.mRightSelectionPosition;
+
         eventData.mRightSelectionPosition += cursorPositionDelta;
+
+        if(impl.mSelectableControlInterface != nullptr)
+        {
+          impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+        }
       }
       selecting = true;
     }
@@ -677,8 +685,16 @@ 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();
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
     }
   }
 }
@@ -693,11 +709,19 @@ void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
     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);
       eventData.mUpdateCursorPosition      = true;
       eventData.mUpdateInputStyle          = true;
       eventData.mScrollAfterUpdatePosition = true;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
+      }
     }
   }
 }
@@ -716,11 +740,19 @@ void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, cons
 
     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;
+
+      if(impl.mSelectableControlInterface != nullptr)
+      {
+        impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
+      }
     }
   }
 }
@@ -745,6 +777,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)
   {
@@ -804,6 +838,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)
@@ -829,6 +868,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)
   {
@@ -885,6 +926,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;
 }
 
@@ -999,6 +1045,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)
     {
@@ -1029,6 +1077,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;
index a96d23c..d810928 100644 (file)
@@ -1209,7 +1209,9 @@ void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint3
 
   if(mEventData->mSelectionEnabled && (pStart || pEnd))
   {
-    uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+    uint32_t length   = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+    uint32_t oldStart = mEventData->mLeftSelectionPosition;
+    uint32_t oldEnd   = mEventData->mRightSelectionPosition;
 
     if(pStart)
     {
@@ -1233,6 +1235,11 @@ void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint3
       mEventData->mUpdateLeftSelectionPosition  = true;
       mEventData->mUpdateRightSelectionPosition = true;
     }
+
+    if(mSelectableControlInterface != nullptr)
+    {
+      mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+    }
   }
 }
 
@@ -1265,9 +1272,18 @@ bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focus
   // If there is no focus, only the value is updated.
   if(focused)
   {
+    bool     wasInSelectingState = mEventData->mState == EventData::SELECTING;
+    uint32_t oldStart            = mEventData->mLeftSelectionPosition;
+    uint32_t oldEnd              = mEventData->mRightSelectionPosition;
     ChangeState(EventData::EDITING);
     mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
     mEventData->mUpdateCursorPosition                                        = true;
+
+    if(mSelectableControlInterface != nullptr && wasInSelectingState)
+    {
+      mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+    }
+
     ScrollTextToMatchCursor();
   }
 
@@ -1384,9 +1400,17 @@ void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteA
 
 void Controller::Impl::SetSelection(int start, int end)
 {
+  uint32_t oldStart = mEventData->mLeftSelectionPosition;
+  uint32_t oldEnd   = mEventData->mRightSelectionPosition;
+
   mEventData->mLeftSelectionPosition  = start;
   mEventData->mRightSelectionPosition = end;
   mEventData->mUpdateCursorPosition   = true;
+
+  if(mSelectableControlInterface != nullptr)
+  {
+    mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
+  }
 }
 
 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
index 61b8c7c..c200c38 100644 (file)
@@ -69,6 +69,11 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
        (EventData::EDITING_WITH_GRAB_HANDLE == eventData->mState) ||
        (EventData::EDITING_WITH_PASTE_POPUP == eventData->mState))
     {
+      if((impl.mSelectableControlInterface != nullptr) && (EventData::SELECTING == eventData->mState))
+      {
+        impl.mSelectableControlInterface->SelectionChanged(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition, eventData->mPrimaryCursorPosition, eventData->mPrimaryCursorPosition);
+      }
+
       impl.ChangeState(EventData::EDITING);
     }
   }
index b058e26..30c6e04 100644 (file)
@@ -1913,6 +1913,59 @@ string Controller::GetSelectedText() const
   return text;
 }
 
+string Controller::CopyText()
+{
+  string text;
+  mImpl->RetrieveSelection(text, false);
+  mImpl->SendSelectionToClipboard(false); // Text not modified
+
+  mImpl->mEventData->mUpdateCursorPosition = true;
+
+  mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
+
+  return text;
+}
+
+string Controller::CutText()
+{
+  string text;
+  mImpl->RetrieveSelection(text, false);
+
+  if(!IsEditable())
+  {
+    return "";
+  }
+
+  mImpl->SendSelectionToClipboard(true); // Synchronous call to modify text
+  mImpl->mOperationsPending = ALL_OPERATIONS;
+
+  if((0u != mImpl->mModel->mLogicalModel->mText.Count()) ||
+     !mImpl->IsPlaceholderAvailable())
+  {
+    mImpl->QueueModifyEvent(ModifyEvent::TEXT_DELETED);
+  }
+  else
+  {
+    ShowPlaceholderText();
+  }
+
+  mImpl->mEventData->mUpdateCursorPosition = true;
+  mImpl->mEventData->mScrollAfterDelete    = true;
+
+  mImpl->RequestRelayout();
+
+  if(nullptr != mImpl->mEditableControlInterface)
+  {
+    mImpl->mEditableControlInterface->TextChanged(true);
+  }
+  return text;
+}
+
+void Controller::PasteText()
+{
+  mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
+}
+
 InputMethodContext::CallbackData Controller::OnInputMethodContextEvent(InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent)
 {
   return EventHandler::OnInputMethodContextEvent(*this, inputMethodContext, inputMethodContextEvent);
index 109be1a..9302cf5 100644 (file)
@@ -1633,6 +1633,21 @@ public: // Text-input Event Queuing.
   void SelectWholeText();
 
   /**
+   * @copydoc Text::EditableControlInterface::CopyText()
+   */
+  string CopyText();
+
+  /**
+   * @copydoc Text::EditableControlInterface::CutText()
+   */
+  string CutText();
+
+  /**
+   * @copydoc Text::EditableControlInterface::PasteText()
+   */
+  void PasteText();
+
+  /**
    * @copydoc Text::SelectableControlInterface::SelectNone()
    */
   void SelectNone();
@@ -1751,8 +1766,7 @@ private: // Update.
   void InsertText(const std::string& text, InsertType type);
 
   /**
-   * @brief Paste given string into Text model
-   * @param[in] stringToPaste this string will be inserted into the text model
+   * @copydoc Text::EditableControlInterface::PasteText()
    */
   void PasteText(const std::string& stringToPaste);
 
index cd071f2..12d4256 100644 (file)
@@ -112,6 +112,23 @@ public:
    * @param[in] editable The editable status.
    */
   virtual void SetEditable(bool editable) = 0;
+
+  /**
+   * @brief Called to copy the selected text.
+   * @return The copied text.
+   */
+  virtual string CopyText() = 0;
+
+  /**
+   * @brief Called to cut the selected text.
+   * @return The cut text.
+   */
+  virtual string CutText() = 0;
+
+  /**
+   * @brief Called to paste the most recent clipboard text item into the control.
+   */
+  virtual void PasteText() = 0;
 };
 
 } // namespace Text
index 3d5feef..931cf6b 100644 (file)
@@ -72,6 +72,16 @@ public:
    * @return The seletced text.
    */
   virtual string GetSelectedText() const = 0;
+
+  /**
+   * @brief Called when the selection has been changed.
+   *
+   * @param oldStart the selection handles start position before the change.
+   * @param oldEnd   the selection handles end position before the change.
+   * @param newStart the selection handles start position after the change.
+   * @param newEnd   the selection handles end position after the change.
+   */
+  virtual void SelectionChanged(uint32_t oldStart, uint32_t oldEnd, uint32_t newStart, uint32_t newEnd) = 0;
 };
 
 } // namespace Text
index b082510..cf63deb 100644 (file)
@@ -491,6 +491,9 @@ void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX
 
   if(characterHit || (Controller::NoTextTap::HIGHLIGHT == action))
   {
+    uint32_t oldStart = eventData->mLeftSelectionPosition;
+    uint32_t oldEnd   = eventData->mRightSelectionPosition;
+
     impl.ChangeState(EventData::SELECTING);
 
     eventData->mLeftSelectionPosition  = selectionStart;
@@ -510,6 +513,11 @@ void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX
 
     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
     eventData->mPrimaryCursorPosition = std::max(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
+
+    if(impl.mSelectableControlInterface != nullptr)
+    {
+      impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
+    }
   }
   else if(Controller::NoTextTap::SHOW_SELECTION_POPUP == action)
   {