Copied some TextInput grab/selection handle code 77/36077/8
authorPaul Wisbey <p.wisbey@samsung.com>
Sun, 1 Mar 2015 13:39:27 +0000 (13:39 +0000)
committerPaul Wisbey <p.wisbey@samsung.com>
Mon, 2 Mar 2015 11:12:36 +0000 (11:12 +0000)
Change-Id: Id65562d557cd2017e3d54f44e9e764a34ad73697

dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/public-api/text/decorator/text-decorator.cpp
dali-toolkit/public-api/text/decorator/text-decorator.h
dali-toolkit/public-api/text/rendering/basic/text-basic-renderer.cpp
dali-toolkit/public-api/text/text-controller.cpp
dali-toolkit/public-api/text/text-controller.h

index 4ebda6f..53c5705 100644 (file)
@@ -32,8 +32,11 @@ using namespace Dali::Toolkit::Text;
 namespace
 {
 
+const unsigned int DEFAULT_RENDERING_BACKEND = 0;
+
 } // namespace
 
+
 namespace Dali
 {
 
@@ -300,12 +303,23 @@ void TextField::OnInitialize()
 
   // Forward input events to controller
   EnableGestureDetection( Gesture::Tap );
+
+  // TODO - Fix TapGestureDetector to support single and double tap
+  mDoubleTapDetector = TapGestureDetector::New();
+  mDoubleTapDetector.SetTapsRequired( 2 );
+  mDoubleTapDetector.DetectedSignal().Connect( this, &TextField::OnDoubleTap );
+  mDoubleTapDetector.Attach(Self());
 }
 
 void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container )
 {
   if( mController->Relayout( size ) )
   {
+    if( mDecorator )
+    {
+      mDecorator->Relayout( size );
+    }
+
     if( !mRenderer )
     {
       mRenderer = Backend::Get().NewRenderer( mRenderingBackend );
@@ -325,7 +339,12 @@ void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container )
 
 void TextField::OnTap( const TapGesture& tap )
 {
-  mController->TapEvent( tap.localPoint.x, tap.localPoint.y );
+  mController->TapEvent( tap.numberOfTaps, tap.localPoint.x, tap.localPoint.y );
+}
+
+void TextField::OnDoubleTap( Actor actor, const TapGesture& tap )
+{
+  mController->TapEvent( tap.numberOfTaps, tap.localPoint.x, tap.localPoint.y );
 }
 
 void TextField::RequestTextRelayout()
@@ -334,7 +353,8 @@ void TextField::RequestTextRelayout()
 }
 
 TextField::TextField()
-: Control( ControlBehaviour( CONTROL_BEHAVIOUR_NONE ) )
+: Control( ControlBehaviour( CONTROL_BEHAVIOUR_NONE ) ),
+  mRenderingBackend( DEFAULT_RENDERING_BACKEND )
 {
 }
 
index 443e3e1..7b013f5 100644 (file)
@@ -89,6 +89,11 @@ private: // From Control
   virtual void OnTap( const TapGesture& tap );
 
   /**
+   * TODO - Fix TapGestureDetector to support single and double tap
+   */
+  void OnDoubleTap( Actor actor, const TapGesture& tap );
+
+  /**
    * @copydoc Text::ControlInterface::RequestTextRelayout()
    */
   virtual void RequestTextRelayout();
@@ -117,6 +122,8 @@ private: // Data
   Text::RendererPtr mRenderer;
   Text::DecoratorPtr mDecorator;
 
+  TapGestureDetector mDoubleTapDetector;
+
   unsigned int mRenderingBackend;
 };
 
index 4a7f666..0503635 100644 (file)
 #include <dali-toolkit/public-api/text/decorator/text-decorator.h>
 
 // EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/actors/image-actor.h>
+#include <dali/public-api/actors/layer.h>
 #include <dali/public-api/common/constants.h>
-#include <dali/public-api/images/image.h>
+#include <dali/public-api/events/tap-gesture.h>
+#include <dali/public-api/events/tap-gesture-detector.h>
+#include <dali/public-api/events/pan-gesture.h>
+#include <dali/public-api/events/pan-gesture-detector.h>
+#include <dali/public-api/images/resource-image.h>
 #include <dali/public-api/math/vector2.h>
 #include <dali/public-api/math/vector4.h>
+#include <dali/public-api/signals/connection-tracker.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+
+#ifdef DEBUG_ENABLED
+#define DECORATOR_DEBUG
+#endif
+
+// Local Data
+namespace
+{
+
+const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
+const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
+const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
+//const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
+//const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
+
+const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
+const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
+
+} // end of namespace
 
 namespace Dali
 {
@@ -33,7 +64,7 @@ namespace Toolkit
 namespace Text
 {
 
-struct Decorator::Impl
+struct Decorator::Impl : public ConnectionTracker
 {
   struct CursorImpl
   {
@@ -52,10 +83,33 @@ struct Decorator::Impl
     Vector4 color;
   };
 
-  Impl(Dali::Toolkit::Internal::Control& parent, Observer& observer)
+  struct SelectionHandleImpl
+  {
+    SelectionHandleImpl()
+    : x(0.0f),
+      y(0.0f),
+      cursorHeight(0.0f),
+      flipped(false)
+    {
+    }
+
+    float x;
+    float y;
+    float cursorHeight; ///< Not the handle height
+    bool flipped;
+
+    ImageActor actor;
+    Actor grabArea;
+
+    Image pressedImage;
+    Image releasedImage;
+  };
+
+  Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
   : mParent(parent),
     mObserver(observer),
     mActiveCursor(ACTIVE_CURSOR_NONE),
+    mActiveGrabHandle(false),
     mCursorBlinkInterval(0.5f),
     mCursorBlinkDuration(0.0f)
   {
@@ -63,19 +117,237 @@ struct Decorator::Impl
 
   void Relayout( const Vector2& size )
   {
+    // Show or hide the grab handle
+    if( mActiveGrabHandle )
+    {
+      SetupTouchEvents();
+
+      CreateActiveLayer();
+      CreateGrabHandle();
+
+      mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height );
+    }
+    else if( mGrabHandle )
+    {
+      UnparentAndReset( mGrabHandle );
+    }
+
+    // Show or hide the selection handles/highlight
+    if( mActiveSelection )
+    {
+      SetupTouchEvents();
+
+      CreateActiveLayer();
+      CreateSelectionHandles();
+
+      SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
+      primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight );
+
+      SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
+      secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight );
+
+      //CreateHighlight(); TODO
+    }
+    else
+    {
+      UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
+      UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
+    }
+
     // TODO
   }
 
+  void SetupTouchEvents()
+  {
+    if ( !mTapDetector )
+    {
+      mTapDetector = TapGestureDetector::New();
+      mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
+    }
+
+    if ( !mPanGestureDetector )
+    {
+      mPanGestureDetector = PanGestureDetector::New();
+      mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
+    }
+  }
+
+  void CreateActiveLayer()
+  {
+    if( !mActiveLayer )
+    {
+      Actor parent = mParent.Self();
+
+      mActiveLayer = Layer::New();
+#ifdef DECORATOR_DEBUG
+      mActiveLayer.SetName ( "ActiveLayerActor" );
+#endif
+
+      mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
+      mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
+      mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
+      mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
+
+      parent.Add( mActiveLayer );
+    }
+
+    mActiveLayer.RaiseToTop();
+  }
+
+  void CreateGrabHandle()
+  {
+    if( !mGrabHandle )
+    {
+      if ( !mGrabHandleImage )
+      {
+        mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
+      }
+
+      mGrabHandle = ImageActor::New( mGrabHandleImage );
+#ifdef DECORATOR_DEBUG
+      mGrabHandle.SetName( "GrabHandleActor" );
+#endif
+      mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
+      mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
+      mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
+
+      mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
+#ifdef DECORATOR_DEBUG
+      mGrabArea.SetName( "GrabArea" );
+#endif
+      mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
+      mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+      mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
+      mGrabHandle.Add(mGrabArea);
+
+      mTapDetector.Attach( mGrabArea );
+      mPanGestureDetector.Attach( mGrabArea );
+
+      mActiveLayer.Add(mGrabHandle);
+    }
+  }
+
+  void CreateSelectionHandles()
+  {
+    SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
+    if ( !primary.actor )
+    {
+      if ( !primary.releasedImage )
+      {
+        primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
+      }
+
+      primary.actor = ImageActor::New( primary.releasedImage );
+#ifdef DECORATOR_DEBUG
+      primary.actor.SetName("SelectionHandleOne");
+#endif
+      primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+      primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
+      primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
+      primary.flipped = false;
+
+      primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
+#ifdef DECORATOR_DEBUG
+      primary.grabArea.SetName("SelectionHandleOneGrabArea");
+#endif
+      primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+      primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
+      primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
+
+      mTapDetector.Attach( primary.grabArea );
+      mPanGestureDetector.Attach( primary.grabArea );
+      primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
+
+      primary.actor.Add( primary.grabArea );
+      mActiveLayer.Add( primary.actor );
+    }
+
+    SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
+    if ( !secondary.actor )
+    {
+      if ( !secondary.releasedImage )
+      {
+        secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
+      }
+
+      secondary.actor = ImageActor::New( secondary.releasedImage );
+#ifdef DECORATOR_DEBUG
+      secondary.actor.SetName("SelectionHandleTwo");
+#endif
+      secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+      secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
+      secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
+      secondary.flipped = false;
+
+      secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
+#ifdef DECORATOR_DEBUG
+      secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
+#endif
+      secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
+      secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
+      secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
+
+      mTapDetector.Attach( secondary.grabArea );
+      mPanGestureDetector.Attach( secondary.grabArea );
+      secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
+
+      secondary.actor.Add( secondary.grabArea );
+      mActiveLayer.Add( secondary.actor );
+    }
+
+    //SetUpHandlePropertyNotifications(); TODO
+  }
+
+  void OnTap( Actor actor, const TapGesture& tap )
+  {
+    if( actor == mGrabHandle )
+    {
+      // TODO
+    }
+  }
+
+  void OnPan( Actor actor, const PanGesture& gesture )
+  {
+    if( actor == mGrabHandle )
+    {
+      // TODO
+    }
+  }
+
+  bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
+  {
+    // TODO
+    return false;
+  }
+
+  bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
+  {
+    // TODO
+    return false;
+  }
+
   Internal::Control& mParent;
   Observer& mObserver;
 
+  Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
+
   unsigned int mActiveCursor;
+  bool         mActiveGrabHandle;
+  bool         mActiveSelection;
 
   CursorImpl mCursor[CURSOR_COUNT];
 
+  ImageActor mGrabHandle;
+  Actor mGrabArea;
+
+  SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
+
   Image mCursorImage;
   Image mGrabHandleImage;
 
+  TapGestureDetector mTapDetector;
+  PanGestureDetector mPanGestureDetector;
+
   float mCursorBlinkInterval;
   float mCursorBlinkDuration;
 };
@@ -164,6 +436,16 @@ float Decorator::GetCursorBlinkDuration() const
   return mImpl->mCursorBlinkDuration;
 }
 
+void Decorator::SetGrabHandleActive( bool active )
+{
+  mImpl->mActiveGrabHandle = active;
+}
+
+bool Decorator::IsGrabHandleActive() const
+{
+  return mImpl->mActiveGrabHandle;
+}
+
 void Decorator::SetGrabHandleImage( Dali::Image image )
 {
   mImpl->mGrabHandleImage = image;
@@ -174,15 +456,61 @@ Dali::Image Decorator::GetGrabHandleImage() const
   return mImpl->mGrabHandleImage;
 }
 
+void Decorator::SetSelectionActive( bool active )
+{
+  mImpl->mActiveSelection = active;
+}
+
+bool Decorator::IsSelectionActive() const
+{
+  return mImpl->mActiveSelection;
+}
+
+void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
+{
+  mImpl->mSelectionHandle[handle].x = x;
+  mImpl->mSelectionHandle[handle].y = y;
+  mImpl->mSelectionHandle[handle].cursorHeight = height;
+}
+
+void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
+{
+  x = mImpl->mSelectionHandle[handle].x;
+  y = mImpl->mSelectionHandle[handle].y;
+  height = mImpl->mSelectionHandle[handle].cursorHeight;
+}
+
+void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
+{
+  if( SELECTION_HANDLE_PRESSED == state )
+  {
+    mImpl->mSelectionHandle[handle].pressedImage = image;
+  }
+  else
+  {
+    mImpl->mSelectionHandle[handle].releasedImage = image;
+  }
+}
+
+Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
+{
+  if( SELECTION_HANDLE_PRESSED == state )
+  {
+    return mImpl->mSelectionHandle[handle].pressedImage;
+  }
+
+  return mImpl->mSelectionHandle[handle].releasedImage;
+}
+
 Decorator::~Decorator()
 {
   delete mImpl;
 }
 
-Decorator::Decorator(Dali::Toolkit::Internal::Control& parent, Observer& observer)
+Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
 : mImpl( NULL )
 {
-  mImpl = new Decorator::Impl(parent, observer);
+  mImpl = new Decorator::Impl( parent, observer );
 }
 
 } // namespace Text
index 3ebc588..809be0d 100644 (file)
@@ -62,10 +62,25 @@ enum ActiveCursor
 // The state information for grab handle events
 enum GrabHandleState
 {
-  GRAB_HANDLE_MOVING,
+  GRAB_HANDLE_TAPPED,
+  GRAB_HANDLE_PRESSED,
   GRAB_HANDLE_RELEASED
 };
 
+// The set the selection-handle positions etc.
+enum SelectionHandle
+{
+  PRIMARY_SELECTION_HANDLE,
+  SECONDARY_SELECTION_HANDLE,
+  SELECTION_HANDLE_COUNT
+};
+
+enum SelectionHandleState
+{
+  SELECTION_HANDLE_PRESSED,
+  SELECTION_HANDLE_RELEASED
+};
+
 /**
  * @brief A Text Decorator is used to display cursors, handles, selection highlights and pop-ups.
  *
@@ -250,6 +265,58 @@ public:
    */
   Dali::Image GetGrabHandleImage() const;
 
+  /**
+   * @brief Sets whether the selection handles and highlight are active.
+   *
+   * @param[in] active True if the selection handles and highlight are active.
+   */
+  void SetSelectionActive( bool active );
+
+  /**
+   * @brief Query whether the selection handles and highlight are active.
+   *
+   * @return True if the selection handles and highlight are active.
+   */
+  bool IsSelectionActive() const;
+
+  /**
+   * @brief Sets the position of a selection handle.
+   *
+   * @param[in] handle The handle to set.
+   * @param[in] x The x position relative to the top-left of the parent control.
+   * @param[in] y The y position relative to the top-left of the parent control.
+   * @param[in] cursorHeight The logical cursor height at this position.
+   */
+  void SetPosition( SelectionHandle handle, float x, float y, float cursorHeight );
+
+  /**
+   * @brief Retrieves the position of a selection handle.
+   *
+   * @param[in] handle The handle to get.
+   * @param[out] x The x position relative to the top-left of the parent control.
+   * @param[out] y The y position relative to the top-left of the parent control.
+   * @param[out] cursorHeight The logical cursor height at this position.
+   */
+  void GetPosition( SelectionHandle handle, float& x, float& y, float& cursorHeight ) const;
+
+  /**
+   * @brief Sets the image for one of the selection handles.
+   *
+   * @param[in] handle The selection handle.
+   * @param[in] state A different image can be set for the pressed/released states.
+   * @param[in] image The image to use.
+   */
+  void SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image );
+
+  /**
+   * @brief Retrieves the image for a selection handle.
+   *
+   * @param[in] handle The selection handle.
+   * @param[in] state A different image can be set for the pressed/released states.
+   * @return The image.
+   */
+  Dali::Image GetImage( SelectionHandle handle, SelectionHandleState state ) const;
+
 protected:
 
   /**
index 6fbaa3a..5cd608f 100644 (file)
@@ -279,6 +279,8 @@ Text::RendererPtr BasicRenderer::New()
 
 RenderableActor BasicRenderer::Render( Text::ViewInterface& view )
 {
+  UnparentAndReset( mImpl->mActor );
+
   Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
 
   if( numberOfGlyphs > 0 )
index 4582944..7d0b956 100644 (file)
 #include <dali-toolkit/public-api/text/visual-model.h>
 
 // EXTERNAL INCLUDES
-#include <dali/public-api/text-abstraction/font-client.h>
 #include <limits>
+#include <vector>
+#include <dali/public-api/text-abstraction/font-client.h>
+
+using std::vector;
 
 namespace Dali
 {
@@ -56,6 +59,7 @@ struct Controller::TextInput
   union Param
   {
     int mInt;
+    unsigned int mUint;
     float mFloat;
   };
 
@@ -71,16 +75,102 @@ struct Controller::TextInput
     EventType type;
     Param p1;
     Param p2;
+    Param p3;
+  };
+
+  enum State
+  {
+    INACTIVE,
+    SELECTING,
+    EDITING
   };
 
   TextInput( DecoratorPtr decorator )
-  : mDecorator( decorator )
+  : mDecorator( decorator ),
+    mState( INACTIVE )
   {
   }
 
+  /**
+   * @brief Helper to move the cursor, grab handle etc.
+   */
+  bool ProcessTouchEvents()
+  {
+    mDecoratorUpdated = false;
+
+    if( mDecorator )
+    {
+      for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
+      {
+        switch( iter->type )
+        {
+          case KEYBOARD_FOCUS_GAIN_EVENT:
+          {
+            OnKeyboardFocus( true );
+            break;
+          }
+          case KEYBOARD_FOCUS_LOST_EVENT:
+          {
+            OnKeyboardFocus( false );
+            break;
+          }
+          case TAP_EVENT:
+          {
+            OnTapEvent( *iter );
+            break;
+          }
+          case GRAB_HANDLE_EVENT:
+          {
+            OnGrabHandleEvent( *iter );
+            break;
+          }
+        }
+      }
+    }
+
+    mEventQueue.clear();
+
+    return mDecoratorUpdated;
+  }
+
+  void OnKeyboardFocus( bool hasFocus )
+  {
+    // TODO
+  }
+
+  void OnTapEvent( const Event& event )
+  {
+    if( 1u == event.p1.mUint )
+    {
+      mState = TextInput::EDITING;
+      mDecorator->SetGrabHandleActive( true );
+      mDecorator->SetPosition( PRIMARY_CURSOR, 0, 0, 10 );
+      mDecoratorUpdated = true;
+    }
+    else if( 2u == event.p1.mUint )
+    {
+      mState = TextInput::SELECTING;
+      mDecorator->SetGrabHandleActive( false );
+      mDecorator->SetSelectionActive( true );
+      mDecoratorUpdated = true;
+    }
+  }
+
+  void OnGrabHandleEvent( const Event& event )
+  {
+    // TODO
+  }
+
   DecoratorPtr mDecorator;
+  bool mDecoratorUpdated;
+
+  State mState;
 
-  std::vector<Event> mEventQueue;
+  /**
+   * This is used to delay handling events until after the model has been updated.
+   * The number of updates to the model is minimized to improve performance.
+   */
+  vector<Event> mEventQueue; ///< The queue of touch events etc.
 };
 
 struct Controller::Impl
@@ -162,11 +252,11 @@ bool Controller::Relayout( const Vector2& size )
     return false;
   }
 
-  bool viewUpdated = false;
+  bool updated = false;
 
   if( size != mImpl->mControlSize )
   {
-    viewUpdated = DoRelayout( size, mImpl->mOperations );
+    updated = DoRelayout( size, mImpl->mOperations );
 
     // Do not re-do any operation until something changes.
     mImpl->mOperations = NO_OPERATION;
@@ -174,7 +264,13 @@ bool Controller::Relayout( const Vector2& size )
     mImpl->mControlSize = size;
   }
 
-  return viewUpdated;
+  if( mImpl->mTextInput )
+  {
+    // Move the cursor, grab handle etc.
+    updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
+  }
+
+  return updated;
 }
 
 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
@@ -308,8 +404,6 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
     viewUpdated = true;
   }
 
-  // TODO - process input events to move grab handle
-
   return viewUpdated;
 }
 
@@ -411,15 +505,17 @@ void Controller::KeyboardFocusLostEvent()
   }
 }
 
-void Controller::TapEvent( float x, float y)
+void Controller::TapEvent( unsigned int tapCount, float x, float y )
 {
   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
 
   if( mImpl->mTextInput )
   {
     TextInput::Event event( TextInput::TAP_EVENT );
-    event.p1.mFloat = x;
-    event.p2.mFloat = y;
+    event.p1.mUint = tapCount;
+    event.p2.mFloat = x;
+    event.p3.mFloat = y;
+    mImpl->mTextInput->mEventQueue.push_back( event );
 
     RequestRelayout();
   }
@@ -434,6 +530,7 @@ void Controller::GrabHandleEvent( GrabHandleState state, float x )
     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
     event.p1.mInt   = state;
     event.p2.mFloat = x;
+    mImpl->mTextInput->mEventQueue.push_back( event );
 
     RequestRelayout();
   }
index d2c9117..8f67531 100644 (file)
@@ -109,7 +109,7 @@ public:
    *
    * @note UI Controls are expected to minimize calls to this method e.g. call once after size negotiation.
    * @param[in] size A the size of a bounding box to layout text within.
-   * @return True if the View was updated.
+   * @return True if the text model or decorations were updated.
    */
   bool Relayout( const Vector2& size );
 
@@ -153,9 +153,12 @@ public:
   void KeyboardFocusLostEvent();
 
   /**
-   * @brief Caller by editable UI controls when focus is lost.
+   * @brief Caller by editable UI controls when a tap gesture occurs.
+   * @param[in] tapCount The number of taps.
+   * @param[in] x The x position relative to the top-left of the parent control.
+   * @param[in] y The y position relative to the top-left of the parent control.
    */
-  void TapEvent( float x, float y );
+  void TapEvent( unsigned int tapCount, float x, float y );
 
   /**
    * @copydoc Dali::Toolkit::Text::Decorator::Observer::GrabHandleEvent()