Adding Decorator BoundingBox
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / text / text-controller.cpp
index 4582944..58fd536 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,173 @@ struct Controller::TextInput
     EventType type;
     Param p1;
     Param p2;
+    Param p3;
+  };
+
+  enum State
+  {
+    INACTIVE,
+    SELECTING,
+    EDITING
   };
 
-  TextInput( DecoratorPtr decorator )
-  : mDecorator( decorator )
+  TextInput( LogicalModelPtr logicalModel,
+             VisualModelPtr visualModel,
+             DecoratorPtr decorator )
+  : mLogicalModel( logicalModel ),
+    mVisualModel( visualModel ),
+    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;
   }
 
-  DecoratorPtr mDecorator;
+  void OnKeyboardFocus( bool hasFocus )
+  {
+    // TODO
+  }
+
+  void OnTapEvent( const Event& event )
+  {
+    if( 1u == event.p1.mUint )
+    {
+      mState = TextInput::EDITING;
+      mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+      mDecorator->StartCursorBlink();
+      mDecorator->SetGrabHandleActive( true );
+
+      float xPosition = event.p2.mFloat;
+      float yPosition = event.p3.mFloat;
+      float height(0.0f);
+      GetClosestCursorPosition( xPosition, yPosition, height );
+      mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
+
+      mDecoratorUpdated = true;
+    }
+    else if( 2u == event.p1.mUint )
+    {
+      mState = TextInput::SELECTING;
+      mDecorator->SetGrabHandleActive( false );
+      mDecorator->SetSelectionActive( true );
+      mDecoratorUpdated = true;
+    }
+  }
+
+  void OnGrabHandleEvent( const Event& event )
+  {
+    unsigned int state = event.p1.mUint;
+
+    if( GRAB_HANDLE_PRESSED == state )
+    {
+      float xPosition = event.p2.mFloat;
+      float yPosition = event.p3.mFloat;
+      float height(0.0f);
+
+      GetClosestCursorPosition( xPosition, yPosition, height );
+
+      mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
+      mDecoratorUpdated = true;
+    }
+  }
 
-  std::vector<Event> mEventQueue;
+  void GetClosestCursorPosition( float& x, float& y, float& height )
+  {
+    // TODO - Look at LineRuns first
+
+    Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
+    if( 0 == numberOfGlyphs )
+    {
+      return;
+    }
+
+    Vector<GlyphInfo> glyphs;
+    glyphs.Resize( numberOfGlyphs );
+    mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
+
+    std::vector<Vector2> positions;
+    positions.resize( numberOfGlyphs );
+    mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
+
+    unsigned int closestGlyph = 0;
+    float closestDistance = std::numeric_limits<float>::max();
+
+    for( unsigned int i=0; i<glyphs.Count(); ++i )
+    {
+      float glyphX = positions[i].x + glyphs[i].width*0.5f;
+      float glyphY = positions[i].y + glyphs[i].height*0.5f;
+
+      float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
+
+      if( distanceToGlyph < closestDistance )
+      {
+        closestDistance = distanceToGlyph;
+        closestGlyph = i;
+      }
+    }
+
+    // TODO - Consider RTL languages
+    x = positions[closestGlyph].x + glyphs[closestGlyph].width;
+    y = 0.0f;
+
+    FontMetrics metrics;
+    TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
+    height = metrics.height; // TODO - Fix for multi-line
+  }
+
+  LogicalModelPtr mLogicalModel;
+  VisualModelPtr  mVisualModel;
+  DecoratorPtr    mDecorator;
+
+  State mState;
+
+  /**
+   * 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.
+
+  bool mDecoratorUpdated;
 };
 
 struct Controller::Impl
@@ -150,7 +311,7 @@ void Controller::EnableTextInput( DecoratorPtr decorator )
 {
   if( !mImpl->mTextInput )
   {
-    mImpl->mTextInput = new TextInput( decorator );
+    mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
   }
 }
 
@@ -162,11 +323,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 +335,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 )
@@ -272,7 +439,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
 
   if( GET_GLYPH_METRICS & operations )
   {
-    TextAbstraction::FontClient::Get().GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
+    mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
   }
 
   if( LAYOUT & operations )
@@ -308,8 +475,6 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
     viewUpdated = true;
   }
 
-  // TODO - process input events to move grab handle
-
   return viewUpdated;
 }
 
@@ -411,29 +576,33 @@ 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();
   }
 }
 
-void Controller::GrabHandleEvent( GrabHandleState state, float x )
+void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
 {
   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
 
   if( mImpl->mTextInput )
   {
     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
-    event.p1.mInt   = state;
+    event.p1.mUint  = state;
     event.p2.mFloat = x;
+    event.p3.mFloat = y;
+    mImpl->mTextInput->mEventQueue.push_back( event );
 
     RequestRelayout();
   }