Modify the cursor position.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index 9d53ae6..bd66860 100644 (file)
@@ -33,7 +33,7 @@ namespace
 {
 
 #if defined(DEBUG_ENABLED)
-  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
+  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
 /**
@@ -58,8 +58,6 @@ struct GlyphMetrics
   float xBearing;   ///< The x bearing of the first glyph.
 };
 
-const std::string EMPTY_STRING("");
-
 } // namespace
 
 namespace Dali
@@ -78,20 +76,20 @@ namespace Text
  * @param[in] numberOfGlyphs The number of glyphs.
  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
  * @param[in] visualModel The visual model.
- * @param[in] fontClient The font client.
+ * @param[in] metrics Used to access metrics from FontClient.
  */
 void GetGlyphsMetrics( GlyphIndex glyphIndex,
                        Length numberOfGlyphs,
                        GlyphMetrics& glyphMetrics,
-                       VisualModelPtr visualModel,
-                       TextAbstraction::FontClient& fontClient )
+                       VisualModelPtr& visualModel,
+                       MetricsPtr& metrics )
 {
   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
 
   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
 
   Text::FontMetrics fontMetrics;
-  fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+  metrics->GetFontMetrics( firstGlyph.fontId, fontMetrics );
 
   glyphMetrics.fontHeight = fontMetrics.height;
   glyphMetrics.advance = firstGlyph.advance;
@@ -279,6 +277,8 @@ bool Controller::Impl::ProcessInputEvents()
 
 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
 {
+  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
+
   // Calculate the operations to be done.
   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
 
@@ -423,7 +423,7 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
   if( GET_GLYPH_METRICS & operations )
   {
     GlyphInfo* glyphsBuffer = glyphs.Begin();
-    mFontClient.GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
+    mMetrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
 
     // Update the width and advance of all new paragraph characters.
     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
@@ -436,12 +436,34 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
       glyph.advance = 0.f;
     }
   }
+
+  if( mEventData &&
+      mEventData->mPreEditFlag &&
+      ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
+  {
+    // Add the underline for the pre-edit text.
+    const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+    const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+
+    const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
+    const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
+    const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
+    const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
+
+    GlyphRun underlineRun;
+    underlineRun.glyphIndex = glyphStart;
+    underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
+
+    // TODO: At the moment the underline runs are only for pre-edit.
+    mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+  }
 }
 
 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
 {
   if( mFontDefaults )
   {
+    DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::GetDefaultFonts font family(%s)\n", mFontDefaults->mFontDescription.family.c_str() );
     FontRun fontRun;
     fontRun.characterRun.characterIndex = 0;
     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
@@ -457,8 +479,8 @@ float Controller::Impl::GetDefaultFontLineHeight()
   FontId defaultFontId = 0u;
   if( NULL == mFontDefaults )
   {
-    defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
-                                           EMPTY_STRING );
+    TextAbstraction::FontDescription fontDescription;
+    defaultFontId = mFontClient.GetFontId( fontDescription );
   }
   else
   {
@@ -466,7 +488,7 @@ float Controller::Impl::GetDefaultFontLineHeight()
   }
 
   Text::FontMetrics fontMetrics;
-  mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+  mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
 
   return( fontMetrics.ascender - fontMetrics.descender );
 }
@@ -516,13 +538,16 @@ void Controller::Impl::OnTapEvent( const Event& event )
 
     if( 1u == tapCount )
     {
-      if( ! IsShowingPlaceholderText() )
+      if( IsShowingRealText() )
       {
         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
 
         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
                                                                     yPosition );
+
+        // When the cursor position is changing, delay cursor blinking
+        mEventData->mDecorator->DelayCursorBlink();
       }
       else
       {
@@ -852,12 +877,14 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete
     return;
   }
 
+  const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
+
   //Get start and end position of selection
-  uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
-  uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
+  uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
+  uint32_t lengthOfSelectedText =  ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
 
   // Validate the start and end selection points
-  if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
+  if(  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() )
   {
     //Get text as a UTF8 string
     Vector<Character>& utf32Characters = mLogicalModel->mText;
@@ -873,7 +900,7 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete
       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
       currentText.Erase( first, last );
     }
-    mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
+    mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
     mEventData->mScrollAfterDelete = true;
     mEventData->mDecoratorUpdated = true;
   }
@@ -946,8 +973,16 @@ void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart
   const LineRun& firstLine = *lines.Begin();
   const float height = firstLine.ascender + -firstLine.descender;
 
+  const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
+  const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
+  const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
+
   // Swap the indices if the start is greater than the end.
-  const bool indicesSwapped = ( selectionStart > selectionEnd );
+  const bool indicesSwapped = selectionStart > selectionEnd;
+
+  // Tell the decorator to flip the selection handles if needed.
+  mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
+
   if( indicesSwapped )
   {
     std::swap( selectionStart, selectionEnd );
@@ -967,9 +1002,6 @@ void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart
   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
 
-  // Tell the decorator to swap the selection handles if needed.
-  mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
-
   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
 
   // Traverse the glyphs.
@@ -1053,6 +1085,9 @@ void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart
 
   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
 
+  // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
+  mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
+
   // Set the flag to update the decorator.
   mEventData->mDecoratorUpdated = true;
 }
@@ -1317,10 +1352,25 @@ LineIndex Controller::Impl::GetClosestLine( float y ) const
 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
 {
   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
+  DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
+
+  if ( mLogicalModel->mText.Count() == 0 )
+  {
+    return;  // if model empty
+  }
+
   if( hitCharacter >= mLogicalModel->mText.Count() )
   {
-    // Selection out of bounds.
-    return;
+    // Closest hit character is the last character.
+    if ( hitCharacter ==  mLogicalModel->mText.Count() )
+    {
+      hitCharacter--; //Hit character index set to last character in logical model
+    }
+    else
+    {
+      // hitCharacter is out of bounds
+      return;
+    }
   }
 
   startIndex = hitCharacter;
@@ -1352,6 +1402,8 @@ void Controller::Impl::FindSelectionIndices( float visualX, float visualY, Chara
 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
                                                         float visualY )
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
+
   if( NULL == mEventData )
   {
     // Nothing to do if there is no text input.
@@ -1418,7 +1470,7 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
                       numberOfGlyphs,
                       glyphMetrics,
                       mVisualModel,
-                      mFontClient );
+                      mMetrics );
 
     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
 
@@ -1455,6 +1507,8 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
 
+  DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
+
   return logicalIndex;
 }
 
@@ -1473,7 +1527,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
     cursorInfo.lineHeight = GetDefaultFontLineHeight();
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
 
-    cursorInfo.primaryPosition.x = 1.f;
+    cursorInfo.primaryPosition.x = 0.f;
     cursorInfo.primaryPosition.y = 0.f;
 
     // Nothing else to do.
@@ -1555,7 +1609,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
                     primaryNumberOfGlyphs,
                     glyphMetrics,
                     mVisualModel,
-                    mFontClient );
+                    mMetrics );
 
   // Whether to add the glyph's advance to the cursor position.
   // i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
@@ -1645,7 +1699,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
                       secondaryNumberOfGlyphs,
                       glyphMetrics,
                       mVisualModel,
-                      mFontClient );
+                      mMetrics );
 
     // Set the secondary cursor's position.
     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
@@ -1709,7 +1763,7 @@ void Controller::Impl::UpdateCursorPosition()
     return;
   }
 
-  if( IsShowingPlaceholderText() )
+  if( IsShowingPlaceholderText() || ( 0u == mLogicalModel->mText.Count() ) )
   {
     // Do not want to use the place-holder text to set the cursor position.
 
@@ -1722,8 +1776,8 @@ void Controller::Impl::UpdateCursorPosition()
     FontId defaultFontId = 0u;
     if( NULL == mFontDefaults )
     {
-      defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
-                                             EMPTY_STRING );
+      TextAbstraction::FontDescription fontDescription;
+      defaultFontId = mFontClient.GetFontId( fontDescription );
     }
     else
     {
@@ -1731,7 +1785,7 @@ void Controller::Impl::UpdateCursorPosition()
     }
 
     Text::FontMetrics fontMetrics;
-    mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+    mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
 
     lineHeight = fontMetrics.ascender - fontMetrics.descender;
 
@@ -1742,7 +1796,7 @@ void Controller::Impl::UpdateCursorPosition()
     {
       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
       {
-        cursorPosition.x = 1.f;
+        cursorPosition.x = 0.f;
         break;
       }
       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
@@ -1752,7 +1806,7 @@ void Controller::Impl::UpdateCursorPosition()
       }
       case LayoutEngine::HORIZONTAL_ALIGN_END:
       {
-        cursorPosition.x = mVisualModel->mControlSize.width;
+        cursorPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
         break;
       }
     }
@@ -1954,8 +2008,9 @@ void Controller::Impl::ScrollTextToMatchCursor()
   }
 
   // Set which cursors are active according the state.
-  if( ( EventData::EDITING == mEventData->mState ) ||
-      ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
+  if( ( EventData::EDITING == mEventData->mState )                  ||
+      ( EventData::EDITING_WITH_POPUP == mEventData->mState )       ||
+      ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
   {
     if( cursorInfo.isSecondaryCursor )