Merge "Support line spacing & min line size in texteditor" into devel/master
authorBowon Ryu <bowon.ryu@samsung.com>
Fri, 20 Aug 2021 02:21:37 +0000 (02:21 +0000)
committerGerrit Code Review <gerrit@review>
Fri, 20 Aug 2021 02:21:37 +0000 (02:21 +0000)
12 files changed:
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/text/cursor-helper-functions.cpp
dali-toolkit/internal/text/layouts/layout-engine.cpp
dali-toolkit/internal/text/line-run.h
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-selection-handle-controller.cpp
dali-toolkit/internal/text/text-view.cpp

index 9b1d0ff..5003bea 100644 (file)
@@ -1005,6 +1005,11 @@ int UtcDaliTextEditorSetPropertyP(void)
   application.SendNotification();
   application.Render();
 
+  // Check the line size property
+  DALI_TEST_EQUALS( editor.GetProperty<float>( DevelTextEditor::Property::MIN_LINE_SIZE ), 0.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+  editor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 50.f );
+  DALI_TEST_EQUALS( editor.GetProperty<float>( DevelTextEditor::Property::MIN_LINE_SIZE ), 50.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
   END_TEST;
 }
 
@@ -3975,4 +3980,54 @@ int UtcDaliToolkitTextEditorEllipsisPositionProperty(void)
   DALI_TEST_EQUALS( textEditor.GetProperty< int >( DevelTextEditor::Property::ELLIPSIS_POSITION ), static_cast< int >( Toolkit::DevelText::EllipsisPosition::END ), TEST_LOCATION );
 
   END_TEST;
+}
+
+int UtcDaliTextEditorLineSpacing(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorLineSpacing ");
+
+  TextEditor textEditor = TextEditor::New();
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 400.0f, 400.f ) );
+  application.GetScene().Add( textEditor );
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Line #1\nLine #2\nLine #3" );
+  textEditor.SetProperty( DevelTextEditor::Property::LINE_SPACING, 0 );
+
+  Vector3 sizeBefore = textEditor.GetNaturalSize();
+
+  textEditor.SetProperty( DevelTextEditor::Property::LINE_SPACING, 20 );
+
+  //add 20 for each line  20 * 3
+  DALI_TEST_EQUALS(sizeBefore.height + 60.0f, textEditor.GetNaturalSize().height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliTextEditorMinLineSize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorMinLineSize ");
+
+  TextEditor textEditor = TextEditor::New();
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 400.0f, 400.f ) );
+  application.GetScene().Add( textEditor );
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "Line #1\nLine #2\nLine #3" );
+  textEditor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 0 );
+
+  Vector3 sizeBefore = textEditor.GetNaturalSize();
+
+  textEditor.SetProperty( DevelTextEditor::Property::MIN_LINE_SIZE, 60 );
+
+  DALI_TEST_NOT_EQUALS( sizeBefore, textEditor.GetNaturalSize(), 0.0f, TEST_LOCATION);
+
+  //60 * 3 lines
+  DALI_TEST_EQUALS(180.0f, textEditor.GetNaturalSize().height, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index 2d14542..7a6442a 100644 (file)
@@ -267,6 +267,13 @@ enum Type
   * @see DevelText::EllipsisPosition
   */
   ELLIPSIS_POSITION,
+
+  /**
+   * @brief Sets the height of the line in points.
+   * @details Name "minLineSize", type Property::FLOAT.
+   * @note If the font size is larger than the line size, it works with the font size.
+   */
+  MIN_LINE_SIZE,
 };
 
 } // namespace Property
index 9609a3b..41d7126 100644 (file)
@@ -828,7 +828,7 @@ void Ellipsis(const RendererParameters& textParameters, TextAbstraction::TextRen
     {
       Length finalNumberOfGlyphs = 0u;
 
-      if((line.ascender - line.descender) > textLayoutArea.height)
+      if((GetLineHeight(line)) > textLayoutArea.height)
       {
         // The height of the line is bigger than the height of the text area.
         // Show the ellipsis glyph even if it doesn't fit in the text area.
@@ -1524,7 +1524,7 @@ Dali::Property::Array RenderForLastIndex(RendererParameters& textParameters)
     const LineRun& line = *(lines.Begin() + index);
     numberOfCharacters += line.characterRun.numberOfCharacters;
 
-    lineOffset = lineSize > 0.f ? lineSize : (line.ascender + -line.descender);
+    lineOffset = lineSize > 0.f ? lineSize : GetLineHeight(line);
     penY += lineOffset;
     if((penY + lineOffset) > boundingBox)
     {
index dd67a85..8dee30e 100644 (file)
@@ -151,6 +151,7 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "inputMethodSett
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "inputFilter",                          MAP,       INPUT_FILTER                        )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "ellipsis",                             BOOLEAN,   ELLIPSIS                            )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "ellipsisPosition",                     INTEGER,   ELLIPSIS_POSITION                   )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "minLineSize",                          FLOAT,     MIN_LINE_SIZE                       )
 
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "textChanged",        SIGNAL_TEXT_CHANGED       )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputStyleChanged",  SIGNAL_INPUT_STYLE_CHANGED)
@@ -491,13 +492,8 @@ void TextEditor::SetProperty(BaseObject* object, Property::Index index, const Pr
       }
       case Toolkit::TextEditor::Property::LINE_SPACING:
       {
-        // The line spacing isn't supported by the TextEditor. Since it's supported
-        // by the TextLabel for now it must be ignored. The property is being shadowed
-        // locally so its value isn't affected.
         const float lineSpacing = value.Get<float>();
-        impl.mLineSpacing       = lineSpacing;
-        // set it to 0.0 due to missing implementation
-        impl.mController->SetDefaultLineSpacing(0.0f);
+        impl.mController->SetDefaultLineSpacing(lineSpacing);
         impl.mRenderer.Reset();
         break;
       }
@@ -832,6 +828,15 @@ void TextEditor::SetProperty(BaseObject* object, Property::Index index, const Pr
         }
         break;
       }
+      case Toolkit::DevelTextEditor::Property::MIN_LINE_SIZE:
+      {
+        const float minLineSize = value.Get<float>();
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextEditor %p MIN_LINE_SIZE %f\n", impl.mController.Get(), minLineSize);
+
+        impl.mController->SetDefaultLineSize(minLineSize);
+        impl.mRenderer.Reset();
+        break;
+      }
     } // switch
   }   // texteditor
 }
@@ -1011,9 +1016,7 @@ Property::Value TextEditor::GetProperty(BaseObject* object, Property::Index inde
       }
       case Toolkit::TextEditor::Property::LINE_SPACING:
       {
-        // LINE_SPACING isn't implemented for the TextEditor. Returning
-        // only shadowed value, not the real one.
-        value = impl.mLineSpacing;
+        value = impl.mController->GetDefaultLineSpacing();
         break;
       }
       case Toolkit::TextEditor::Property::INPUT_LINE_SPACING:
@@ -1222,6 +1225,11 @@ Property::Value TextEditor::GetProperty(BaseObject* object, Property::Index inde
         value = impl.mController->GetEllipsisPosition();
         break;
       }
+      case Toolkit::DevelTextEditor::Property::MIN_LINE_SIZE:
+      {
+        value = impl.mController->GetDefaultLineSize();
+        break;
+      }
     } //switch
   }
 
@@ -2291,10 +2299,10 @@ bool TextEditor::AccessibleImpl::SetCursorOffset(size_t offset)
   return true;
 }
 
-Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset( size_t offset, Dali::Accessibility::TextBoundary boundary)
+Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset(size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self = Toolkit::TextEditor::DownCast(Self());
-  auto text = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
+  auto self     = Toolkit::TextEditor::DownCast(Self());
+  auto text     = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
   auto textSize = text.size();
 
   auto range = Dali::Accessibility::Range{};
@@ -2315,7 +2323,7 @@ Dali::Accessibility::Range TextEditor::AccessibleImpl::GetTextAtOffset( size_t o
     case Dali::Accessibility::TextBoundary::LINE:
     {
       auto textString = text.c_str();
-      auto breaks = std::vector<char>(textSize, 0);
+      auto breaks     = std::vector<char>(textSize, 0);
 
       if(boundary == Dali::Accessibility::TextBoundary::WORD)
       {
@@ -2390,8 +2398,8 @@ Dali::Accessibility::Range TextEditor::AccessibleImpl::GetRangeOfSelection(size_
     return {};
   }
 
-  auto self  = Toolkit::TextEditor::DownCast(Self());
-  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  auto        self       = Toolkit::TextEditor::DownCast(Self());
+  auto        controller = Dali::Toolkit::GetImpl(self).GetTextController();
   std::string value{};
   controller->RetrieveSelection(value);
   auto indices = controller->GetSelectionIndexes();
@@ -2474,7 +2482,7 @@ Dali::Accessibility::States TextEditor::AccessibleImpl::CalculateStates()
 {
   using namespace Dali::Accessibility;
 
-  auto states = DevelControl::AccessibleImpl::CalculateStates();
+  auto states              = DevelControl::AccessibleImpl::CalculateStates();
   states[State::EDITABLE]  = true;
   states[State::FOCUSABLE] = true;
 
@@ -2489,7 +2497,7 @@ Dali::Accessibility::States TextEditor::AccessibleImpl::CalculateStates()
 
 bool TextEditor::AccessibleImpl::InsertText(size_t startPosition, std::string text)
 {
-  auto self = Toolkit::TextEditor::DownCast(Self());
+  auto self         = Toolkit::TextEditor::DownCast(Self());
   auto insertedText = self.GetProperty(Toolkit::TextEditor::Property::TEXT).Get<std::string>();
 
   insertedText.insert(startPosition, text);
index 1df142c..c2d35d9 100644 (file)
@@ -155,9 +155,7 @@ LineIndex GetClosestLine(VisualModelPtr visualModel,
   {
     const LineRun& lineRun = *it;
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    totalHeight += lineRun.ascender - lineRun.descender;
+    totalHeight += GetLineHeight(lineRun);
 
     if(visualY < totalHeight)
     {
@@ -186,9 +184,7 @@ float CalculateLineOffset(const Vector<LineRun>& lines,
   {
     const LineRun& lineRun = *it;
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    offset += lineRun.ascender - lineRun.descender;
+    offset += GetLineHeight(lineRun);
   }
 
   return offset;
@@ -495,9 +491,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 newLineIndex);
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    cursorInfo.lineHeight = newLine.ascender - newLine.descender;
+    cursorInfo.lineHeight = GetLineHeight(newLine);
 
     // Set the primary cursor's height.
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
@@ -543,9 +537,7 @@ void GetCursorPosition(GetCursorPositionParameters& parameters,
     cursorInfo.lineOffset = CalculateLineOffset(parameters.visualModel->mLines,
                                                 lineIndex);
 
-    // The line height is the addition of the line ascender and the line descender.
-    // However, the line descender has a negative value, hence the subtraction.
-    cursorInfo.lineHeight = line.ascender - line.descender;
+    cursorInfo.lineHeight = GetLineHeight(line);
 
     // Calculate the primary cursor.
 
index b27f634..695d950 100644 (file)
@@ -36,6 +36,12 @@ namespace Toolkit
 {
 namespace Text
 {
+float GetLineHeight(const LineRun lineRun)
+{
+  // The line height is the addition of the line ascender, the line descender and the line spacing.
+  // However, the line descender has a negative value, hence the subtraction.
+  return lineRun.ascender - lineRun.descender + lineRun.lineSpacing;
+}
 namespace Layout
 {
 namespace
@@ -1275,7 +1281,7 @@ struct Engine::Impl
       layoutSize.width = layoutParameters.boundingBox.width;
       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
       {
-        layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
+        layoutSize.height += GetLineHeight(*lineRun);
       }
 
       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
@@ -1382,7 +1388,7 @@ struct Engine::Impl
       layoutSize.width = lineRun.width;
     }
 
-    layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
+    layoutSize.height += GetLineHeight(lineRun);
   }
 
   /**
@@ -1433,7 +1439,7 @@ struct Engine::Impl
 
     lineRun.lineSpacing += mDefaultLineSpacing;
 
-    layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
+    layoutSize.height += GetLineHeight(lineRun);
   }
 
   /**
@@ -1457,7 +1463,7 @@ struct Engine::Impl
         layoutSize.width = line.width;
       }
 
-      layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
+      layoutSize.height += GetLineHeight(line);
     }
   }
 
index ff826e6..52c47a1 100644 (file)
@@ -48,6 +48,13 @@ struct LineRun
   CharacterRun       characterRunForSecondHalfLine; ///< The initial character index and the number of characters of the run for the second half of line.
 };
 
+/**
+ * @brief Get the line height for the specified line run.
+ *
+ * @param[in] lineRun The line runs to get the height for.
+ */
+float GetLineHeight(const LineRun lineRun);
+
 } // namespace Text
 
 } // namespace Toolkit
index 2aafff7..f8aa7a5 100644 (file)
@@ -338,7 +338,7 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
     const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
 
     // Get the next hit 'y' point.
-    const float hitPointY = cursorInfo.lineOffset - 0.5f * (line.ascender - line.descender);
+    const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
 
     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
     bool matchedCharacter = false;
@@ -374,7 +374,7 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
 
       // Get the next hit 'y' point.
-      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * (line.ascender - line.descender);
+      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
       bool matchedCharacter = false;
index dce67ff..b058e26 100644 (file)
@@ -1219,12 +1219,31 @@ const std::string& Controller::GetDefaultOutlineProperties() const
   return EMPTY_STRING;
 }
 
+void Controller::RelayoutForNewLineSize()
+{
+  // relayout all characters
+  mImpl->mTextUpdateInfo.mCharacterIndex             = 0;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd    = mImpl->mModel->mLogicalModel->mText.Count();
+  mImpl->mOperationsPending                          = static_cast<OperationsMask>(mImpl->mOperationsPending | LAYOUT);
+
+  //remove selection
+  if((mImpl->mEventData != nullptr) && (mImpl->mEventData->mState == EventData::SELECTING))
+  {
+    mImpl->ChangeState(EventData::EDITING);
+  }
+
+  mImpl->RequestRelayout();
+}
+
 bool Controller::SetDefaultLineSpacing(float lineSpacing)
 {
   if(std::fabs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
   {
     mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
     mImpl->mRecalculateNaturalSize = true;
+
+    RelayoutForNewLineSize();
     return true;
   }
   return false;
@@ -1241,6 +1260,8 @@ bool Controller::SetDefaultLineSize(float lineSize)
   {
     mImpl->mLayoutEngine.SetDefaultLineSize(lineSize);
     mImpl->mRecalculateNaturalSize = true;
+
+    RelayoutForNewLineSize();
     return true;
   }
   return false;
index 8c388b9..109be1a 100644 (file)
@@ -1874,6 +1874,11 @@ private: // Helpers.
    */
   void ResetScrollPosition();
 
+  /**
+   * @brief fill needed relayout parameters when line size is changed & request relayout.
+   */
+  void RelayoutForNewLineSize();
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
index 0601750..b082510 100644 (file)
@@ -144,9 +144,7 @@ void SelectionHandleController::Reposition(Controller::Impl& impl)
 
   lineRun += firstLineIndex;
 
-  // The line height is the addition of the line ascender and the line descender.
-  // However, the line descender has a negative value, hence the subtraction.
-  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+  selectionBoxInfo->lineHeight = GetLineHeight(*lineRun);
 
   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
 
@@ -275,9 +273,7 @@ void SelectionHandleController::Reposition(Controller::Impl& impl)
         // Update the line's vertical offset.
         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
 
-        // The line height is the addition of the line ascender and the line descender.
-        // However, the line descender has a negative value, hence the subtraction.
-        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+        selectionBoxInfo->lineHeight = GetLineHeight(*lineRun);
       }
     }
   }
index f8a2722..a5a6b3b 100644 (file)
@@ -225,7 +225,7 @@ Length View::GetGlyphs(GlyphInfo* glyphs,
 
               lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
 
-              penY += line->ascender;
+              penY += line->ascender + line->lineSpacing;
             }
           }
         }
@@ -265,7 +265,7 @@ Length View::GetGlyphs(GlyphInfo* glyphs,
           const LineRun& elidedLine = *ellipsisLine;
 
           if((1u == numberOfLines) &&
-             (elidedLine.ascender - elidedLine.descender > mImpl->mVisualModel->mControlSize.height))
+             (GetLineHeight(elidedLine) > mImpl->mVisualModel->mControlSize.height))
           {
             // Replace the first glyph with ellipsis glyph
             auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u;