[dali_2.0.41] Merge branch 'devel/master' 93/263193/1
authorAdam Bialogonski <adam.b@samsung.com>
Fri, 27 Aug 2021 09:50:19 +0000 (10:50 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 27 Aug 2021 09:50:19 +0000 (10:50 +0100)
Change-Id: Idda67df1c5165dee199f0915af61e53407cd12cd

41 files changed:
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-native-image-source.h [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-native-image.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-native-image-source.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-web-engine.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
automated-tests/src/dali-toolkit/utc-Dali-WebView.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/devel-api/controls/web-view/web-context.cpp
dali-toolkit/devel-api/controls/web-view/web-context.h
dali-toolkit/devel-api/controls/web-view/web-view.cpp
dali-toolkit/devel-api/controls/web-view/web-view.h
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp
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/controls/web-view/web-view-impl.cpp [changed mode: 0644->0755]
dali-toolkit/internal/controls/web-view/web-view-impl.h
dali-toolkit/internal/text/cursor-helper-functions.cpp
dali-toolkit/internal/text/layouts/layout-engine.cpp
dali-toolkit/internal/text/line-run.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
dali-toolkit/internal/text/text-view.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-native-image-source.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-native-image-source.h
new file mode 100644 (file)
index 0000000..c459e07
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef TEST_NATIVE_IMAGE_SOURCE_H
+#define TEST_NATIVE_IMAGE_SOURCE_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Dali
+{
+namespace NativeImageSourceTest
+{
+
+const char* GetCustomFragmentPrefix();
+
+}
+} // namespace Dali
+
+#endif // TEST_NATIVE_IMAGE_SOURCE_H
index fb9451c..bd1bfcc 100644 (file)
@@ -78,11 +78,6 @@ public:
     mCallStack.PushCall("GetTextureTarget", "");
     return GL_TEXTURE_EXTERNAL_OES;
   };
-  inline virtual const char* GetCustomFragmentPrefix() const
-  {
-    mCallStack.PushCall("GetCustomFragmentPrefix", "");
-    return "#extension GL_OES_EGL_image_external:require\n";
-  };
   inline virtual bool ApplyNativeFragmentShader(std::string& shader)
   {
     mCallStack.PushCall("ApplyNativeFragmentShader", "");
index 29ea5d5..d2be2d7 100755 (executable)
 // CLASS HEADER
 #include <dali/public-api/adaptor-framework/native-image-source.h>
 
+// INTERNAL INCLUDES
+#include "test-native-image-source.h"
+
 // EXTERNAL INCLUDES
 #include <dali/public-api/object/any.h>
 
 namespace Dali
 {
 
+const char* NativeImageSourceTest::GetCustomFragmentPrefix()
+{
+  return "#extension GL_OES_EGL_image_external:require\n";
+}
+
 NativeImageSourcePtr NativeImageSource::New( unsigned int width, unsigned int height, ColorDepth depth )
 {
   Any empty;
@@ -100,13 +108,9 @@ int NativeImageSource::GetTextureTarget() const
   return 0;
 }
 
-const char* NativeImageSource::GetCustomFragmentPrefix() const
-{
-  return "";
-}
-
 bool NativeImageSource::ApplyNativeFragmentShader(std::string& shader)
 {
+  shader = Dali::NativeImageSourceTest::GetCustomFragmentPrefix() + shader;
   return true;
 }
 
index c1d8700..8f8853c 100755 (executable)
@@ -83,6 +83,7 @@ bool OnStorageUsageAcquired();
 bool OnFormPasswordAcquired();
 bool OnDownloadStarted();
 bool OnMimeOverridden();
+bool OnRequestIntercepted();
 bool OnChangesWatch();
 bool OnPlainTextReceived();
 
@@ -155,7 +156,7 @@ public:
   {
   }
 
-  bool GetWebDatabaseOrigins(Dali::WebEngineContext::WebEngineSecurityOriginAcquiredCallback callback)
+  bool GetWebDatabaseOrigins(Dali::WebEngineContext::WebEngineSecurityOriginAcquiredCallback callback) override
   {
     if (callback)
     {
@@ -165,12 +166,12 @@ public:
     return true;
   }
 
-  bool DeleteWebDatabase(Dali::WebEngineSecurityOrigin& origin)
+  bool DeleteWebDatabase(Dali::WebEngineSecurityOrigin& origin) override
   {
     return true;
   }
 
-  bool GetWebStorageOrigins(Dali::WebEngineContext::WebEngineSecurityOriginAcquiredCallback callback)
+  bool GetWebStorageOrigins(Dali::WebEngineContext::WebEngineSecurityOriginAcquiredCallback callback) override
   {
     if (callback)
     {
@@ -180,7 +181,7 @@ public:
     return true;
   }
 
-  bool GetWebStorageUsageForOrigin(Dali::WebEngineSecurityOrigin& origin, Dali::WebEngineContext::WebEngineStorageUsageAcquiredCallback callback) 
+  bool GetWebStorageUsageForOrigin(Dali::WebEngineSecurityOrigin& origin, Dali::WebEngineContext::WebEngineStorageUsageAcquiredCallback callback) override
   {
     if (callback)
     {
@@ -194,7 +195,7 @@ public:
   {
   }
 
-  bool DeleteWebStorage(Dali::WebEngineSecurityOrigin& origin)
+  bool DeleteWebStorage(Dali::WebEngineSecurityOrigin& origin) override
   {
     return true;
   }
@@ -207,12 +208,12 @@ public:
   {
   }
 
-  bool DeleteApplicationCache(Dali::WebEngineSecurityOrigin& origin)
+  bool DeleteApplicationCache(Dali::WebEngineSecurityOrigin& origin) override
   {
     return true;
   }
 
-  void GetFormPasswordList(Dali::WebEngineContext::WebEngineFormPasswordAcquiredCallback callback)
+  void GetFormPasswordList(Dali::WebEngineContext::WebEngineFormPasswordAcquiredCallback callback) override
   {
     if (callback)
     {
@@ -221,7 +222,7 @@ public:
     }
   }
 
-  void RegisterDownloadStartedCallback(Dali::WebEngineContext::WebEngineDownloadStartedCallback callback)
+  void RegisterDownloadStartedCallback(Dali::WebEngineContext::WebEngineDownloadStartedCallback callback) override
   {
     if (callback)
     {
@@ -230,7 +231,7 @@ public:
     }
   }
 
-  void RegisterMimeOverriddenCallback(Dali::WebEngineContext::WebEngineMimeOverriddenCallback callback)
+  void RegisterMimeOverriddenCallback(Dali::WebEngineContext::WebEngineMimeOverriddenCallback callback) override
   {
     if (callback)
     {
@@ -239,6 +240,15 @@ public:
     }
   }
 
+  void RegisterRequestInterceptedCallback(Dali::WebEngineContext::WebEngineRequestInterceptedCallback callback) override
+  {
+    if (callback)
+    {
+      ConnectToGlobalSignal(&OnRequestIntercepted);
+      mRequestInterceptedCallback = callback;
+    }
+  }
+
   void EnableCache( bool cacheEnabled ) override
   {
     mockCacheEnabled = cacheEnabled;
@@ -317,10 +327,11 @@ public:
 
 public:
   Dali::WebEngineContext::WebEngineSecurityOriginAcquiredCallback mSecurityOriginAcquiredCallback;
-  Dali::WebEngineContext::WebEngineStorageUsageAcquiredCallback mStorageUsageAcquiredCallback;
-  Dali::WebEngineContext::WebEngineFormPasswordAcquiredCallback mFormPasswordAcquiredCallback;
-  Dali::WebEngineContext::WebEngineDownloadStartedCallback mDownloadStartedCallback;
-  Dali::WebEngineContext::WebEngineMimeOverriddenCallback mMimeOverriddenCallback;
+  Dali::WebEngineContext::WebEngineStorageUsageAcquiredCallback   mStorageUsageAcquiredCallback;
+  Dali::WebEngineContext::WebEngineFormPasswordAcquiredCallback   mFormPasswordAcquiredCallback;
+  Dali::WebEngineContext::WebEngineDownloadStartedCallback        mDownloadStartedCallback;
+  Dali::WebEngineContext::WebEngineMimeOverriddenCallback         mMimeOverriddenCallback;
+  Dali::WebEngineContext::WebEngineRequestInterceptedCallback     mRequestInterceptedCallback;
 
 private:
   Dali::WebEngineContext::CacheModel mockModel;
@@ -611,25 +622,55 @@ public:
     return "http://test.html";
   }
 
+  Dali::Property::Map GetHeaders() const override
+  {
+    return mockHeadersMap;
+  }
+
+  std::string GetMethod() const override
+  {
+    return "GET";
+  }
+
   bool Ignore() override
   {
     return true;
   }
 
-  bool SetResponseStatus(int statusCode, const std::string &customedStatusText) override
+  bool SetResponseStatus(int statusCode, const std::string& customedStatusText) override
+  {
+    return true;
+  }
+
+  bool AddResponseHeader(const std::string& fieldName, const std::string& fieldValue) override
   {
+    mockHeadersMap.Add(fieldName, fieldValue);
     return true;
   }
 
-  bool AddResponseHeader(const std::string &fieldName, const std::string &fieldValue) override
+  bool AddResponseHeaders(const Dali::Property::Map& headers) override
   {
+    mockHeadersMap.Merge(headers);
     return true;
   }
 
-  bool AddResponseBody(const std::string &body, uint32_t length) override
+  bool AddResponseBody(const std::stringbody, uint32_t length) override
   {
     return true;
   }
+
+  bool AddResponse(const std::string& headers, const std::string& body, uint32_t length) override
+  {
+    return true;
+  }
+
+  bool WriteResponseChunk(const std::string& chunk, uint32_t length) override
+  {
+    return true;
+  }
+
+private:
+  Dali::Property::Map mockHeadersMap;
 };
 
 class MockWebEngineConsoleMessage : public Dali::WebEngineConsoleMessage
@@ -835,7 +876,7 @@ public:
     return "test";
   }
 
-  Dali::Property::Map& GetAttributes() const override
+  Dali::Property::Map GetAttributes() const override
   {
     return mockAttributesMap;
   }
@@ -1493,11 +1534,6 @@ public:
     mFormRepostDecidedCallback = callback;
   }
 
-  void RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback)
-  {
-    mRequestInterceptorCallback = callback;
-  }
-
   void RegisterConsoleMessageReceivedCallback(Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback callback)
   {
     mConsoleMessageCallback = callback;
@@ -1572,7 +1608,6 @@ public:
   Dali::WebEnginePlugin::WebEngineUrlChangedCallback             mUrlChangedCallback;
   Dali::WebEnginePlugin::WebEngineFormRepostDecidedCallback      mFormRepostDecidedCallback;
   Dali::WebEnginePlugin::WebEngineFrameRenderedCallback          mFrameRenderedCallback;
-  Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback     mRequestInterceptorCallback;
   Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback mConsoleMessageCallback;
   Dali::WebEnginePlugin::WebEngineResponsePolicyDecidedCallback  mResponsePolicyDecisionCallback;
   Dali::WebEnginePlugin::WebEngineCertificateCallback            mCertificateConfirmCallback;
@@ -1659,11 +1694,6 @@ bool OnLoadUrl()
     {
       gInstance->mFrameRenderedCallback();
     }
-    if (gInstance->mRequestInterceptorCallback)
-    {
-      std::unique_ptr<Dali::WebEngineRequestInterceptor> interceptor(new MockWebEngineRequestInterceptor());
-      gInstance->mRequestInterceptorCallback(std::move(interceptor));
-    }
     if (gInstance->mConsoleMessageCallback)
     {
       std::unique_ptr<Dali::WebEngineConsoleMessage> message(new MockWebEngineConsoleMessage());
@@ -1879,6 +1909,17 @@ bool OnMimeOverridden()
   return false;
 }
 
+bool OnRequestIntercepted()
+{
+  DisconnectFromGlobalSignal(&OnRequestIntercepted);
+  if (gWebEngineContextInstance)
+  {
+    Dali::WebEngineRequestInterceptorPtr interceptor = new MockWebEngineRequestInterceptor();
+    gWebEngineContextInstance->mRequestInterceptedCallback(interceptor);
+  }
+  return false;
+}
+
 bool OnChangesWatch()
 {
   DisconnectFromGlobalSignal( &OnChangesWatch );
@@ -2373,11 +2414,6 @@ void WebEngine::RegisterFormRepostDecidedCallback(Dali::WebEnginePlugin::WebEngi
   Internal::Adaptor::GetImplementation( *this ).RegisterFormRepostDecidedCallback(callback);
 }
 
-void WebEngine::RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback)
-{
-  Internal::Adaptor::GetImplementation( *this ).RegisterRequestInterceptorCallback(callback);
-}
-
 void WebEngine::RegisterConsoleMessageReceivedCallback(Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback callback)
 {
   Internal::Adaptor::GetImplementation( *this ).RegisterConsoleMessageReceivedCallback(callback);
index 5d46d68..7322dbf 100644 (file)
@@ -1590,12 +1590,6 @@ int UtcDaliAnimatedVectorImageVisualFrameDrops(void)
 
   application.GetScene().Add(actor);
 
-  application.SendNotification();
-  application.Render();
-
-  // Trigger count is 1 - render the first frame
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
-
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
@@ -1605,8 +1599,8 @@ int UtcDaliAnimatedVectorImageVisualFrameDrops(void)
   application.SendNotification();
   application.Render();
 
-  // Wait for calculating frame drops
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  // Trigger count is 2 - render the first frame & calculating frame drops
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
 
   // Check dropped frame
   uint32_t frames = Test::VectorAnimationRenderer::GetDroppedFrames();
index 1dbb737..cd834df 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <test-encoded-image-buffer.h>
 #include "dummy-control.h"
+#include "test-native-image-source.h"
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -459,7 +460,7 @@ int UtcDaliImageVisualWithNativeImage(void)
   const Property::Map* outMap = value.GetMap();
   std::string fragmentShader = (*outMap)["fragment"].Get<std::string>();
 
-  const char* fragmentPrefix = nativeImageSource->GetCustomFragmentPrefix();
+  const char* fragmentPrefix = Dali::NativeImageSourceTest::GetCustomFragmentPrefix();
   size_t pos = fragmentShader.find(fragmentPrefix);
 
   DALI_TEST_EQUALS( pos != std::string::npos, true, TEST_LOCATION );
index 9b1d0ff..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;
@@ -140,6 +143,8 @@ static bool gInputFilteredAcceptedCallbackCalled;
 static bool gInputFilteredRejectedCallbackCalled;
 static bool gInputStyleChangedCallbackCalled;
 static bool gMaxCharactersCallBackCalled;
+static bool gCursorPositionChangedCallbackCalled;
+static uint32_t oldCursorPos;
 static Dali::Toolkit::TextEditor::InputStyle::Mask gInputStyleMask;
 
 struct CallbackFunctor
@@ -156,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");
@@ -168,6 +182,14 @@ static void TestAnchorClickedCallback(TextEditor control, const char* href, unsi
   }
 }
 
+static void TestCursorPositionChangedCallback( TextEditor control, unsigned int oldPos )
+{
+  tet_infoline(" TestCursorPositionChangedCallback");
+
+  gCursorPositionChangedCallbackCalled = true;
+  oldCursorPos = oldPos;
+}
+
 static void TestTextChangedCallback( TextEditor control )
 {
   tet_infoline(" TestTextChangedCallback");
@@ -1005,6 +1027,11 @@ int UtcDaliTextEditorSetPropertyP(void)
   application.SendNotification();
   application.Render();
 
+  // Check the line size property
+  DALI_TEST_EQUALS( editor.GetProperty<float>( DevelTextEditor::Property::MIN_LINE_SIZE ), 0.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+  editor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 50.f );
+  DALI_TEST_EQUALS( editor.GetProperty<float>( DevelTextEditor::Property::MIN_LINE_SIZE ), 50.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
   END_TEST;
 }
 
@@ -3975,4 +4002,580 @@ int UtcDaliToolkitTextEditorEllipsisPositionProperty(void)
   DALI_TEST_EQUALS( textEditor.GetProperty< int >( DevelTextEditor::Property::ELLIPSIS_POSITION ), static_cast< int >( Toolkit::DevelText::EllipsisPosition::END ), TEST_LOCATION );
 
   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;
+  tet_infoline(" UtcDaliTextEditorLineSpacing ");
+
+  TextEditor textEditor = TextEditor::New();
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 400.0f, 400.f ) );
+  application.GetScene().Add( textEditor );
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Line #1\nLine #2\nLine #3" );
+  textEditor.SetProperty( DevelTextEditor::Property::LINE_SPACING, 0 );
+
+  Vector3 sizeBefore = textEditor.GetNaturalSize();
+
+  textEditor.SetProperty( DevelTextEditor::Property::LINE_SPACING, 20 );
+
+  //add 20 for each line  20 * 3
+  DALI_TEST_EQUALS(sizeBefore.height + 60.0f, textEditor.GetNaturalSize().height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliTextEditorMinLineSize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorMinLineSize ");
+
+  TextEditor textEditor = TextEditor::New();
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 400.0f, 400.f ) );
+  application.GetScene().Add( textEditor );
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Line #1\nLine #2\nLine #3" );
+  textEditor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 0 );
+
+  Vector3 sizeBefore = textEditor.GetNaturalSize();
+
+  textEditor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 60 );
+
+  DALI_TEST_NOT_EQUALS( sizeBefore, textEditor.GetNaturalSize(), 0.0f, TEST_LOCATION);
+
+  //60 * 3 lines
+  DALI_TEST_EQUALS(180.0f, textEditor.GetNaturalSize().height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorCursorPositionChangedSignal(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorCursorPositionChangedSignal");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  // connect to the selection changed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextEditor::CursorPositionChangedSignal(editor).Connect(&TestCursorPositionChangedCallback);
+  bool cursorPositionChangedSignal = false;
+  editor.ConnectSignal( testTracker, "cursorPositionChanged",   CallbackFunctor(&cursorPositionChangedSignal) );
+
+  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();
+
+  editor.SetKeyInputFocus();
+
+  // Tap on the text editor
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 23, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  // Move to left.
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 18, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  // Insert C
+  application.ProcessEvent( GenerateKey( "c", "", "c", KEY_C_CODE, 0, 0, Integration::KeyEvent::DOWN, "c", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 17, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  //delete one character
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_BACKSPACE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 18, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  editor.SetProperty( TextEditor::Property::TEXT, "Hello" );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 17, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  editor.SetProperty( DevelTextEditor::Property::PRIMARY_CURSOR_POSITION, 3);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  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 d00dc75..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;
@@ -130,6 +134,8 @@ static bool gMaxCharactersCallBackCalled;
 static bool gInputFilteredAcceptedCallbackCalled;
 static bool gInputFilteredRejectedCallbackCalled;
 static bool gInputStyleChangedCallbackCalled;
+static bool gCursorPositionChangedCallbackCalled;
+static uint32_t oldCursorPos;
 static Dali::Toolkit::TextField::InputStyle::Mask gInputStyleMask;
 
 static void LoadBitmapResource(TestPlatformAbstraction& platform, int width, int height)
@@ -212,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");
@@ -224,6 +239,14 @@ static void TestAnchorClickedCallback(TextField control, const char* href, unsig
   }
 }
 
+static void TestCursorPositionChangedCallback( TextField control, unsigned int oldPos )
+{
+  tet_infoline(" TestCursorPositionChangedCallback");
+
+  gCursorPositionChangedCallbackCalled = true;
+  oldCursorPos = oldPos;
+}
+
 static void TestTextChangedCallback( TextField control )
 {
   tet_infoline(" TestTextChangedCallback");
@@ -3976,4 +3999,512 @@ int UtcDaliToolkitTextFieldEllipsisPositionProperty(void)
   DALI_TEST_EQUALS( textField.GetProperty< int >( DevelTextField::Property::ELLIPSIS_POSITION ), static_cast< int >( Toolkit::DevelText::EllipsisPosition::END ), TEST_LOCATION );
 
   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;
+  tet_infoline(" utcDaliTextFieldCursorPositionChangedSignal");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  // connect to the selection changed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextField::CursorPositionChangedSignal(field).Connect(&TestCursorPositionChangedCallback);
+  bool cursorPositionChangedSignal = false;
+  field.ConnectSignal( testTracker, "cursorPositionChanged",   CallbackFunctor(&cursorPositionChangedSignal) );
+
+  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();
+
+  field.SetKeyInputFocus();
+
+  // Tap on the text field
+  TestGenerateTap( application, 3.0f, 25.0f );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 23, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  // Move to left.
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_CURSOR_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 17, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  // Insert D
+  application.ProcessEvent( GenerateKey( "D", "", "D", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "D", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 16, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  //delete one character
+  application.ProcessEvent( GenerateKey( "", "", "", DALI_KEY_BACKSPACE, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 17, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  field.SetProperty( TextField::Property::TEXT, "Hello" );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  DALI_TEST_EQUALS(oldCursorPos, 16, TEST_LOCATION);
+
+  gCursorPositionChangedCallbackCalled = false;
+
+  field.SetProperty(DevelTextField::Property::PRIMARY_CURSOR_POSITION, 3);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gCursorPositionChangedCallbackCalled);
+  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 16518da..4e553e8 100755 (executable)
@@ -77,8 +77,6 @@ static bool gWheelEventHandled = false;
 static int gFormRepostDecidedCallbackCalled = 0;
 static std::unique_ptr<Dali::WebEngineFormRepostDecision> gFormRepostDecidedInstance = nullptr;
 static int gFrameRenderedCallbackCalled = 0;
-static int gRequestInterceptorCallbackCalled = 0;
-static std::unique_ptr<Dali::WebEngineRequestInterceptor> gRequestInterceptorInstance = nullptr;
 static int gConsoleMessageCallbackCalled = 0;
 static std::unique_ptr<Dali::WebEngineConsoleMessage> gConsoleMessageInstance = nullptr;
 static int gResponsePolicyDecidedCallbackCalled = 0;
@@ -94,6 +92,8 @@ static int gStorageUsageAcquiredCallbackCalled = 0;
 static int gFormPasswordsAcquiredCallbackCalled = 0;
 static int gDownloadStartedCallbackCalled = 0;
 static int gMimeOverriddenCallbackCalled = 0;
+static int gRequestInterceptedCallbackCalled = 0;
+static Dali::WebEngineRequestInterceptorPtr gRequestInterceptorInstance = nullptr;
 static std::vector<std::unique_ptr<Dali::WebEngineSecurityOrigin>> gSecurityOriginList;
 static std::vector<std::unique_ptr<Dali::WebEngineContext::PasswordData>> gPasswordDataList;
 static int gContextMenuShownCallbackCalled = 0;
@@ -240,12 +240,6 @@ static void OnFrameRendered()
   gFrameRenderedCallbackCalled++;
 }
 
-static void OnRequestInterceptor(std::unique_ptr<Dali::WebEngineRequestInterceptor> interceptor)
-{
-  gRequestInterceptorCallbackCalled++;
-  gRequestInterceptorInstance = std::move(interceptor);
-}
-
 static void OnConsoleMessage(std::unique_ptr<Dali::WebEngineConsoleMessage> message)
 {
   gConsoleMessageCallbackCalled++;
@@ -300,6 +294,13 @@ static bool OnMimeOverridden(const std::string&, const std::string&, std::string
   return false;
 }
 
+static bool OnRequestIntercepted(Dali::WebEngineRequestInterceptorPtr interceptor)
+{
+  gRequestInterceptedCallbackCalled++;
+  gRequestInterceptorInstance = interceptor;
+  return false;
+}
+
 static void OnContextMenuShown(std::unique_ptr<Dali::WebEngineContextMenu> menu)
 {
   gContextMenuShownCallbackCalled++;
@@ -1231,36 +1232,6 @@ int UtcDaliWebViewVideoPlayingGeolocationPermission(void)
   END_TEST;
 }
 
-int UtcDaliWebViewHttpRequestInterceptor(void)
-{
-  ToolkitTestApplication application;
-
-  WebView view = WebView::New();
-  DALI_TEST_CHECK( view );
-
-  // load url.
-  view.RegisterRequestInterceptorCallback( &OnRequestInterceptor );
-  DALI_TEST_EQUALS( gRequestInterceptorCallbackCalled, 0, TEST_LOCATION );
-  DALI_TEST_CHECK(gRequestInterceptorInstance == 0);
-
-  view.LoadUrl( TEST_URL1 );
-  Test::EmitGlobalTimerSignal();
-  DALI_TEST_EQUALS( gRequestInterceptorCallbackCalled, 1, TEST_LOCATION );
-
-  // check request interceptor.
-  DALI_TEST_CHECK(gRequestInterceptorInstance != 0);
-  DALI_TEST_CHECK(gRequestInterceptorInstance->Ignore());
-  DALI_TEST_CHECK(gRequestInterceptorInstance->SetResponseStatus(400, "error"));
-  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponseHeader("key", "value"));
-  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponseBody("test", 4));
-  std::string testUrl("http://test.html");
-  DALI_TEST_EQUALS(gRequestInterceptorInstance->GetUrl(), testUrl, TEST_LOCATION);
-
-  gRequestInterceptorInstance = nullptr;
-
-  END_TEST;
-}
-
 int UtcDaliWebViewResponsePolicyDecisionRequest(void)
 {
   ToolkitTestApplication application;
@@ -1334,8 +1305,8 @@ int UtcDaliWebViewHitTest(void)
   DALI_TEST_EQUALS(hitTest->GetTagName(), testTagName, TEST_LOCATION);
   std::string testNodeValue("test");
   DALI_TEST_EQUALS(hitTest->GetNodeValue(), testNodeValue, TEST_LOCATION);
-  Dali::Property::Map* testMap = &hitTest->GetAttributes();
-  DALI_TEST_CHECK(testMap);
+  Dali::Property::Map testMap = hitTest->GetAttributes();
+  DALI_TEST_EQUALS(testMap.Count(), 0, TEST_LOCATION);
   std::string testImageFileNameExtension("jpg");
   DALI_TEST_EQUALS(hitTest->GetImageFileNameExtension(), testImageFileNameExtension, TEST_LOCATION);
   Dali::PixelData testImageBuffer = hitTest->GetImageBuffer();
@@ -1668,6 +1639,47 @@ int UtcDaliWebContextGetWebDatabaseStorageOrigins(void)
   END_TEST;
 }
 
+int UtcDaliWebContextHttpRequestInterceptor(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  DALI_TEST_CHECK( view );
+
+  Dali::Toolkit::WebContext* context = view.GetContext();
+  DALI_TEST_CHECK( context != 0 )
+
+  // load url.
+  context->RegisterRequestInterceptedCallback(&OnRequestIntercepted);
+  DALI_TEST_EQUALS(gRequestInterceptedCallbackCalled, 0, TEST_LOCATION);
+  DALI_TEST_CHECK(gRequestInterceptorInstance == 0);
+
+  Test::EmitGlobalTimerSignal();
+  DALI_TEST_EQUALS( gRequestInterceptedCallbackCalled, 1, TEST_LOCATION );
+
+  // check request interceptor.
+  DALI_TEST_CHECK(gRequestInterceptorInstance != 0);
+  DALI_TEST_CHECK(gRequestInterceptorInstance->Ignore());
+  DALI_TEST_CHECK(gRequestInterceptorInstance->SetResponseStatus(400, "error"));
+  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponseHeader("key1", "value1"));
+  Dali::Property::Map testHeaders;
+  testHeaders.Insert("key2", "value2");
+  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponseHeaders(testHeaders));
+  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponseBody("test", 4));
+  DALI_TEST_CHECK(gRequestInterceptorInstance->AddResponse("key:value", "test", 4));
+  DALI_TEST_CHECK(gRequestInterceptorInstance->WriteResponseChunk("test", 4));
+  std::string testUrl("http://test.html");
+  DALI_TEST_EQUALS(gRequestInterceptorInstance->GetUrl(), testUrl, TEST_LOCATION);
+  std::string testMethod("GET");
+  DALI_TEST_EQUALS(gRequestInterceptorInstance->GetMethod(), testMethod, TEST_LOCATION);
+  Dali::Property::Map resultHeaders = gRequestInterceptorInstance->GetHeaders();
+  DALI_TEST_EQUALS(resultHeaders.Count(), 2, TEST_LOCATION);
+
+  gRequestInterceptorInstance = nullptr;
+
+  END_TEST;
+}
+
 // test cases for web cookie manager.
 
 int UtcDaliWebCookieManagerGetSetCookieAcceptPolicy(void)
index 2e263ce..74a57e6 100644 (file)
@@ -40,11 +40,21 @@ AnchorClickedSignalType& AnchorClickedSignal(TextEditor textEditor)
   return GetImpl(textEditor).AnchorClickedSignal();
 }
 
+CursorPositionChangedSignalType& CursorPositionChangedSignal(TextEditor textEditor)
+{
+  return GetImpl(textEditor).CursorPositionChangedSignal();
+}
+
 InputFilteredSignalType& InputFilteredSignal(TextEditor textEditor)
 {
   return GetImpl(textEditor).InputFilteredSignal();
 }
 
+SelectionChangedSignalType& SelectionChangedSignal(TextEditor textEditor)
+{
+  return GetImpl(textEditor).SelectionChangedSignal();
+}
+
 void SelectWholeText(TextEditor textEditor)
 {
   GetImpl(textEditor).SelectWholeText();
@@ -65,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 2d14542..345368d 100644 (file)
@@ -267,6 +267,13 @@ enum Type
   * @see DevelText::EllipsisPosition
   */
   ELLIPSIS_POSITION,
+
+  /**
+   * @brief Sets the height of the line in points.
+   * @details Name "minLineSize", type Property::FLOAT.
+   * @note If the font size is larger than the line size, it works with the font size.
+   */
+  MIN_LINE_SIZE,
 };
 
 } // namespace Property
@@ -318,6 +325,26 @@ using AnchorClickedSignalType = Signal<void(TextEditor, const char*, uint32_t)>;
 DALI_TOOLKIT_API AnchorClickedSignalType& AnchorClickedSignal(TextEditor textEditor);
 
 /**
+ * @brief cursor position changed signal type.
+ *
+ * @note Signal
+ *  - uint32_t    : old position.
+ */
+using CursorPositionChangedSignalType = Signal<void(TextEditor, uint32_t)>;
+
+/**
+ * @brief This signal is emitted when the cursor position has been changed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName(TextEditor textEditor, uint32_t oldPosition);
+ * @endcode
+ * @param[in] textEditor The instance of TextEditor.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API CursorPositionChangedSignalType& CursorPositionChangedSignal(TextEditor textEditor);
+
+/**
  * @brief Input filtered signal type.
  */
 using InputFilteredSignalType = Signal<void(TextEditor, Toolkit::InputFilter::Property::Type)>;
@@ -349,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.
@@ -385,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 a5d0428..e9a609f 100644 (file)
@@ -35,11 +35,21 @@ AnchorClickedSignalType& AnchorClickedSignal(TextField textField)
   return GetImpl(textField).AnchorClickedSignal();
 }
 
+CursorPositionChangedSignalType& CursorPositionChangedSignal(TextField textField)
+{
+  return GetImpl(textField).CursorPositionChangedSignal();
+}
+
 InputFilteredSignalType& InputFilteredSignal(TextField textField)
 {
   return GetImpl(textField).InputFilteredSignal();
 }
 
+SelectionChangedSignalType& SelectionChangedSignal(TextField textField)
+{
+  return GetImpl(textField).SelectionChangedSignal();
+}
+
 void SelectWholeText(TextField textField)
 {
   GetImpl(textField).SelectWholeText();
@@ -55,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 bd9c39a..4dff21d 100644 (file)
@@ -247,6 +247,26 @@ using AnchorClickedSignalType = Signal<void(TextField, const char*, uint32_t)>;
 DALI_TOOLKIT_API AnchorClickedSignalType& AnchorClickedSignal(TextField textField);
 
 /**
+ * @brief cursor position changed signal type.
+ *
+ * @note Signal
+ *  - uint32_t    : old position.
+ */
+using CursorPositionChangedSignalType = Signal<void(TextField, uint32_t)>;
+
+/**
+ * @brief This signal is emitted when the cursor position has been changed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName(TextField textField, uint32_t oldPosition);
+ * @endcode
+ * @param[in] textField The instance of TextField.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API CursorPositionChangedSignalType& CursorPositionChangedSignal(TextField textField);
+
+/**
  * @brief Input filtered signal type.
  */
 using InputFilteredSignalType = Signal<void(TextField, Toolkit::InputFilter::Property::Type)>;
@@ -278,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.
@@ -306,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 12218f7..bba91c6 100755 (executable)
@@ -144,6 +144,11 @@ void WebContext::RegisterMimeOverriddenCallback(Dali::WebEngineContext::WebEngin
   mWebEngineContext.RegisterMimeOverriddenCallback(callback);
 }
 
+void WebContext::RegisterRequestInterceptedCallback(Dali::WebEngineContext::WebEngineRequestInterceptedCallback callback)
+{
+  mWebEngineContext.RegisterRequestInterceptedCallback(callback);
+}
+
 void WebContext::EnableCache(bool cacheEnabled)
 {
   mWebEngineContext.EnableCache(cacheEnabled);
index 56bf2d8..69e723b 100755 (executable)
@@ -229,6 +229,13 @@ public:
   void RegisterMimeOverriddenCallback(Dali::WebEngineContext::WebEngineMimeOverriddenCallback callback);
 
   /**
+   * @brief Callback to be called when http request need be intercepted.
+   *
+   * @param[in] callback
+   */
+  void RegisterRequestInterceptedCallback(Dali::WebEngineContext::WebEngineRequestInterceptedCallback callback);
+
+  /**
    * @brief Toggle the cache to be enabled or disabled
    *
    * @param[in] cacheEnabled enable or disable cache
index 7fff902..8412273 100755 (executable)
@@ -354,11 +354,6 @@ void WebView::RegisterFrameRenderedCallback(Dali::WebEnginePlugin::WebEngineFram
   Dali::Toolkit::GetImpl(*this).RegisterFrameRenderedCallback(callback);
 }
 
-void WebView::RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback)
-{
-  Dali::Toolkit::GetImpl(*this).RegisterRequestInterceptorCallback(callback);
-}
-
 void WebView::RegisterConsoleMessageReceivedCallback(Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback callback)
 {
   Dali::Toolkit::GetImpl(*this).RegisterConsoleMessageReceivedCallback(callback);
index fa8cee3..6b373df 100755 (executable)
@@ -678,13 +678,6 @@ public:
   void RegisterFrameRenderedCallback(Dali::WebEnginePlugin::WebEngineFrameRenderedCallback callback);
 
   /**
-   * @brief Callback to be called when http request need be intercepted.
-   *
-   * @param[in] callback
-   */
-  void RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback);
-
-  /**
    * @brief Callback to be called when console message will be logged.
    *
    * @param[in] callback
index 9609a3b..41d7126 100644 (file)
@@ -828,7 +828,7 @@ void Ellipsis(const RendererParameters& textParameters, TextAbstraction::TextRen
     {
       Length finalNumberOfGlyphs = 0u;
 
-      if((line.ascender - line.descender) > textLayoutArea.height)
+      if((GetLineHeight(line)) > textLayoutArea.height)
       {
         // The height of the line is bigger than the height of the text area.
         // Show the ellipsis glyph even if it doesn't fit in the text area.
@@ -1524,7 +1524,7 @@ Dali::Property::Array RenderForLastIndex(RendererParameters& textParameters)
     const LineRun& line = *(lines.Begin() + index);
     numberOfCharacters += line.characterRun.numberOfCharacters;
 
-    lineOffset = lineSize > 0.f ? lineSize : (line.ascender + -line.descender);
+    lineOffset = lineSize > 0.f ? lineSize : GetLineHeight(line);
     penY += lineOffset;
     if((penY + lineOffset) > boundingBox)
     {
index c04a730..700d76f 100644 (file)
@@ -101,7 +101,7 @@ void CanvasView::OnInitialize()
       new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::IMAGE));
   });
 
-  Adaptor::Get().RegisterProcessor(*this);
+  Adaptor::Get().RegisterProcessor(*this, true);
 }
 
 void CanvasView::OnRelayout(const Vector2& size, RelayoutContainer& container)
index dd67a85..7a3b4ba 100644 (file)
@@ -151,13 +151,15 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "inputMethodSett
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "inputFilter",                          MAP,       INPUT_FILTER                        )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "ellipsis",                             BOOLEAN,   ELLIPSIS                            )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "ellipsisPosition",                     INTEGER,   ELLIPSIS_POSITION                   )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "minLineSize",                          FLOAT,     MIN_LINE_SIZE                       )
 
-DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "textChanged",        SIGNAL_TEXT_CHANGED       )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputStyleChanged",  SIGNAL_INPUT_STYLE_CHANGED)
-DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "maxLengthReached",   SIGNAL_MAX_LENGTH_REACHED )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "anchorClicked",      SIGNAL_ANCHOR_CLICKED     )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputFiltered",      SIGNAL_INPUT_FILTERED     )
-
+DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "textChanged",           SIGNAL_TEXT_CHANGED           )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputStyleChanged",     SIGNAL_INPUT_STYLE_CHANGED    )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "maxLengthReached",      SIGNAL_MAX_LENGTH_REACHED     )
+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
@@ -491,13 +493,8 @@ void TextEditor::SetProperty(BaseObject* object, Property::Index index, const Pr
       }
       case Toolkit::TextEditor::Property::LINE_SPACING:
       {
-        // The line spacing isn't supported by the TextEditor. Since it's supported
-        // by the TextLabel for now it must be ignored. The property is being shadowed
-        // locally so its value isn't affected.
         const float lineSpacing = value.Get<float>();
-        impl.mLineSpacing       = lineSpacing;
-        // set it to 0.0 due to missing implementation
-        impl.mController->SetDefaultLineSpacing(0.0f);
+        impl.mController->SetDefaultLineSpacing(lineSpacing);
         impl.mRenderer.Reset();
         break;
       }
@@ -832,6 +829,15 @@ void TextEditor::SetProperty(BaseObject* object, Property::Index index, const Pr
         }
         break;
       }
+      case Toolkit::DevelTextEditor::Property::MIN_LINE_SIZE:
+      {
+        const float minLineSize = value.Get<float>();
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextEditor %p MIN_LINE_SIZE %f\n", impl.mController.Get(), minLineSize);
+
+        impl.mController->SetDefaultLineSize(minLineSize);
+        impl.mRenderer.Reset();
+        break;
+      }
     } // switch
   }   // texteditor
 }
@@ -1011,9 +1017,7 @@ Property::Value TextEditor::GetProperty(BaseObject* object, Property::Index inde
       }
       case Toolkit::TextEditor::Property::LINE_SPACING:
       {
-        // LINE_SPACING isn't implemented for the TextEditor. Returning
-        // only shadowed value, not the real one.
-        value = impl.mLineSpacing;
+        value = impl.mController->GetDefaultLineSpacing();
         break;
       }
       case Toolkit::TextEditor::Property::INPUT_LINE_SPACING:
@@ -1222,6 +1226,11 @@ Property::Value TextEditor::GetProperty(BaseObject* object, Property::Index inde
         value = impl.mController->GetEllipsisPosition();
         break;
       }
+      case Toolkit::DevelTextEditor::Property::MIN_LINE_SIZE:
+      {
+        value = impl.mController->GetDefaultLineSize();
+        break;
+      }
     } //switch
   }
 
@@ -1254,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())
@@ -1305,11 +1343,21 @@ DevelTextEditor::AnchorClickedSignalType& TextEditor::AnchorClickedSignal()
   return mAnchorClickedSignal;
 }
 
+DevelTextEditor::CursorPositionChangedSignalType& TextEditor::CursorPositionChangedSignal()
+{
+  return mCursorPositionChangedSignal;
+}
+
 DevelTextEditor::InputFilteredSignalType& TextEditor::InputFilteredSignal()
 {
   return mInputFilteredSignal;
 }
 
+DevelTextEditor::SelectionChangedSignalType& TextEditor::SelectionChangedSignal()
+{
+  return mSelectionChangedSignal;
+}
+
 Text::ControllerPtr TextEditor::GetTextController()
 {
   return mController;
@@ -1346,6 +1394,14 @@ bool TextEditor::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface*
       editorImpl.AnchorClickedSignal().Connect(tracker, functor);
     }
   }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_CURSOR_POSITION_CHANGED))
+  {
+    if(editor)
+    {
+      Internal::TextEditor& editorImpl(GetImpl(editor));
+      editorImpl.CursorPositionChangedSignal().Connect(tracker, functor);
+    }
+  }
   else if(0 == strcmp(signalName.c_str(), SIGNAL_INPUT_FILTERED))
   {
     if(editor)
@@ -1354,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
@@ -1586,6 +1650,16 @@ void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
     RenderText(updateTextType);
   }
 
+  if(mCursorPositionChanged)
+  {
+    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.
@@ -1846,11 +1920,17 @@ void TextEditor::TextDeleted(unsigned int position, unsigned int length, const s
   }
 }
 
-void TextEditor::CursorMoved(unsigned int position)
+void TextEditor::CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition)
 {
   if(Accessibility::IsUp())
   {
-    Control::Impl::GetAccessibilityObject(Self())->EmitTextCursorMoved(position);
+    Control::Impl::GetAccessibilityObject(Self())->EmitTextCursorMoved(newPosition);
+  }
+
+  if((oldPosition != newPosition) && !mCursorPositionChanged)
+  {
+    mCursorPositionChanged = true;
+    mOldPosition           = oldPosition;
   }
 }
 
@@ -1939,12 +2019,44 @@ void TextEditor::AnchorClicked(const std::string& href)
   mAnchorClickedSignal.Emit(handle, href.c_str(), href.length());
 }
 
+void TextEditor::EmitCursorPositionChangedSignal()
+{
+  Dali::Toolkit::TextEditor handle(GetOwner());
+  mCursorPositionChanged = false;
+  mCursorPositionChangedSignal.Emit(handle, mOldPosition);
+}
+
 void TextEditor::InputFiltered(Toolkit::InputFilter::Property::Type type)
 {
   Dali::Toolkit::TextEditor handle(GetOwner());
   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)
@@ -2222,7 +2334,9 @@ TextEditor::TextEditor()
   mScrollAnimationEnabled(false),
   mScrollBarEnabled(false),
   mScrollStarted(false),
-  mTextChanged(false)
+  mTextChanged(false),
+  mCursorPositionChanged(false),
+  mSelectionChanged(false)
 {
 }
 
@@ -2291,10 +2405,10 @@ bool TextEditor::AccessibleImpl::SetCursorOffset(size_t offset)
   return true;
 }
 
-Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset( size_t offset, Dali::Accessibility::TextBoundary boundary)
+Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset(size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self = Toolkit::TextEditor::DownCast(Self());
-  auto text = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
+  auto self     = Toolkit::TextEditor::DownCast(Self());
+  auto text     = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
   auto textSize = text.size();
 
   auto range = Dali::Accessibility::Range{};
@@ -2315,7 +2429,7 @@ Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset( size_t o
     case Dali::Accessibility::TextBoundary::LINE:
     {
       auto textString = text.c_str();
-      auto breaks = std::vector<char>(textSize, 0);
+      auto breaks     = std::vector<char>(textSize, 0);
 
       if(boundary == Dali::Accessibility::TextBoundary::WORD)
       {
@@ -2390,8 +2504,8 @@ Dali::Accessibility::Range TextEditor::AccessibleImpl::GetRangeOfSelection(size_
     return {};
   }
 
-  auto self  = Toolkit::TextEditor::DownCast(Self());
-  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  auto        self       = Toolkit::TextEditor::DownCast(Self());
+  auto        controller = Dali::Toolkit::GetImpl(self).GetTextController();
   std::string value{};
   controller->RetrieveSelection(value);
   auto indices = controller->GetSelectionIndexes();
@@ -2474,7 +2588,7 @@ Dali::Accessibility::States TextEditor::AccessibleImpl::CalculateStates()
 {
   using namespace Dali::Accessibility;
 
-  auto states = DevelControl::AccessibleImpl::CalculateStates();
+  auto states              = DevelControl::AccessibleImpl::CalculateStates();
   states[State::EDITABLE]  = true;
   states[State::FOCUSABLE] = true;
 
@@ -2489,7 +2603,7 @@ Dali::Accessibility::States TextEditor::AccessibleImpl::CalculateStates()
 
 bool TextEditor::AccessibleImpl::InsertText(size_t startPosition, std::string text)
 {
-  auto self = Toolkit::TextEditor::DownCast(Self());
+  auto self         = Toolkit::TextEditor::DownCast(Self());
   auto insertedText = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
 
   insertedText.insert(startPosition, text);
index 7ba1190..aff671a 100644 (file)
@@ -93,11 +93,21 @@ public:
   DevelTextEditor::AnchorClickedSignalType& AnchorClickedSignal();
 
   /**
+   * @copydoc Dali::Toollkit::TextEditor::CursorPositionChangedSignal()
+   */
+  DevelTextEditor::CursorPositionChangedSignalType& CursorPositionChangedSignal();
+
+  /**
    * @copydoc Dali::Toollkit::TextEditor::InputFilteredSignal()
    */
   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.
@@ -216,9 +226,9 @@ private: // From Control
   void TextDeleted(unsigned int position, unsigned int length, const std::string& content) override;
 
   /**
-   * @copydoc Text::EditableControlInterface::CursorMoved()
+   * @copydoc Text::EditableControlInterface::CursorPositionChanged()
    */
-  void CursorMoved(unsigned int position) override;
+  void CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition) override;
 
   /**
    * @copydoc Text::EditableControlInterface::TextChanged()
@@ -236,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;
@@ -311,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
 
   /**
@@ -368,11 +398,21 @@ private: // Implementation
   void OnIdleSignal();
 
   /**
+   * @brief Emits CursorPositionChanged signal.
+   */
+  void EmitCursorPositionChangedSignal();
+
+  /**
    * @brief Emits TextChanged signal.
    */
   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
@@ -425,12 +465,14 @@ private: // Implementation
 
 private: // Data
   // Signals
-  Toolkit::TextEditor::TextChangedSignalType           mTextChangedSignal;
-  Toolkit::TextEditor::InputStyleChangedSignalType     mInputStyleChangedSignal;
-  Toolkit::TextEditor::ScrollStateChangedSignalType    mScrollStateChangedSignal;
-  Toolkit::DevelTextEditor::MaxLengthReachedSignalType mMaxLengthReachedSignal;
-  Toolkit::DevelTextEditor::AnchorClickedSignalType    mAnchorClickedSignal;
-  Toolkit::DevelTextEditor::InputFilteredSignalType    mInputFilteredSignal;
+  Toolkit::TextEditor::TextChangedSignalType                mTextChangedSignal;
+  Toolkit::TextEditor::InputStyleChangedSignalType          mInputStyleChangedSignal;
+  Toolkit::TextEditor::ScrollStateChangedSignalType         mScrollStateChangedSignal;
+  Toolkit::DevelTextEditor::MaxLengthReachedSignalType      mMaxLengthReachedSignal;
+  Toolkit::DevelTextEditor::AnchorClickedSignalType         mAnchorClickedSignal;
+  Toolkit::DevelTextEditor::InputFilteredSignalType         mInputFilteredSignal;
+  Toolkit::DevelTextEditor::CursorPositionChangedSignalType mCursorPositionChangedSignal;
+  Toolkit::DevelTextEditor::SelectionChangedSignalType      mSelectionChangedSignal;
 
   InputMethodContext            mInputMethodContext;
   Text::ControllerPtr           mController;
@@ -457,7 +499,16 @@ private: // Data
   bool  mScrollAnimationEnabled : 1;
   bool  mScrollBarEnabled : 1;
   bool  mScrollStarted : 1;
-  bool  mTextChanged : 1; ///< If true, emits TextChangedSignal in next OnRelayout().
+  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 d2bfc5c..c6fd205 100644 (file)
@@ -140,11 +140,13 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextField, "grabHandleColor"
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextField, "inputFilter",                      MAP,       INPUT_FILTER                        )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextField, "ellipsisPosition",                 INTEGER,   ELLIPSIS_POSITION                   )
 
-DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "textChanged",       SIGNAL_TEXT_CHANGED       )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "maxLengthReached",  SIGNAL_MAX_LENGTH_REACHED )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "inputStyleChanged", SIGNAL_INPUT_STYLE_CHANGED)
-DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "anchorClicked",     SIGNAL_ANCHOR_CLICKED     )
-DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "inputFiltered",     SIGNAL_INPUT_FILTERED     )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "textChanged",           SIGNAL_TEXT_CHANGED           )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "maxLengthReached",      SIGNAL_MAX_LENGTH_REACHED     )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "inputStyleChanged",     SIGNAL_INPUT_STYLE_CHANGED    )
+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
@@ -1214,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;
@@ -1246,6 +1277,14 @@ bool TextField::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface*
       fieldImpl.AnchorClickedSignal().Connect(tracker, functor);
     }
   }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_CURSOR_POSITION_CHANGED))
+  {
+    if(field)
+    {
+      Internal::TextField& fieldImpl(GetImpl(field));
+      fieldImpl.CursorPositionChangedSignal().Connect(tracker, functor);
+    }
+  }
   else if(0 == strcmp(signalName.c_str(), SIGNAL_INPUT_FILTERED))
   {
     if(field)
@@ -1254,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
@@ -1283,11 +1330,21 @@ DevelTextField::AnchorClickedSignalType& TextField::AnchorClickedSignal()
   return mAnchorClickedSignal;
 }
 
+DevelTextField::CursorPositionChangedSignalType& TextField::CursorPositionChangedSignal()
+{
+  return mCursorPositionChangedSignal;
+}
+
 DevelTextField::InputFilteredSignalType& TextField::InputFilteredSignal()
 {
   return mInputFilteredSignal;
 }
 
+DevelTextField::SelectionChangedSignalType& TextField::SelectionChangedSignal()
+{
+  return mSelectionChangedSignal;
+}
+
 void TextField::OnInitialize()
 {
   Actor self = Self();
@@ -1484,6 +1541,16 @@ void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
     RenderText(updateTextType);
   }
 
+  if(mCursorPositionChanged)
+  {
+    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.
@@ -1765,11 +1832,17 @@ void TextField::TextDeleted(unsigned int position, unsigned int length, const st
   }
 }
 
-void TextField::CursorMoved(unsigned int position)
+void TextField::CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition)
 {
   if(Accessibility::IsUp())
   {
-    Control::Impl::GetAccessibilityObject(Self())->EmitTextCursorMoved(position);
+    Control::Impl::GetAccessibilityObject(Self())->EmitTextCursorMoved(newPosition);
+  }
+
+  if((oldPosition != newPosition) && !mCursorPositionChanged)
+  {
+    mCursorPositionChanged = true;
+    mOldPosition           = oldPosition;
   }
 }
 
@@ -1854,12 +1927,44 @@ void TextField::AnchorClicked(const std::string& href)
   mAnchorClickedSignal.Emit(handle, href.c_str(), href.length());
 }
 
+void TextField::EmitCursorPositionChangedSignal()
+{
+  Dali::Toolkit::TextField handle(GetOwner());
+  mCursorPositionChangedSignal.Emit(handle, mOldPosition);
+  mCursorPositionChanged = false;
+}
+
 void TextField::InputFiltered(Toolkit::InputFilter::Property::Type type)
 {
   Dali::Toolkit::TextField handle(GetOwner());
   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)
@@ -1996,7 +2101,9 @@ TextField::TextField()
   mRenderingBackend(DEFAULT_RENDERING_BACKEND),
   mExceedPolicy(Dali::Toolkit::TextField::EXCEED_POLICY_CLIP),
   mHasBeenStaged(false),
-  mTextChanged(false)
+  mTextChanged(false),
+  mCursorPositionChanged(false),
+  mSelectionChanged(false)
 {
 }
 
@@ -2067,8 +2174,8 @@ bool TextField::AccessibleImpl::SetCursorOffset(size_t offset)
 Dali::Accessibility::Range TextField::AccessibleImpl::GetTextAtOffset(
   size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self = Toolkit::TextField::DownCast(Self());
-  auto text = self.GetProperty(Toolkit::TextField::Property::TEXT).Get<std::string>();
+  auto self     = Toolkit::TextField::DownCast(Self());
+  auto text     = self.GetProperty(Toolkit::TextField::Property::TEXT).Get<std::string>();
   auto textSize = text.size();
 
   auto range = Dali::Accessibility::Range{};
@@ -2089,7 +2196,7 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetTextAtOffset(
     case Dali::Accessibility::TextBoundary::LINE:
     {
       auto textString = text.c_str();
-      auto breaks = std::vector<char>(textSize, 0);
+      auto breaks     = std::vector<char>(textSize, 0);
 
       if(boundary == Dali::Accessibility::TextBoundary::WORD)
       {
@@ -2164,8 +2271,8 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetRangeOfSelection(size_t
     return {};
   }
 
-  auto self  = Toolkit::TextField::DownCast(Self());
-  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  auto        self       = Toolkit::TextField::DownCast(Self());
+  auto        controller = Dali::Toolkit::GetImpl(self).GetTextController();
   std::string value{};
   controller->RetrieveSelection(value);
   auto indices = controller->GetSelectionIndexes();
@@ -2264,7 +2371,7 @@ Dali::Accessibility::States TextField::AccessibleImpl::CalculateStates()
 
 bool TextField::AccessibleImpl::InsertText(size_t startPosition, std::string text)
 {
-  auto self = Toolkit::TextField::DownCast(Self());
+  auto self         = Toolkit::TextField::DownCast(Self());
   auto insertedText = self.GetProperty(Toolkit::TextField::Property::TEXT).Get<std::string>();
 
   insertedText.insert(startPosition, text);
index b1f5991..0b24ff0 100644 (file)
@@ -117,10 +117,20 @@ public:
   DevelTextField::AnchorClickedSignalType& AnchorClickedSignal();
 
   /**
+   * @copydoc TextField::CursorPositionChangedSignal()
+   */
+  DevelTextField::CursorPositionChangedSignalType& CursorPositionChangedSignal();
+
+  /**
    * @copydoc TextField::InputFilteredSignal()
    */
   DevelTextField::InputFilteredSignalType& InputFilteredSignal();
 
+  /**
+   * @copydoc TextField::SelectionChangedSignal()
+   */
+  DevelTextField::SelectionChangedSignalType& SelectionChangedSignal();
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
@@ -207,9 +217,9 @@ private: // From Control
   void TextDeleted(unsigned int position, unsigned int length, const std::string& content) override;
 
   /**
-   * @copydoc Text::EditableControlInterface::CursorMoved()
+   * @copydoc Text::EditableControlInterface::CursorPositionChanged()
    */
-  void CursorMoved(unsigned int position) override;
+  void CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition) override;
 
   /**
    * @copydoc Text::EditableControlInterface::TextChanged()
@@ -227,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;
@@ -283,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
 
   /**
@@ -343,6 +373,16 @@ private: // Implementation
   void EmitTextChangedSignal();
 
   /**
+   * @brief Emits CursorPositionChanged signal.
+   */
+  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.
@@ -381,11 +421,13 @@ private: // Implementation
 
 private: // Data
   // Signals
-  Toolkit::TextField::TextChangedSignalType        mTextChangedSignal;
-  Toolkit::TextField::MaxLengthReachedSignalType   mMaxLengthReachedSignal;
-  Toolkit::TextField::InputStyleChangedSignalType  mInputStyleChangedSignal;
-  Toolkit::DevelTextField::AnchorClickedSignalType mAnchorClickedSignal;
-  Toolkit::DevelTextField::InputFilteredSignalType mInputFilteredSignal;
+  Toolkit::TextField::TextChangedSignalType                mTextChangedSignal;
+  Toolkit::TextField::MaxLengthReachedSignalType           mMaxLengthReachedSignal;
+  Toolkit::TextField::InputStyleChangedSignalType          mInputStyleChangedSignal;
+  Toolkit::DevelTextField::AnchorClickedSignalType         mAnchorClickedSignal;
+  Toolkit::DevelTextField::InputFilteredSignalType         mInputFilteredSignal;
+  Toolkit::DevelTextField::CursorPositionChangedSignalType mCursorPositionChangedSignal;
+  Toolkit::DevelTextField::SelectionChangedSignalType      mSelectionChangedSignal;
 
   InputMethodContext       mInputMethodContext;
   Text::ControllerPtr      mController;
@@ -404,7 +446,16 @@ private: // Data
   int   mRenderingBackend;
   int   mExceedPolicy;
   bool  mHasBeenStaged : 1;
-  bool  mTextChanged : 1; ///< If true, emits TextChangedSignal in next OnRelayout().
+  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:
   /**
old mode 100644 (file)
new mode 100755 (executable)
index e336f2c..77b29ca
@@ -31,7 +31,6 @@
 #include <dali/devel-api/adaptor-framework/web-engine-http-auth-handler.h>
 #include <dali/devel-api/adaptor-framework/web-engine-load-error.h>
 #include <dali/devel-api/adaptor-framework/web-engine-policy-decision.h>
-#include <dali/devel-api/adaptor-framework/web-engine-request-interceptor.h>
 #include <dali/devel-api/adaptor-framework/web-engine-settings.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/scripting/enum-helper.h>
@@ -702,14 +701,6 @@ void WebView::RegisterFrameRenderedCallback(Dali::WebEnginePlugin::WebEngineFram
   mFrameRenderedCallback = callback;
 }
 
-void WebView::RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback)
-{
-  if(mWebEngine)
-  {
-    mWebEngine.RegisterRequestInterceptorCallback(callback);
-  }
-}
-
 void WebView::RegisterConsoleMessageReceivedCallback(Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback callback)
 {
   if(mWebEngine)
index 15f3ce3..ca1ec27 100755 (executable)
@@ -353,11 +353,6 @@ public:
   void RegisterFrameRenderedCallback(Dali::WebEnginePlugin::WebEngineFrameRenderedCallback callback);
 
   /**
-   * @copydoc Dali::Toolkit::WebView::RegisterRequestInterceptorCallback()
-   */
-  void RegisterRequestInterceptorCallback(Dali::WebEnginePlugin::WebEngineRequestInterceptorCallback callback);
-
-  /**
    * @copydoc Dali::Toolkit::WebView::RegisterConsoleMessageReceivedCallback()
    */
   void RegisterConsoleMessageReceivedCallback(Dali::WebEnginePlugin::WebEngineConsoleMessageReceivedCallback callback);
index 1df142c..c2d35d9 100644 (file)
@@ -155,9 +155,7 @@ LineIndex GetClosestLine(VisualModelPtr visualModel,
   {
     const LineRun& lineRun = *it;
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    totalHeight += lineRun.ascender - lineRun.descender;
+    totalHeight += GetLineHeight(lineRun);
 
     if(visualY < totalHeight)
     {
@@ -186,9 +184,7 @@ float CalculateLineOffset(const Vector<LineRun>& lines,
   {
     const LineRun& lineRun = *it;
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    offset += lineRun.ascender - lineRun.descender;
+    offset += GetLineHeight(lineRun);
   }
 
   return offset;
@@ -495,9 +491,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 newLineIndex);
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    cursorInfo.lineHeight = newLine.ascender - newLine.descender;
+    cursorInfo.lineHeight = GetLineHeight(newLine);
 
     // Set the primary cursor's height.
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
@@ -543,9 +537,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 lineIndex);
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    cursorInfo.lineHeight = line.ascender - line.descender;
+    cursorInfo.lineHeight = GetLineHeight(line);
 
     // Calculate the primary cursor.
 
index b27f634..695d950 100644 (file)
@@ -36,6 +36,12 @@ namespace Toolkit
 {
 namespace Text
 {
+float GetLineHeight(const LineRun lineRun)
+{
+  // The line height is the addition of the line ascender, the line descender and the line spacing.
+  // However, the line descender has a negative value, hence the subtraction.
+  return lineRun.ascender - lineRun.descender + lineRun.lineSpacing;
+}
 namespace Layout
 {
 namespace
@@ -1275,7 +1281,7 @@ struct Engine::Impl
       layoutSize.width = layoutParameters.boundingBox.width;
       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
       {
-        layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
+        layoutSize.height += GetLineHeight(*lineRun);
       }
 
       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
@@ -1382,7 +1388,7 @@ struct Engine::Impl
       layoutSize.width = lineRun.width;
     }
 
-    layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
+    layoutSize.height += GetLineHeight(lineRun);
   }
 
   /**
@@ -1433,7 +1439,7 @@ struct Engine::Impl
 
     lineRun.lineSpacing += mDefaultLineSpacing;
 
-    layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
+    layoutSize.height += GetLineHeight(lineRun);
   }
 
   /**
@@ -1457,7 +1463,7 @@ struct Engine::Impl
         layoutSize.width = line.width;
       }
 
-      layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
+      layoutSize.height += GetLineHeight(line);
     }
   }
 
index ff826e6..52c47a1 100644 (file)
@@ -48,6 +48,13 @@ struct LineRun
   CharacterRun       characterRunForSecondHalfLine; ///< The initial character index and the number of characters of the run for the second half of line.
 };
 
+/**
+ * @brief Get the line height for the specified line run.
+ *
+ * @param[in] lineRun The line runs to get the height for.
+ */
+float GetLineHeight(const LineRun lineRun);
+
 } // namespace Text
 
 } // namespace Toolkit
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 2aafff7..e24e8aa 100644 (file)
@@ -54,6 +54,8 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
     return false;
   }
 
+  unsigned int oldPos = eventData->mPrimaryCursorPosition;
+
   if(eventData->mDecorator)
   {
     for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
@@ -124,12 +126,14 @@ bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
   {
     // Updates the cursor position and scrolls the text to make it visible.
     CursorInfo cursorInfo;
+
     // Calculate the cursor position from the new cursor index.
     impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
 
-    if(nullptr != impl.mEditableControlInterface)
+    //only emit the event if the cursor is moved in current function.
+    if(nullptr != impl.mEditableControlInterface && eventData->mEventQueue.size() > 0)
     {
-      impl.mEditableControlInterface->CursorMoved(eventData->mPrimaryCursorPosition);
+      impl.mEditableControlInterface->CursorPositionChanged(oldPos, eventData->mPrimaryCursorPosition);
     }
 
     if(eventData->mUpdateCursorHookPosition)
@@ -338,7 +342,7 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
     const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
 
     // Get the next hit 'y' point.
-    const float hitPointY = cursorInfo.lineOffset - 0.5f * (line.ascender - line.descender);
+    const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
 
     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
     bool matchedCharacter = false;
@@ -374,7 +378,7 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
 
       // Get the next hit 'y' point.
-      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * (line.ascender - line.descender);
+      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
       bool matchedCharacter = false;
@@ -406,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;
     }
@@ -673,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);
+      }
     }
   }
 }
@@ -689,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);
+      }
     }
   }
 }
@@ -712,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);
+      }
     }
   }
 }
@@ -741,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)
   {
@@ -800,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)
@@ -825,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)
   {
@@ -881,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;
 }
 
@@ -995,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)
     {
@@ -1025,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 3fe3322..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);
+    }
   }
 }
 
@@ -1260,15 +1267,31 @@ bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focus
   }
 
   uint32_t length                    = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+  uint32_t oldCursorPos              = mEventData->mPrimaryCursorPosition;
   mEventData->mPrimaryCursorPosition = std::min(index, length);
   // 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();
   }
+
+  if(nullptr != mEditableControlInterface)
+  {
+    mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
+  }
+
   return true;
 }
 
@@ -1377,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 d946365..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);
     }
   }
@@ -139,6 +144,8 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
     controller.ShowPlaceholderText();
   }
 
+  unsigned int oldCursorPos = (nullptr != eventData ? eventData->mPrimaryCursorPosition : 0);
+
   // Resets the cursor position.
   controller.ResetCursorPosition(lastCursorIndex);
 
@@ -156,6 +163,7 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
   // Do this last since it provides callbacks into application code.
   if(NULL != impl.mEditableControlInterface)
   {
+    impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, lastCursorIndex);
     impl.mEditableControlInterface->TextChanged(true);
   }
 }
@@ -172,9 +180,10 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri
     return;
   }
 
-  bool removedPrevious  = false;
-  bool removedSelected  = false;
-  bool maxLengthReached = false;
+  bool         removedPrevious  = false;
+  bool         removedSelected  = false;
+  bool         maxLengthReached = false;
+  unsigned int oldCursorPos     = eventData->mPrimaryCursorPosition;
 
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n", &controller, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"), eventData->mPrimaryCursorPosition, eventData->mPreEditFlag, eventData->mPreEditStartPosition, eventData->mPreEditLength);
 
@@ -418,6 +427,11 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri
     }
   }
 
+  if(nullptr != impl.mEditableControlInterface)
+  {
+    impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, eventData->mPrimaryCursorPosition);
+  }
+
   if(maxLengthReached)
   {
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", logicalModel->mText.Count());
@@ -561,6 +575,11 @@ bool Controller::TextUpdater::RemoveText(
         RemoveTextAnchor(controller, cursorOffset, numberOfCharacters, previousCursorIndex);
       }
 
+      if(nullptr != impl.mEditableControlInterface)
+      {
+        impl.mEditableControlInterface->CursorPositionChanged(previousCursorIndex, cursorIndex);
+      }
+
       // Cursor position retreat
       previousCursorIndex = cursorIndex;
 
index dce67ff..30c6e04 100644 (file)
@@ -1219,12 +1219,31 @@ const std::string& Controller::GetDefaultOutlineProperties() const
   return EMPTY_STRING;
 }
 
+void Controller::RelayoutForNewLineSize()
+{
+  // relayout all characters
+  mImpl->mTextUpdateInfo.mCharacterIndex             = 0;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd    = mImpl->mModel->mLogicalModel->mText.Count();
+  mImpl->mOperationsPending                          = static_cast<OperationsMask>(mImpl->mOperationsPending | LAYOUT);
+
+  //remove selection
+  if((mImpl->mEventData != nullptr) && (mImpl->mEventData->mState == EventData::SELECTING))
+  {
+    mImpl->ChangeState(EventData::EDITING);
+  }
+
+  mImpl->RequestRelayout();
+}
+
 bool Controller::SetDefaultLineSpacing(float lineSpacing)
 {
   if(std::fabs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
   {
     mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
     mImpl->mRecalculateNaturalSize = true;
+
+    RelayoutForNewLineSize();
     return true;
   }
   return false;
@@ -1241,6 +1260,8 @@ bool Controller::SetDefaultLineSize(float lineSize)
   {
     mImpl->mLayoutEngine.SetDefaultLineSize(lineSize);
     mImpl->mRecalculateNaturalSize = true;
+
+    RelayoutForNewLineSize();
     return true;
   }
   return false;
@@ -1892,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 8c388b9..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);
 
@@ -1874,6 +1888,11 @@ private: // Helpers.
    */
   void ResetScrollPosition();
 
+  /**
+   * @brief fill needed relayout parameters when line size is changed & request relayout.
+   */
+  void RelayoutForNewLineSize();
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
index d62101e..12d4256 100644 (file)
@@ -56,7 +56,7 @@ public:
   /**
    * @brief Called to signal that caret (cursor position) has been moved.
    */
-  virtual void CursorMoved(unsigned int position) = 0;
+  virtual void CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition) = 0;
 
   /**
    * @brief Called to signal that text has been inserted or deleted.
@@ -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 0601750..cf63deb 100644 (file)
@@ -144,9 +144,7 @@ void SelectionHandleController::Reposition(Controller::Impl& impl)
 
   lineRun += firstLineIndex;
 
-  // The line height is the addition of the line ascender and the line descender.
-  // However, the line descender has a negative value, hence the subtraction.
-  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+  selectionBoxInfo->lineHeight = GetLineHeight(*lineRun);
 
   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
 
@@ -275,9 +273,7 @@ void SelectionHandleController::Reposition(Controller::Impl& impl)
         // Update the line's vertical offset.
         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
 
-        // The line height is the addition of the line ascender and the line descender.
-        // However, the line descender has a negative value, hence the subtraction.
-        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+        selectionBoxInfo->lineHeight = GetLineHeight(*lineRun);
       }
     }
   }
@@ -495,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;
@@ -514,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)
   {
index f8a2722..a5a6b3b 100644 (file)
@@ -225,7 +225,7 @@ Length View::GetGlyphs(GlyphInfo* glyphs,
 
               lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
 
-              penY += line->ascender;
+              penY += line->ascender + line->lineSpacing;
             }
           }
         }
@@ -265,7 +265,7 @@ Length View::GetGlyphs(GlyphInfo* glyphs,
           const LineRun& elidedLine = *ellipsisLine;
 
           if((1u == numberOfLines) &&
-             (elidedLine.ascender - elidedLine.descender > mImpl->mVisualModel->mControlSize.height))
+             (GetLineHeight(elidedLine) > mImpl->mVisualModel->mControlSize.height))
           {
             // Replace the first glyph with ellipsis glyph
             auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u;
index 3966d0c..2f29ac0 100644 (file)
@@ -538,7 +538,7 @@ VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool
   {
     uint32_t droppedFrames = 0;
 
-    while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)))
+    while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)) && droppedFrames < mTotalFrame)
     {
       droppedFrames++;
       mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
index 2f6f7a9..1ee229f 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 40;
+const unsigned int TOOLKIT_MICRO_VERSION = 41;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index de44474..3fd537a 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.40
+Version:    2.0.41
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT