Merge "If the height is small even if scrolling is enabled, it should be elide."...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
old mode 100644 (file)
new mode 100755 (executable)
index 12f20e4..f97a8ea
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 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.
@@ -70,7 +70,8 @@ struct LineLayout
     extraWidth( 0.f ),
     wsLengthEndOfLine( 0.f ),
     ascender( 0.f ),
-    descender( MAX_FLOAT )
+    descender( MAX_FLOAT ),
+    lineSpacing( 0.f )
   {}
 
   ~LineLayout()
@@ -100,6 +101,7 @@ struct LineLayout
   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
   float          ascender;           ///< The maximum ascender of all fonts in the line.
   float          descender;          ///< The minimum descender of all fonts in the line.
+  float          lineSpacing;        ///< The line spacing
 };
 
 struct Engine::Impl
@@ -107,7 +109,8 @@ struct Engine::Impl
   Impl()
   : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
     mCursorWidth( CURSOR_WIDTH ),
-    mDefaultLineSpacing( LINE_SPACING )
+    mDefaultLineSpacing( LINE_SPACING ),
+    mPreviousCharacterExtraWidth( 0.0f )
   {
   }
 
@@ -133,6 +136,9 @@ struct Engine::Impl
     {
       lineLayout.descender = fontMetrics.descender;
     }
+
+    // set the line spacing
+    lineLayout.lineSpacing = mDefaultLineSpacing;
   }
 
   /**
@@ -198,7 +204,7 @@ struct Engine::Impl
     LineLayout tmpLineLayout;
 
     const bool isMultiline = mLayout == MULTI_LINE_BOX;
-    const bool isWordLaidOut = parameters.lineWrapMode == Layout::LineWrap::WORD;
+    const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD;
 
     // The last glyph to be laid-out.
     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
@@ -275,9 +281,6 @@ struct Engine::Impl
       // Get the line break info for the current character.
       const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
 
-      // Get the word break info for the current character.
-      const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
-
       // Increase the number of characters.
       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
 
@@ -340,6 +343,7 @@ struct Engine::Impl
 
             const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
             tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
+            tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
           }
         }
         else
@@ -353,6 +357,7 @@ struct Engine::Impl
 
               const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
+              tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
             }
             else // LTR
             {
@@ -380,6 +385,7 @@ struct Engine::Impl
 
               const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
+              tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
             }
           }
         }
@@ -388,8 +394,19 @@ struct Engine::Impl
         tmpLineLayout.wsLengthEndOfLine = 0.f;
       }
 
+      // If calculation is end but wsLengthEndOfLine is exist, it means end of text is space.
+      // Merge remained length.
+      if ( !parameters.ignoreSpaceAfterText && glyphIndex == lastGlyphOfParagraphPlusOne-1 && tmpLineLayout.wsLengthEndOfLine > 0 )
+      {
+        tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine;
+        tmpLineLayout.wsLengthEndOfLine = 0u;
+      }
+
+      // Save the current extra width to compare with the next one
+      mPreviousCharacterExtraWidth = tmpExtraWidth;
+
       // Check if the accumulated length fits in the width of the box.
-      if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
+      if( ( completelyFill || isMultiline )  && !(parameters.ignoreSpaceAfterText && isWhiteSpace) &&
           ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
       {
         // Current word does not fit in the box's width.
@@ -446,7 +463,7 @@ struct Engine::Impl
       }
 
       if( isMultiline &&
-          ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
+          ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
       {
         oneWordLaidOut = isWordLaidOut;
         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid-out\n" );
@@ -470,6 +487,7 @@ struct Engine::Impl
 
   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
                           Length numberOfGlyphs,
+                          float outlineWidth,
                           Vector2* glyphPositionsBuffer )
   {
     // Traverse the glyphs and set the positions.
@@ -479,7 +497,8 @@ struct Engine::Impl
     // so the penX position needs to be moved to the right.
 
     const GlyphInfo& glyph = *glyphsBuffer;
-    float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
+    float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing + outlineWidth : outlineWidth;
+
 
     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
     {
@@ -536,6 +555,7 @@ struct Engine::Impl
    * @param[in,out] numberOfLines The number of laid-out lines.
    * @param[in] penY The vertical layout position.
    * @param[in] currentParagraphDirection The current paragraph's direction.
+   * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
    *
    * return Whether the line is ellipsized.
    */
@@ -546,14 +566,17 @@ struct Engine::Impl
                      Vector2* glyphPositionsBuffer,
                      Length& numberOfLines,
                      float penY,
-                     CharacterDirection currentParagraphDirection )
+                     CharacterDirection currentParagraphDirection,
+                     bool& isAutoScrollEnabled )
   {
-    const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
-                            ( ( mLayout == SINGLE_LINE_BOX ) &&
-                              ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
+    const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
+                                                ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
+                                                  ( ( mLayout == SINGLE_LINE_BOX ) &&
+                                                  ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
 
     if( ellipsis )
     {
+      isAutoScrollEnabled = false;
       // Do not layout more lines if ellipsis is enabled.
 
       // The last line needs to be completely filled with characters.
@@ -566,7 +589,7 @@ struct Engine::Impl
         // Get the last line and layout it again with the 'completelyFill' flag to true.
         lineRun = linesBuffer + ( numberOfLines - 1u );
 
-        penY -= layout.ascender - lineRun->descender;
+        penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
 
         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
       }
@@ -599,11 +622,12 @@ struct Engine::Impl
       layoutSize.width = layoutParameters.boundingBox.width;
       if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
       {
-        layoutSize.height += ( lineRun->ascender + -lineRun->descender );
+        layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
       }
 
       SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
                          ellipsisLayout.numberOfGlyphs,
+                         layoutParameters.outlineWidth,
                          glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
     }
 
@@ -636,6 +660,8 @@ struct Engine::Impl
     lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
     lineRun.characterRun.characterIndex = layout.characterIndex;
     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
+    lineRun.lineSpacing = mDefaultLineSpacing;
+
     if( isLastLine && !layoutParameters.isLastNewParagraph )
     {
       const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
@@ -666,7 +692,7 @@ struct Engine::Impl
       layoutSize.width = lineRun.width;
     }
 
-    layoutSize.height += ( lineRun.ascender + -lineRun.descender );
+    layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
   }
 
   /**
@@ -706,8 +732,9 @@ struct Engine::Impl
     lineRun.alignmentOffset = 0.f;
     lineRun.direction = !RTL;
     lineRun.ellipsis = false;
+    lineRun.lineSpacing = mDefaultLineSpacing;
 
-    layoutSize.height += ( lineRun.ascender + -lineRun.descender );
+    layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
   }
 
   /**
@@ -731,7 +758,7 @@ struct Engine::Impl
         layoutSize.width = line.width;
       }
 
-      layoutSize.height += ( line.ascender + -line.descender );
+      layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
     }
   }
 
@@ -768,7 +795,8 @@ struct Engine::Impl
                    Vector<Vector2>& glyphPositions,
                    Vector<LineRun>& lines,
                    Size& layoutSize,
-                   bool elideTextEnabled )
+                   bool elideTextEnabled,
+                   bool& isAutoScrollEnabled )
   {
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
@@ -902,7 +930,8 @@ struct Engine::Impl
                                  glyphPositionsBuffer,
                                  numberOfLines,
                                  penY,
-                                 currentParagraphDirection );
+                                 currentParagraphDirection,
+                                 isAutoScrollEnabled );
       }
 
       if( ellipsis )
@@ -963,10 +992,11 @@ struct Engine::Impl
         // Sets the positions of the glyphs.
         SetGlyphPositions( layoutParameters.glyphsBuffer + index,
                            layout.numberOfGlyphs,
+                           layoutParameters.outlineWidth,
                            glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
 
         // Updates the vertical pen's position.
-        penY += -layout.descender;
+        penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
 
         // Increase the glyph index.
         index = nextIndex;
@@ -1043,7 +1073,7 @@ struct Engine::Impl
       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
 
-      float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
+      float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing - layoutParameters.outlineWidth : -layoutParameters.outlineWidth;
 
       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
 
@@ -1078,9 +1108,11 @@ struct Engine::Impl
   void Align( const Size& size,
               CharacterIndex startIndex,
               Length numberOfCharacters,
-              HorizontalAlignment horizontalAlignment,
+              Text::HorizontalAlignment::Type horizontalAlignment,
               Vector<LineRun>& lines,
-              float& alignmentOffset )
+              float& alignmentOffset,
+              Dali::LayoutDirection::Type layoutDirection,
+              bool matchSystemLanguageDirection )
   {
     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
 
@@ -1108,7 +1140,9 @@ struct Engine::Impl
       // the box width, line length, and the paragraph's direction.
       CalculateHorizontalAlignment( size.width,
                                     horizontalAlignment,
-                                    line );
+                                    line,
+                                    layoutDirection,
+                                    matchSystemLanguageDirection );
 
       // Updates the alignment offset.
       alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
@@ -1116,56 +1150,55 @@ struct Engine::Impl
   }
 
   void CalculateHorizontalAlignment( float boxWidth,
-                                     HorizontalAlignment horizontalAlignment,
-                                     LineRun& line )
+                                     HorizontalAlignment::Type horizontalAlignment,
+                                     LineRun& line,
+                                     Dali::LayoutDirection::Type layoutDirection,
+                                     bool matchSystemLanguageDirection )
   {
     line.alignmentOffset = 0.f;
-    const bool isRTL = RTL == line.direction;
+    const bool isLineRTL = RTL == line.direction;
+    // Whether to swap the alignment.
+    // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
+    bool isLayoutRTL = isLineRTL;
     float lineLength = line.width;
 
-    HorizontalAlignment alignment = horizontalAlignment;
-    if( isRTL )
+    // match align for system language direction
+    if( matchSystemLanguageDirection )
     {
       // Swap the alignment type if the line is right to left.
-      switch( alignment )
-      {
-        case HORIZONTAL_ALIGN_BEGIN:
-        {
-          alignment = HORIZONTAL_ALIGN_END;
-          break;
-        }
-        case HORIZONTAL_ALIGN_CENTER:
-        {
-          // Nothing to do.
-          break;
-        }
-        case HORIZONTAL_ALIGN_END:
-        {
-          alignment = HORIZONTAL_ALIGN_BEGIN;
-          break;
-        }
-      }
+      isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
     }
-
     // Calculate the horizontal line offset.
-    switch( alignment )
+    switch( horizontalAlignment )
     {
-      case HORIZONTAL_ALIGN_BEGIN:
+      case HorizontalAlignment::BEGIN:
       {
-        line.alignmentOffset = 0.f;
+        if( isLayoutRTL )
+        {
+          if( isLineRTL )
+          {
+            lineLength += line.extraLength;
+          }
 
-        if( isRTL )
+          line.alignmentOffset = boxWidth - lineLength;
+        }
+        else
         {
-          // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
-          line.alignmentOffset -= line.extraLength;
+          line.alignmentOffset = 0.f;
+
+          if( isLineRTL )
+          {
+            // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
+            line.alignmentOffset -= line.extraLength;
+          }
         }
         break;
       }
-      case HORIZONTAL_ALIGN_CENTER:
+      case HorizontalAlignment::CENTER:
       {
         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
 
-        if( isRTL )
+        if( isLineRTL )
         {
           line.alignmentOffset -= line.extraLength;
         }
@@ -1173,14 +1206,27 @@ struct Engine::Impl
         line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
         break;
       }
-      case HORIZONTAL_ALIGN_END:
+      case HorizontalAlignment::END:
       {
-        if( isRTL )
+        if( isLayoutRTL )
         {
-          lineLength += line.extraLength;
+          line.alignmentOffset = 0.f;
+
+          if( isLineRTL )
+          {
+            // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
+            line.alignmentOffset -= line.extraLength;
+          }
         }
+        else
+        {
+          if( isLineRTL )
+          {
+            lineLength += line.extraLength;
+          }
 
-        line.alignmentOffset = boxWidth - lineLength;
+          line.alignmentOffset = boxWidth - lineLength;
+        }
         break;
       }
     }
@@ -1199,11 +1245,13 @@ struct Engine::Impl
     line.alignmentOffset = 0.f;
     line.direction = !RTL;
     line.ellipsis = false;
+    line.lineSpacing = mDefaultLineSpacing;
   }
 
   Type mLayout;
   float mCursorWidth;
   float mDefaultLineSpacing;
+  float mPreviousCharacterExtraWidth;
 
   IntrusivePtr<Metrics> mMetrics;
 };
@@ -1249,13 +1297,15 @@ bool Engine::LayoutText( const Parameters& layoutParameters,
                          Vector<Vector2>& glyphPositions,
                          Vector<LineRun>& lines,
                          Size& layoutSize,
-                         bool elideTextEnabled )
+                         bool elideTextEnabled,
+                         bool& isAutoScrollEnabled )
 {
   return mImpl->LayoutText( layoutParameters,
                             glyphPositions,
                             lines,
                             layoutSize,
-                            elideTextEnabled );
+                            elideTextEnabled,
+                            isAutoScrollEnabled );
 }
 
 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
@@ -1272,16 +1322,20 @@ void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
 void Engine::Align( const Size& size,
                     CharacterIndex startIndex,
                     Length numberOfCharacters,
-                    Layout::HorizontalAlignment horizontalAlignment,
+                    Text::HorizontalAlignment::Type horizontalAlignment,
                     Vector<LineRun>& lines,
-                    float& alignmentOffset )
+                    float& alignmentOffset,
+                    Dali::LayoutDirection::Type layoutDirection,
+                    bool matchSystemLanguageDirection )
 {
   mImpl->Align( size,
                 startIndex,
                 numberOfCharacters,
                 horizontalAlignment,
                 lines,
-                alignmentOffset );
+                alignmentOffset,
+                layoutDirection,
+                matchSystemLanguageDirection );
 }
 
 void Engine::SetDefaultLineSpacing( float lineSpacing )