Merge "Change the double tap and long press events on top of no text actions to highl...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-editor-impl.cpp
index 51684a4..fc86c7e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
@@ -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/object/property-helper-devel.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/integration-api/adaptors/adaptor.h>
 #include <dali/integration-api/debug.h>
@@ -33,6 +33,7 @@
 #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-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>
 #include <dali-toolkit/internal/text/rendering/text-backend.h>
 #include <dali-toolkit/internal/text/text-effects-style.h>
@@ -73,6 +74,11 @@ 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 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()
 {
@@ -122,6 +128,11 @@ DALI_PROPERTY_REGISTRATION( Toolkit, TextEditor, "emboss",
 DALI_PROPERTY_REGISTRATION( Toolkit, TextEditor, "inputEmboss",                          MAP,       INPUT_EMBOSS                         )
 DALI_PROPERTY_REGISTRATION( Toolkit, TextEditor, "outline",                              MAP,       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_SIGNAL_REGISTRATION( Toolkit, TextEditor, "textChanged",        SIGNAL_TEXT_CHANGED )
 DALI_SIGNAL_REGISTRATION( Toolkit, TextEditor, "inputStyleChanged",  SIGNAL_INPUT_STYLE_CHANGED )
@@ -590,6 +601,50 @@ void TextEditor::SetProperty( BaseObject* object, Property::Index index, const P
         }
         break;
       }
+      case Toolkit::DevelTextEditor::Property::SMOOTH_SCROLL:
+      {
+        const bool enable = value.Get< bool >();
+        DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor SMOOTH_SCROLL %d\n", enable );
+
+        impl.mScrollAnimationEnabled = enable;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SMOOTH_SCROLL_DURATION:
+      {
+        const float duration = value.Get< float >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor SMOOTH_SCROLL_DURATION %f\n", duration );
+
+        impl.mScrollAnimationDuration = duration;
+        if ( impl.mTextVerticalScroller )
+        {
+          impl.mTextVerticalScroller->SetDuration( duration );
+        }
+        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;
+      }
     } // switch
   } // texteditor
 }
@@ -891,6 +946,31 @@ Property::Value TextEditor::GetProperty( BaseObject* object, Property::Index ind
         GetOutlineProperties( impl.mController, value, Text::EffectStyle::INPUT );
         break;
       }
+      case Toolkit::DevelTextEditor::Property::SMOOTH_SCROLL:
+      {
+        value = impl.mScrollAnimationEnabled;
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::SMOOTH_SCROLL_DURATION:
+      {
+        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;
+      }
     } //switch
   }
 
@@ -957,10 +1037,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.
@@ -1092,7 +1177,9 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
   {
     if( mRenderer )
     {
-      renderableActor = mRenderer->Render( mController->GetView(), DepthIndex::TEXT );
+      renderableActor = mRenderer->Render( mController->GetView(),
+                                           mAlignmentOffset,
+                                           DepthIndex::CONTENT );
     }
 
     if( renderableActor != mRenderableActor )
@@ -1104,10 +1191,6 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
 
   if( mRenderableActor )
   {
-    const Vector2& scrollOffset = mController->GetTextModel()->GetScrollPosition();
-
-    mRenderableActor.SetPosition( scrollOffset.x, scrollOffset.y );
-
     // Make sure the actors are parented correctly with/without clipping
     Actor self = mStencil ? mStencil : Self();
 
@@ -1121,27 +1204,25 @@ void TextEditor::RenderText( Text::Controller::UpdateTextType updateTextType )
     mClippingDecorationActors.clear();
 
     self.Add( mRenderableActor );
+
+    ApplyScrollPosition();
   }
+  UpdateScrollBar();
 }
 
 void TextEditor::OnKeyInputFocusGained()
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor::OnKeyInputFocusGained %p\n", mController.Get() );
 
-  VirtualKeyboard::StatusChangedSignal().Connect( this, &TextEditor::KeyboardStatusChanged );
+  mImfManager.StatusChangedSignal().Connect( this, &TextEditor::KeyboardStatusChanged );
 
-  ImfManager imfManager = ImfManager::Get();
+  mImfManager.EventReceivedSignal().Connect( this, &TextEditor::OnImfEvent );
 
-  if ( imfManager )
-  {
-    imfManager.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() );
 
@@ -1159,19 +1240,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() );
 
@@ -1189,11 +1266,7 @@ 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 );
@@ -1208,11 +1281,7 @@ 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 );
 
@@ -1225,7 +1294,12 @@ bool TextEditor::OnKeyEvent( const KeyEvent& event )
 
   if( Dali::DALI_KEY_ESCAPE == event.keyCode ) // Make a Dali key code for this
   {
-    ClearKeyInputFocus();
+    // Make sure ClearKeyInputFocus when only key is up
+    if( event.state == KeyEvent::Up )
+    {
+      ClearKeyInputFocus();
+    }
+
     return true;
   }
 
@@ -1317,6 +1391,73 @@ void TextEditor::AddDecoration( Actor& actor, bool needsClipping )
   }
 }
 
+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);
+
+    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 );
+  }
+
+  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();
+}
+
 void TextEditor::OnStageConnect( Dali::Actor actor )
 {
   if ( mHasBeenStaged )
@@ -1372,13 +1513,13 @@ void TextEditor::KeyboardStatusChanged(bool keyboardShown)
 
 void TextEditor::OnStageConnection( int depth )
 {
-  // Call the Control::OnStageConnection() to set the depth of the background.
-  Control::OnStageConnection( depth );
-
   // Sets the depth to the visuals inside the text's decorator.
   mDecorator->SetTextDepth( depth );
 
   // The depth of the text renderer is set in the RenderText() called from OnRelayout().
+
+  // Call the Control::OnStageConnection() to set the depth of the background.
+  Control::OnStageConnection( depth );
 }
 
 bool TextEditor::OnTouched( Actor actor, const TouchData& touch )
@@ -1395,11 +1536,44 @@ void TextEditor::OnIdleSignal()
   mIdleCallback = NULL;
 }
 
+void TextEditor::ApplyScrollPosition()
+{
+  const Vector2& scrollOffset = mController->GetTextModel()->GetScrollPosition();
+  float scrollAmount = 0.0f;
+
+  if ( mScrollAnimationEnabled )
+  {
+    scrollAmount = mController->GetScrollAmountByUserInput();
+  }
+  if ( mTextVerticalScroller )
+  {
+    mTextVerticalScroller->CheckStartAnimation( mRenderableActor, scrollOffset.x + mAlignmentOffset, scrollOffset.y - scrollAmount, scrollAmount );
+  }
+  else if ( Equals( scrollAmount, 0.0f, Math::MACHINE_EPSILON_1 ))
+  {
+    mRenderableActor.SetPosition( scrollOffset.x + mAlignmentOffset, scrollOffset.y - scrollAmount );
+  }
+  else
+  {
+    mTextVerticalScroller = Text::TextVerticalScroller::New();
+    if ( !Equals( mScrollAnimationDuration, 0.0f, Math::MACHINE_EPSILON_1 ))
+    {
+      mTextVerticalScroller->SetDuration( mScrollAnimationDuration );
+    }
+    mTextVerticalScroller->CheckStartAnimation( mRenderableActor, scrollOffset.x + mAlignmentOffset, scrollOffset.y - scrollAmount, scrollAmount );
+  }
+}
+
 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 )
+  mHasBeenStaged( false ),
+  mScrollAnimationEnabled( false ),
+  mScrollBarEnabled( false )
 {
 }