Merge "Fix for text highlight." into devel/master
authorPaul Wisbey <p.wisbey@samsung.com>
Fri, 17 Jul 2015 13:48:14 +0000 (06:48 -0700)
committerGerrit Code Review <gerrit@review.vlan103.tizen.org>
Fri, 17 Jul 2015 13:48:14 +0000 (06:48 -0700)
1  2 
dali-toolkit/internal/text/text-controller-impl.cpp

@@@ -396,8 -396,7 +396,7 @@@ void Controller::Impl::UpdateModel( Ope
        // There is no right to left characters. Clear the directions vector.
        mLogicalModel->mCharacterDirections.Clear();
      }
-    }
+   }
  
    Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
    Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
@@@ -726,7 -725,7 +725,7 @@@ void Controller::Impl::OnHandleEvent( c
        Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
  
        // Position the grag handle close to either the left or right edge.
 -      position.x = scrollRightDirection ? 0.f : mControlSize.width;
 +      position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
  
        // Get the new handle position.
        // The grab handle's position is in decorator coords. Need to transforms to text coords.
        Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
  
        // Position the selection handle close to either the left or right edge.
 -      position.x = scrollRightDirection ? 0.f : mControlSize.width;
 +      position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
  
        // Get the new handle position.
        // The selection handle's position is in decorator coords. Need to transforms to text coords.
@@@ -923,34 -922,103 +922,103 @@@ void Controller::Impl::RepositionSelect
    const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
    const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
    const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+   const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin();
+   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
  
    // TODO: Better algorithm to create the highlight box.
    // TODO: Multi-line.
  
+   // Get the height of the line.
    const Vector<LineRun>& lines = mVisualModel->mLines;
    const LineRun& firstLine = *lines.Begin();
    const float height = firstLine.ascender + -firstLine.descender;
  
+   // Swap the indices if the start is greater than the end.
    const bool indicesSwapped = ( selectionStart > selectionEnd );
    if( indicesSwapped )
    {
      std::swap( selectionStart, selectionEnd );
    }
  
+   // Get the indices to the first and last selected glyphs.
+   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
    const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
-   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) );
-   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
+   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
+   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
  
+   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
+   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) );
+   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
+   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.
    for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
    {
-     // TODO: Fix the LATIN ligatures. i.e ff, fi, etc...
      const GlyphInfo& glyph = *( glyphsBuffer + index );
      const Vector2& position = *( positionsBuffer + index );
  
+     if( splitStartGlyph )
+     {
+       // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
+       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
+       // Get the direction of the character.
+       CharacterDirection isCurrentRightToLeft = false;
+       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+       {
+         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
+       }
+       // The end point could be in the middle of the ligature.
+       // Calculate the number of characters selected.
+       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
+       const float xPosition = position.x - glyph.xBearing + offset.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
+       mEventData->mDecorator->AddHighlight( xPosition,
+                                             offset.y,
+                                             xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
+                                             height );
+       splitStartGlyph = false;
+       continue;
+     }
+     if( splitEndGlyph && ( index == glyphEnd ) )
+     {
+       // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
+       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
+       // Get the direction of the character.
+       CharacterDirection isCurrentRightToLeft = false;
+       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+       {
+         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
+       }
+       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
+       const float xPosition = position.x - glyph.xBearing + offset.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
+       mEventData->mDecorator->AddHighlight( xPosition,
+                                             offset.y,
+                                             xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
+                                             height );
+       splitEndGlyph = false;
+       continue;
+     }
      const float xPosition = position.x - glyph.xBearing + offset.x;
      mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
    }
@@@ -1339,8 -1407,8 +1407,8 @@@ CharacterIndex Controller::Impl::GetClo
  
      const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
  
-     // Prevents to jump the whole Latin ligatures like fi, ff, ...
-     const Length numberOfCharactersInLigature = ( TextAbstraction::LATIN == script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
+     // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ...
+     const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
      const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
  
      for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
@@@ -1414,7 -1482,7 +1482,7 @@@ void Controller::Impl::GetCursorPositio
    }
  
    // Get the line where the character is laid-out.
-   const LineRun* modelLines = mVisualModel->mLines.Begin();
+   const LineRun* const modelLines = mVisualModel->mLines.Begin();
  
    const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
    const LineRun& line = *( modelLines + lineIndex );
      }
    }
  
+   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+   const CharacterIndex* const glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin();
+   const Vector2* const glyphPositionsBuffer = mVisualModel->mGlyphPositions.Begin();
    // Convert the cursor position into the glyph position.
-   const GlyphIndex primaryGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + index );
-   const Length primaryNumberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + index );
-   const Length primaryNumberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() + primaryGlyphIndex );
+   const GlyphIndex primaryGlyphIndex = *( charactersToGlyphBuffer + index );
+   const Length primaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
+   const Length primaryNumberOfCharacters = *( charactersPerGlyphBuffer + primaryGlyphIndex );
  
    // Get the metrics for the group of glyphs.
    GlyphMetrics glyphMetrics;
                      mVisualModel,
                      mFontClient );
  
-   float glyphAdvance = 0.f;
+   // 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,
+   //     if the logical cursor is one, the position is the position of the first glyph and the advance is added.
+   // A 'truth table' was build and an online Karnaugh map tool was used to simplify the logic.
+   //
+   // FLCP A
+   // ------
+   // 0000 1
+   // 0001 1
+   // 0010 0
+   // 0011 0
+   // 0100 1
+   // 0101 0
+   // 0110 1
+   // 0111 0
+   // 1000 0
+   // 1001 x
+   // 1010 x
+   // 1011 1
+   // 1100 x
+   // 1101 x
+   // 1110 x
+   // 1111 x
+   //
+   // Where F -> isFirstPosition
+   //       L -> isLastPosition
+   //       C -> isCurrentRightToLeft
+   //       P -> isRightToLeftParagraph
+   //       A -> Whether to add the glyph's advance.
+   const bool addGlyphAdvance = ( ( isLastPosition && !isRightToLeftParagraph ) ||
+                                  ( isFirstPosition && isRightToLeftParagraph ) ||
+                                  ( !isFirstPosition && !isLastPosition && !isCurrentRightToLeft ) );
+   float glyphAdvance = addGlyphAdvance ? glyphMetrics.advance : 0.f;
    if( !isLastPosition &&
        ( primaryNumberOfCharacters > 1u ) )
    {
-     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + primaryGlyphIndex );
-     glyphAdvance = static_cast<float>( 1u + characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
-   }
-   else
-   {
-     glyphAdvance = glyphMetrics.advance;
+     const CharacterIndex firstIndex = *( glyphsToCharactersBuffer + primaryGlyphIndex );
+     bool isCurrentRightToLeft = false;
+     if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+     {
+       isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + index );
+     }
+     Length numberOfGlyphAdvance = ( isFirstPosition ? 0u : 1u ) + characterIndex - firstIndex;
+     if( isCurrentRightToLeft )
+     {
+       numberOfGlyphAdvance = primaryNumberOfCharacters - numberOfGlyphAdvance;
+     }
+     glyphAdvance = static_cast<float>( numberOfGlyphAdvance ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
    }
  
    // Get the glyph position and x bearing.
-   const Vector2& primaryPosition = *( mVisualModel->mGlyphPositions.Begin() + primaryGlyphIndex );
+   const Vector2& primaryPosition = *( glyphPositionsBuffer + primaryGlyphIndex );
  
    // Set the primary cursor's height.
    cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
  
    // Set the primary cursor's position.
-   if( isLastPosition )
-   {
-     cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
-   }
-   else
-   {
-     cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + ( ( ( isFirstPosition && !isCurrentRightToLeft ) || ( !isFirstPosition && isCurrentRightToLeft ) ) ? 0.f : glyphAdvance );
-   }
+   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + glyphAdvance;
    cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
  
    // Calculate the secondary cursor.
        index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
      }
  
-     const GlyphIndex secondaryGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + index );
-     const Length secondaryNumberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + index );
+     const GlyphIndex secondaryGlyphIndex = *( charactersToGlyphBuffer + index );
+     const Length secondaryNumberOfGlyphs = *( glyphsPerCharacterBuffer + index );
  
-     const Vector2& secondaryPosition = *( mVisualModel->mGlyphPositions.Begin() + index );
+     const Vector2& secondaryPosition = *( glyphPositionsBuffer + secondaryGlyphIndex );
  
      GetGlyphsMetrics( secondaryGlyphIndex,
                        secondaryNumberOfGlyphs,
@@@ -1537,25 -1648,27 +1648,27 @@@ CharacterIndex Controller::Impl::Calcul
  
    CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
  
-   const Script script = mLogicalModel->GetScript( index );
-   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
-   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
  
-   Length numberOfCharacters = 0u;
-   if( TextAbstraction::LATIN == script )
+   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
+   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
+   if( numberOfCharacters > 1u )
    {
-     // Prevents to jump the whole Latin ligatures like fi, ff, ...
-     numberOfCharacters = 1u;
+     const Script script = mLogicalModel->GetScript( index );
+     if( HasLigatureMustBreak( script ) )
+     {
+       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ,  ...
+       numberOfCharacters = 1u;
+     }
    }
    else
    {
-     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
-     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
      while( 0u == numberOfCharacters )
      {
-       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
        ++glyphIndex;
+       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
      }
    }
  
@@@ -1619,12 -1732,12 +1732,12 @@@ void Controller::Impl::UpdateCursorPosi
        }
        case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
        {
 -        cursorPosition.x = floor( 0.5f * mControlSize.width );
 +        cursorPosition.x = floor( 0.5f * mVisualModel->mControlSize.width );
          break;
        }
        case LayoutEngine::HORIZONTAL_ALIGN_END:
        {
 -        cursorPosition.x = mControlSize.width;
 +        cursorPosition.x = mVisualModel->mControlSize.width;
          break;
        }
      }
        }
        case LayoutEngine::VERTICAL_ALIGN_CENTER:
        {
 -        cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
 +        cursorPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - lineHeight ) );
          break;
        }
        case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
        {
 -        cursorPosition.y = mControlSize.height - lineHeight;
 +        cursorPosition.y = mVisualModel->mControlSize.height - lineHeight;
          break;
        }
      }
@@@ -1728,9 -1841,9 +1841,9 @@@ void Controller::Impl::UpdateSelectionH
  void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
  {
    // Clamp between -space & 0 (and the text alignment).
 -  if( actualSize.width > mControlSize.width )
 +  if( actualSize.width > mVisualModel->mControlSize.width )
    {
 -    const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
 +    const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
      mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
      mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
  
  void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
  {
    // Clamp between -space & 0 (and the text alignment).
 -  if( actualSize.height > mControlSize.height )
 +  if( actualSize.height > mVisualModel->mControlSize.height )
    {
 -    const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
 +    const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
      mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
      mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
  
@@@ -1769,9 -1882,9 +1882,9 @@@ void Controller::Impl::ScrollToMakePosi
      mEventData->mScrollPosition.x += offset.x;
      updateDecorator = true;
    }
 -  else if( position.x > mControlSize.width )
 +  else if( position.x > mVisualModel->mControlSize.width )
    {
 -    offset.x = mControlSize.width - position.x;
 +    offset.x = mVisualModel->mControlSize.width - position.x;
      mEventData->mScrollPosition.x += offset.x;
      updateDecorator = true;
    }