Add ClearFocusOnEscape to config section.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-editor-impl.cpp
index b6d9900..77005c0 100644 (file)
@@ -24,7 +24,7 @@
 #include <dali/public-api/adaptor-framework/key.h>
 #include <dali/public-api/common/stage.h>
 #include <dali/public-api/images/resource-image.h>
-#include <dali/devel-api/adaptor-framework/virtual-keyboard.h>
+#include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/object/property-helper-devel.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/integration-api/adaptors/adaptor.h>
@@ -33,6 +33,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/public-api/text/rendering-backend.h>
 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
@@ -75,6 +76,19 @@ const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
 };
 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE ) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
 
+
+const Scripting::StringEnum LINE_WRAP_MODE_STRING_TABLE[] =
+{
+  { "WORD",      Toolkit::Text::Layout::LineWrap::WORD      },
+  { "CHARACTER", Toolkit::Text::Layout::LineWrap::CHARACTER }
+};
+const unsigned int LINE_WRAP_MODE_STRING_TABLE_COUNT = sizeof( LINE_WRAP_MODE_STRING_TABLE ) / sizeof( LINE_WRAP_MODE_STRING_TABLE[0] );
+
+const char* const SCROLL_BAR_POSITION("sourcePosition");
+const char* const SCROLL_BAR_POSITION_MIN("sourcePositionMin");
+const char* const SCROLL_BAR_POSITION_MAX("sourcePositionMax");
+const char* const SCROLL_BAR_CONTENT_SIZE("sourceContentSize");
+
 // Type registration
 BaseHandle Create()
 {
@@ -126,6 +140,16 @@ DALI_PROPERTY_REGISTRATION( Toolkit, TextEditor, "outline",
 DALI_PROPERTY_REGISTRATION( Toolkit, TextEditor, "inputOutline",                         MAP,       INPUT_OUTLINE                        )
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "smoothScroll",                   BOOLEAN,   SMOOTH_SCROLL                        )
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "smoothScrollDuration",           FLOAT,     SMOOTH_SCROLL_DURATION               )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "enableScrollBar",                BOOLEAN,   ENABLE_SCROLL_BAR                    )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "scrollBarShowDuration",          FLOAT,     SCROLL_BAR_SHOW_DURATION             )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "scrollBarFadeDuration",          FLOAT,     SCROLL_BAR_FADE_DURATION             )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "pixelSize",                      FLOAT,     PIXEL_SIZE                           )
+DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextEditor, "lineCount",            INTEGER,   LINE_COUNT                           )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "placeholderText",                STRING,    PLACEHOLDER_TEXT                     )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "placeholderTextColor",           VECTOR4,   PLACEHOLDER_TEXT_COLOR               )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "enableSelection",                BOOLEAN,   ENABLE_SELECTION                     )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "placeholder",                    MAP,       PLACEHOLDER                          )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "lineWrapMode",                   STRING,    LINE_WRAP_MODE                       )
 
 DALI_SIGNAL_REGISTRATION( Toolkit, TextEditor, "textChanged",        SIGNAL_TEXT_CHANGED )
 DALI_SIGNAL_REGISTRATION( Toolkit, TextEditor, "inputStyleChanged",  SIGNAL_INPUT_STYLE_CHANGED )
@@ -224,9 +248,9 @@ void TextEditor::SetProperty( BaseObject* object, Property::Index index, const P
           const float pointSize = value.Get< float >();
           DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p POINT_SIZE %f\n", impl.mController.Get(), pointSize );
 
-          if( !Equals( impl.mController->GetDefaultPointSize(), pointSize ) )
+          if( !Equals( impl.mController->GetDefaultFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
           {
-            impl.mController->SetDefaultPointSize( pointSize );
+            impl.mController->SetDefaultFontSize( pointSize, Text::Controller::POINT_SIZE );
           }
         }
         break;
@@ -614,6 +638,104 @@ void TextEditor::SetProperty( BaseObject* object, Property::Index index, const P
         }
         break;
       }
+      case Toolkit::DevelTextEditor::Property::ENABLE_SCROLL_BAR:
+      {
+        const bool enable = value.Get< bool >();
+        DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor SHOW_SCROLL_BAR %d\n", enable );
+
+        impl.mScrollBarEnabled = enable;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SCROLL_BAR_SHOW_DURATION:
+      {
+        const float duration = value.Get< float >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor SCROLL_BAR_SHOW_DURATION %f\n", duration );
+
+        impl.mAnimationPeriod.delaySeconds = duration;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SCROLL_BAR_FADE_DURATION:
+      {
+        const float duration = value.Get< float >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor SCROLL_BAR_FADE_DURATION %f\n", duration );
+
+        impl.mAnimationPeriod.durationSeconds = duration;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PIXEL_SIZE:
+      {
+        if( impl.mController )
+        {
+          const float pixelSize = value.Get< float >();
+          DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p PIXEL_SIZE %f\n", impl.mController.Get(), pixelSize );
+
+          if( !Equals( impl.mController->GetDefaultFontSize( Text::Controller::PIXEL_SIZE ), pixelSize ) )
+          {
+            impl.mController->SetDefaultFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
+          }
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER_TEXT:
+      {
+        if( impl.mController )
+        {
+          const std::string& text = value.Get< std::string >();
+          DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor::OnPropertySet %p PLACEHOLDER_TEXT %s\n", impl.mController.Get(), text.c_str() );
+
+          impl.mController->SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER_TEXT_COLOR:
+      {
+        if( impl.mController )
+        {
+          const Vector4& textColor = value.Get< Vector4 >();
+          DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p PLACEHOLDER_TEXT_COLOR %f,%f,%f,%f\n", impl.mController.Get(), textColor.r, textColor.g, textColor.b, textColor.a );
+
+          if( impl.mController->GetPlaceholderTextColor() != textColor )
+          {
+            impl.mController->SetPlaceholderTextColor( textColor );
+            impl.mRenderer.Reset();
+          }
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::ENABLE_SELECTION:
+      {
+        if( impl.mController )
+        {
+          const bool enableSelection = value.Get< bool >();
+          DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p ENABLE_SELECTION %d\n", impl.mController.Get(), enableSelection );
+          impl.mController->SetSelectionEnabled( enableSelection );
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER:
+      {
+        const Property::Map* map = value.GetMap();
+        if( map )
+        {
+          impl.mController->SetPlaceholderProperty( *map );
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::LINE_WRAP_MODE:
+      {
+        const std::string& wrapModeStr = value.Get< std::string >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p LINE_WRAP_MODE %s\n", impl.mController.Get(), wrapModeStr.c_str() );
+
+        Layout::LineWrap::Mode lineWrapMode( Layout::LineWrap::WORD );
+        if( Scripting::GetEnumeration< Layout::LineWrap::Mode >( wrapModeStr.c_str(),
+                                                                 LINE_WRAP_MODE_STRING_TABLE,
+                                                                 LINE_WRAP_MODE_STRING_TABLE_COUNT,
+                                                                 lineWrapMode ) )
+        {
+          impl.mController->SetLineWrapMode( lineWrapMode );
+        }
+        break;
+      }
     } // switch
   } // texteditor
 }
@@ -671,7 +793,7 @@ Property::Value TextEditor::GetProperty( BaseObject* object, Property::Index ind
       {
         if( impl.mController )
         {
-          value = impl.mController->GetDefaultPointSize();
+          value = impl.mController->GetDefaultFontSize( Text::Controller::POINT_SIZE );
         }
         break;
       }
@@ -925,6 +1047,78 @@ Property::Value TextEditor::GetProperty( BaseObject* object, Property::Index ind
         value = impl.mScrollAnimationDuration;
         break;
       }
+      case Toolkit::DevelTextEditor::Property::ENABLE_SCROLL_BAR:
+      {
+        value = impl.mScrollBarEnabled;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SCROLL_BAR_SHOW_DURATION:
+      {
+        value = impl.mAnimationPeriod.delaySeconds;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SCROLL_BAR_FADE_DURATION:
+      {
+        value = impl.mAnimationPeriod.durationSeconds;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PIXEL_SIZE:
+      {
+        if( impl.mController )
+        {
+          value = impl.mController->GetDefaultFontSize( Text::Controller::PIXEL_SIZE );
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::LINE_COUNT:
+      {
+        if( impl.mController )
+        {
+          float width = textEditor.GetProperty( Actor::Property::SIZE_WIDTH ).Get<float>();
+          value = impl.mController->GetLineCount( width );
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER_TEXT:
+      {
+        if( impl.mController )
+        {
+          std::string text;
+          impl.mController->GetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
+          value = text;
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER_TEXT_COLOR:
+      {
+        if( impl.mController )
+        {
+          value = impl.mController->GetPlaceholderTextColor();
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::ENABLE_SELECTION:
+      {
+        if( impl.mController )
+        {
+          value = impl.mController->IsSelectionEnabled();
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::PLACEHOLDER:
+      {
+        Property::Map map;
+        impl.mController->GetPlaceholderProperty( map );
+        value = map;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::LINE_WRAP_MODE:
+      {
+        if( impl.mController )
+        {
+          value = impl.mController->GetLineWrapMode();
+        }
+      }
     } //switch
   }
 
@@ -965,6 +1159,11 @@ Toolkit::TextEditor::InputStyleChangedSignalType& TextEditor::InputStyleChangedS
   return mInputStyleChangedSignal;
 }
 
+Toolkit::DevelTextEditor::ScrollStateChangedSignalType& TextEditor::ScrollStateChangedSignal()
+{
+  return mScrollStateChangedSignal;
+}
+
 void TextEditor::OnInitialize()
 {
   Actor self = Self();
@@ -991,10 +1190,15 @@ void TextEditor::OnInitialize()
   // Enable the smooth handle panning.
   mController->SetSmoothHandlePanEnabled( true );
 
+  mController->SetNoTextDoubleTapAction( Controller::NoTextTap::HIGHLIGHT );
+  mController->SetNoTextLongPressAction( Controller::NoTextTap::HIGHLIGHT );
+
   // Forward input events to controller
   EnableGestureDetection( static_cast<Gesture::Type>( Gesture::Tap | Gesture::Pan | Gesture::LongPress ) );
   GetTapGestureDetector().SetMaximumTapsRequired( 2 );
 
+  mImfManager = ImfManager::Get();
+
   self.TouchSignal().Connect( this, &TextEditor::OnTouched );
 
   // Set BoundingBox to stage size if not already set.
@@ -1020,8 +1224,8 @@ void TextEditor::OnInitialize()
 
   // Creates an extra control to be used as stencil buffer.
   mStencil = Control::New();
-  mStencil.SetAnchorPoint( AnchorPoint::CENTER );
-  mStencil.SetParentOrigin( ParentOrigin::CENTER );
+  mStencil.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+  mStencil.SetParentOrigin( ParentOrigin::TOP_LEFT );
 
   // Creates a background visual. Even if the color is transparent it updates the stencil.
   mStencil.SetProperty( Toolkit::Control::Property::BACKGROUND,
@@ -1047,20 +1251,25 @@ void TextEditor::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange:
       const std::string& newFont = GetImpl( styleManager ).GetDefaultFontFamily();
       // Property system did not set the font so should update it.
       mController->UpdateAfterFontChange( newFont );
+      RelayoutRequest();
       break;
     }
 
     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
     {
       GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) );
+      RelayoutRequest();
       break;
     }
     case StyleChange::THEME_CHANGE:
     {
-      GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) );
+      // Nothing to do, let control base class handle this
       break;
     }
   }
+
+  // Up call to Control
+  Control::OnStyleChange( styleManager, change );
 }
 
 Vector3 TextEditor::GetNaturalSize()
@@ -1070,14 +1279,32 @@ Vector3 TextEditor::GetNaturalSize()
 
 float TextEditor::GetHeightForWidth( float width )
 {
-  return mController->GetHeightForWidth( width );
+  Padding padding;
+  Self().GetPadding( padding );
+  return mController->GetHeightForWidth( width ) + padding.top + padding.bottom;
 }
 
 void TextEditor::OnRelayout( const Vector2& size, RelayoutContainer& container )
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor OnRelayout\n");
 
-  const Text::Controller::UpdateTextType updateTextType = mController->Relayout( size );
+  Actor self = Self();
+  Padding padding;
+
+  self.GetPadding( padding );
+  Vector2 contentSize( size.x - ( padding.left + padding.right ), size.y - ( padding.top + padding.bottom ) );
+
+  if( mStencil )
+  {
+    mStencil.SetPosition( padding.left , padding.top  );
+  }
+  if( mActiveLayer )
+  {
+    mActiveLayer.SetPosition( padding.left , padding.top  );
+  }
+
+
+  const Text::Controller::UpdateTextType updateTextType = mController->Relayout( contentSize );
 
   if( ( Text::Controller::NONE_UPDATED != updateTextType ) ||
       !mRenderer )
@@ -1096,6 +1323,7 @@ void TextEditor::OnRelayout( const Vector2& size, RelayoutContainer& container )
     }
 
     RenderText( updateTextType );
+
   }
 
   // The text-editor emits signals when the input style changes. These changes of style are
@@ -1126,7 +1354,11 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
   {
     if( mRenderer )
     {
+      Dali::Toolkit::TextEditor handle = Dali::Toolkit::TextEditor( GetOwner() );
+
       renderableActor = mRenderer->Render( mController->GetView(),
+                                           handle,
+                                           Property::INVALID_INDEX, // Animatable property not supported
                                            mAlignmentOffset,
                                            DepthIndex::CONTENT );
     }
@@ -1149,6 +1381,7 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
          ++it )
     {
       self.Add( *it );
+      DevelActor::LowerToBottom( *it );
     }
     mClippingDecorationActors.clear();
 
@@ -1156,26 +1389,22 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
 
     ApplyScrollPosition();
   }
+  UpdateScrollBar();
 }
 
 void TextEditor::OnKeyInputFocusGained()
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor::OnKeyInputFocusGained %p\n", mController.Get() );
 
-  VirtualKeyboard::StatusChangedSignal().Connect( this, &TextEditor::KeyboardStatusChanged );
-
-  ImfManager imfManager = ImfManager::Get();
+  mImfManager.StatusChangedSignal().Connect( this, &TextEditor::KeyboardStatusChanged );
 
-  if ( imfManager )
-  {
-    imfManager.EventReceivedSignal().Connect( this, &TextEditor::OnImfEvent );
+  mImfManager.EventReceivedSignal().Connect( this, &TextEditor::OnImfEvent );
 
-    // Notify that the text editing start.
-    imfManager.Activate();
+  // Notify that the text editing start.
+  mImfManager.Activate();
 
-    // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
-    imfManager.SetRestoreAfterFocusLost( true );
-  }
+  // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
+  mImfManager.SetRestoreAfterFocusLost( true );
 
    ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
 
@@ -1193,19 +1422,15 @@ void TextEditor::OnKeyInputFocusLost()
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor:OnKeyInputFocusLost %p\n", mController.Get() );
 
-  VirtualKeyboard::StatusChangedSignal().Disconnect( this, &TextEditor::KeyboardStatusChanged );
+  mImfManager.StatusChangedSignal().Disconnect( this, &TextEditor::KeyboardStatusChanged );
 
-  ImfManager imfManager = ImfManager::Get();
-  if ( imfManager )
-  {
-    // The text editing is finished. Therefore the imf manager don't have restore activation.
-    imfManager.SetRestoreAfterFocusLost( false );
+  // The text editing is finished. Therefore the imf manager don't have restore activation.
+  mImfManager.SetRestoreAfterFocusLost( false );
 
-    // Notify that the text editing finish.
-    imfManager.Deactivate();
+  // Notify that the text editing finish.
+  mImfManager.Deactivate();
 
-    imfManager.EventReceivedSignal().Disconnect( this, &TextEditor::OnImfEvent );
-  }
+  mImfManager.EventReceivedSignal().Disconnect( this, &TextEditor::OnImfEvent );
 
   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
 
@@ -1223,14 +1448,12 @@ void TextEditor::OnTap( const TapGesture& gesture )
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor::OnTap %p\n", mController.Get() );
 
-  // Show the keyboard if it was hidden.
-  if (!VirtualKeyboard::IsVisible())
-  {
-    VirtualKeyboard::Show();
-  }
+  mImfManager.Activate();
 
   // Deliver the tap before the focus event to controller; this allows us to detect when focus is gained due to tap-gestures
-  mController->TapEvent( gesture.numberOfTaps, gesture.localPoint.x, gesture.localPoint.y );
+  Padding padding;
+  Self().GetPadding( padding );
+  mController->TapEvent( gesture.numberOfTaps, gesture.localPoint.x - padding.left, gesture.localPoint.y - padding.top );
 
   SetKeyInputFocus();
 }
@@ -1242,13 +1465,11 @@ void TextEditor::OnPan( const PanGesture& gesture )
 
 void TextEditor::OnLongPress( const LongPressGesture& gesture )
 {
-  // Show the keyboard if it was hidden.
-  if (!VirtualKeyboard::IsVisible())
-  {
-    VirtualKeyboard::Show();
-  }
+  mImfManager.Activate();
 
-  mController->LongPressEvent( gesture.state, gesture.localPoint.x, gesture.localPoint.y );
+  Padding padding;
+  Self().GetPadding( padding );
+  mController->LongPressEvent( gesture.state, gesture.localPoint.x - padding.left, gesture.localPoint.y - padding.top );
 
   SetKeyInputFocus();
 }
@@ -1257,9 +1478,14 @@ bool TextEditor::OnKeyEvent( const KeyEvent& event )
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor::OnKeyEvent %p keyCode %d\n", mController.Get(), event.keyCode );
 
-  if( Dali::DALI_KEY_ESCAPE == event.keyCode ) // Make a Dali key code for this
+  if( Dali::DALI_KEY_ESCAPE == event.keyCode && mController->ShouldClearFocusOnEscape() )
   {
-    ClearKeyInputFocus();
+    // Make sure ClearKeyInputFocus when only key is up
+    if( event.state == KeyEvent::Up )
+    {
+      ClearKeyInputFocus();
+    }
+
     return true;
   }
 
@@ -1346,8 +1572,107 @@ void TextEditor::AddDecoration( Actor& actor, bool needsClipping )
     }
     else
     {
+      actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+      actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
       Self().Add( actor );
+      mActiveLayer = actor;
+    }
+  }
+}
+
+void TextEditor::UpdateScrollBar()
+{
+  using namespace Dali;
+
+  float scrollPosition;
+  float controlSize;
+  float layoutSize;
+  bool latestScrolled;
+
+  if ( !mScrollBarEnabled )
+  {
+    return;
+  }
+  latestScrolled = mController->GetTextScrollInfo( scrollPosition, controlSize, layoutSize );
+  if ( !latestScrolled || controlSize > layoutSize)
+  {
+    return;
+  }
+
+  CustomActor self = Self();
+  if( !mScrollBar )
+  {
+    mScrollBar = Toolkit::ScrollBar::New( Toolkit::ScrollBar::Vertical );
+    mScrollBar.SetIndicatorHeightPolicy( Toolkit::ScrollBar::Variable );
+    mScrollBar.SetParentOrigin( ParentOrigin::TOP_RIGHT );
+    mScrollBar.SetAnchorPoint( AnchorPoint::TOP_RIGHT );
+    mScrollBar.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
+    mScrollBar.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
+
+    // Register the scroll position property
+    Property::Index propertyScrollPosition = self.RegisterProperty( SCROLL_BAR_POSITION, scrollPosition );
+    // Register the minimum scroll position property
+    Property::Index propertyMinScrollPosition = self.RegisterProperty( SCROLL_BAR_POSITION_MIN, 0.0f );
+    // Register the maximum scroll position property
+    Property::Index propertyMaxScrollPosition = self.RegisterProperty( SCROLL_BAR_POSITION_MAX, (layoutSize - controlSize) );
+    // Register the scroll content size property
+    Property::Index propertyScrollContentSize = self.RegisterProperty( SCROLL_BAR_CONTENT_SIZE, layoutSize );
+
+    mScrollBar.SetScrollPropertySource(self, propertyScrollPosition, propertyMinScrollPosition, propertyMaxScrollPosition, propertyScrollContentSize);
+
+    // Set style name of ScrollBar for styling
+    mScrollBar.SetStyleName("TextEditorScrollBar");
+    Toolkit::Control scrollIndicator = Toolkit::Control::DownCast( mScrollBar.GetScrollIndicator() );
+    if( scrollIndicator )
+    {
+      // Set style name of ScrollBarIndicator for styling
+      scrollIndicator.SetStyleName("TextEditorScrollBarIndicator");
     }
+
+    self.Add( mScrollBar );
+  }
+  else
+  {
+    Property::Index propertyScrollPosition = self.GetPropertyIndex( SCROLL_BAR_POSITION );
+    Property::Index propertyMaxScrollPosition = self.GetPropertyIndex( SCROLL_BAR_POSITION_MAX );
+    Property::Index propertyScrollContentSize = self.GetPropertyIndex( SCROLL_BAR_CONTENT_SIZE );
+
+    self.SetProperty( propertyScrollPosition, scrollPosition );
+    self.SetProperty( propertyMaxScrollPosition, (layoutSize - controlSize) );
+    self.SetProperty( propertyScrollContentSize, layoutSize );
+  }
+
+  // If scrolling is not started, start scrolling and emit ScrollStateChangedSignal
+  if( !mScrollStarted )
+  {
+    mScrollStarted = true;
+    Dali::Toolkit::TextEditor handle( GetOwner() );
+    mScrollStateChangedSignal.Emit( handle, DevelTextEditor::Scroll::STARTED );
+  }
+
+  Actor indicator = mScrollBar.GetScrollIndicator();
+  if( mAnimation )
+  {
+    mAnimation.Stop(); // Cancel any animation
+  }
+  else
+  {
+    mAnimation = Animation::New( mAnimationPeriod.durationSeconds );
+  }
+  indicator.SetOpacity(1.0f);
+  mAnimation.AnimateTo( Property( indicator, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN, mAnimationPeriod );
+  mAnimation.Play();
+  mAnimation.FinishedSignal().Connect( this, &TextEditor::OnScrollIndicatorAnimationFinished );
+}
+
+void TextEditor::OnScrollIndicatorAnimationFinished( Animation& animation )
+{
+  // If animation is successfully ended, then emit ScrollStateChangedSignal
+  if( animation.GetCurrentProgress() == 0.0f )
+  {
+    mScrollStarted = false;
+    Dali::Toolkit::TextEditor handle( GetOwner() );
+    mScrollStateChangedSignal.Emit( handle, DevelTextEditor::Scroll::FINISHED );
   }
 }
 
@@ -1459,12 +1784,15 @@ void TextEditor::ApplyScrollPosition()
 
 TextEditor::TextEditor()
 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
+  mAnimationPeriod( 0.0f, 0.0f ),
   mIdleCallback( NULL ),
   mAlignmentOffset( 0.f ),
   mScrollAnimationDuration( 0.f ),
   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
   mHasBeenStaged( false ),
-  mScrollAnimationEnabled( false )
+  mScrollAnimationEnabled( false ),
+  mScrollBarEnabled( false ),
+  mScrollStarted( false )
 {
 }