[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / controller / text-controller-impl.cpp
index 10ccb16..10a76f1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -19,7 +19,7 @@
 #include <dali-toolkit/internal/text/controller/text-controller-impl.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/rendering/renderer.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/character-set-conversion.h>
-#include <dali-toolkit/internal/text/cursor-helper-functions.h>
-#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
-#include <dali-toolkit/internal/text/text-control-interface.h>
 #include <dali-toolkit/internal/text/controller/text-controller-impl-data-clearer.h>
 #include <dali-toolkit/internal/text/controller/text-controller-impl-event-handler.h>
 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
 #include <dali-toolkit/internal/text/controller/text-controller-placeholder-handler.h>
 #include <dali-toolkit/internal/text/controller/text-controller-relayouter.h>
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
+#include <dali-toolkit/internal/text/text-control-interface.h>
 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
 #include <dali-toolkit/internal/text/text-run-container.h>
@@ -51,7 +51,9 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT
 
 constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
 
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING         = "";
+const char* MIME_TYPE_TEXT_PLAIN = "text/plain;charset=utf-8";
+const char* MIME_TYPE_HTML       = "application/xhtml+xml";
 
 } // namespace
 
@@ -439,24 +441,88 @@ bool Controller::Impl::ProcessInputEvents()
   return ControllerImplEventHandler::ProcessInputEvents(*this);
 }
 
-void Controller::Impl::NotifyInputMethodContext()
+void Controller::Impl::SetAnchorColor(const Vector4& color)
 {
-  if(mEventData && mEventData->mInputMethodContext)
+  mAnchorColor = color;
+  UpdateAnchorColor();
+}
+
+const Vector4& Controller::Impl::GetAnchorColor() const
+{
+  return mAnchorColor;
+}
+
+void Controller::Impl::SetAnchorClickedColor(const Vector4& color)
+{
+  mAnchorClickedColor = color;
+  UpdateAnchorColor();
+}
+
+const Vector4& Controller::Impl::GetAnchorClickedColor() const
+{
+  return mAnchorClickedColor;
+}
+
+void Controller::Impl::UpdateAnchorColor()
+{
+  if(!mAnchorControlInterface ||
+     !mMarkupProcessorEnabled ||
+     !mModel->mLogicalModel->mAnchors.Count() ||
+     !IsShowingRealText())
   {
-    CharacterIndex cursorPosition = GetLogicalCursorPosition();
+    return;
+  }
 
-    const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
+  bool updateNeeded = false;
 
-    // Update the cursor position by removing the initial white spaces.
-    if(cursorPosition < numberOfWhiteSpaces)
+  // The anchor color & clicked color needs to be updated with the property's color.
+  for(auto& anchor : mModel->mLogicalModel->mAnchors)
+  {
+    if(!anchor.isMarkupColorSet && !anchor.isClicked)
     {
-      cursorPosition = 0u;
+      if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
+      {
+        ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
+        colorRun.color     = mAnchorColor;
+        updateNeeded       = true;
+      }
+      if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
+      {
+        UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
+        underlineRun.properties.color        = mAnchorColor;
+        updateNeeded                         = true;
+      }
     }
-    else
+    else if(!anchor.isMarkupClickedColorSet && anchor.isClicked)
     {
-      cursorPosition -= numberOfWhiteSpaces;
+      if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
+      {
+        ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
+        colorRun.color     = mAnchorClickedColor;
+        updateNeeded       = true;
+      }
+      if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
+      {
+        UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
+        underlineRun.properties.color        = mAnchorClickedColor;
+        updateNeeded                         = true;
+      }
     }
+  }
+
+  if(updateNeeded)
+  {
+    ClearFontData();
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
+    RequestRelayout();
+  }
+}
 
+void Controller::Impl::NotifyInputMethodContext()
+{
+  if(mEventData && mEventData->mInputMethodContext)
+  {
+    CharacterIndex cursorPosition = GetLogicalCursorPosition();
     mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
     mEventData->mInputMethodContext.NotifyCursorPosition();
   }
@@ -523,6 +589,19 @@ void Controller::Impl::GetText(std::string& text) const
   }
 }
 
+Length Controller::Impl::GetNumberOfCharacters() const
+{
+  if(!IsShowingPlaceholderText())
+  {
+    return mModel->GetNumberOfCharacters();
+  }
+  else
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetNumberOfCharacters %p empty (but showing placeholder)\n", this);
+    return 0u;
+  }
+}
+
 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
 {
   // Get the total number of characters.
@@ -540,8 +619,8 @@ Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& ac
   if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
      (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
   {
-    Window window = DevelWindow::Get(actor);
-    return static_cast<Dali::LayoutDirection::Type>(window ? window.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
+    Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(actor);
+    return static_cast<Dali::LayoutDirection::Type>(sceneHolder ? sceneHolder.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
   }
   else
   {
@@ -623,7 +702,7 @@ void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
 
       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
-      mTextUpdateInfo.mStartLineIndex  = mModel->mVisualModel->mLines.Count() - 1u;
+      mTextUpdateInfo.mStartLineIndex  = (mModel->mVisualModel->mLines.Count() > 0u) ? mModel->mVisualModel->mLines.Count() - 1u : 0u;
 
       // Nothing else to do;
       return;
@@ -933,6 +1012,8 @@ void Controller::Impl::SetEditable(bool editable)
     {
       bool decoratorEditable = editable && mIsUserInteractionEnabled;
       mEventData->mDecorator->SetEditable(decoratorEditable);
+      mEventData->mDecoratorUpdated = true;
+      RequestRelayout();
     }
   }
 }
@@ -1051,7 +1132,7 @@ std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
 
 void Controller::Impl::ShowClipboard()
 {
-  if(mClipboard)
+  if(EnsureClipboardCreated())
   {
     mClipboard.ShowClipboard();
   }
@@ -1059,7 +1140,7 @@ void Controller::Impl::ShowClipboard()
 
 void Controller::Impl::HideClipboard()
 {
-  if(mClipboard && mClipboardHideEnabled)
+  if(EnsureClipboardCreated() && mClipboardHideEnabled)
   {
     mClipboard.HideClipboard();
   }
@@ -1072,8 +1153,19 @@ void Controller::Impl::SetClipboardHideEnable(bool enable)
 
 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
 {
-  //Send string to clipboard
-  return (mClipboard && mClipboard.SetItem(source));
+  if(EnsureClipboardCreated())
+  {
+    Dali::Clipboard::ClipData data(MIME_TYPE_TEXT_PLAIN, source.c_str());
+    return mClipboard.SetData(data); // Send clipboard data to clipboard.
+  }
+
+  return false;
+}
+
+bool Controller::Impl::IsClipboardEmpty()
+{
+  bool result(Clipboard::IsAvailable() && EnsureClipboardCreated() && (mClipboard.HasType(MIME_TYPE_TEXT_PLAIN) || mClipboard.HasType(MIME_TYPE_HTML)));
+  return !result;
 }
 
 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
@@ -1084,14 +1176,6 @@ void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
   ChangeState(EventData::EDITING);
 }
 
-void Controller::Impl::RequestGetTextFromClipboard()
-{
-  if(mClipboard)
-  {
-    mClipboard.RequestItem();
-  }
-}
-
 void Controller::Impl::RepositionSelectionHandles()
 {
   SelectionHandleController::Reposition(*this);
@@ -1307,11 +1391,12 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) c
 
   if(index < mEventData->mPrimaryCursorPosition)
   {
-    cursorIndex -= numberOfCharacters;
+    cursorIndex = cursorIndex < numberOfCharacters ? 0u : cursorIndex - numberOfCharacters;
   }
   else
   {
-    cursorIndex += numberOfCharacters;
+    Length textLength = mModel->mVisualModel->mCharactersToGlyph.Count();
+    cursorIndex       = cursorIndex + numberOfCharacters > textLength ? textLength : cursorIndex + numberOfCharacters;
   }
 
   // Will update the cursor hook position.
@@ -1588,16 +1673,16 @@ bool Controller::Impl::IsScrollable(const Vector2& displacement)
   {
     const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
     const bool isVerticalScrollEnabled   = mEventData->mDecorator->IsVerticalScrollEnabled();
-    if(isHorizontalScrollEnabled ||isVerticalScrollEnabled)
+    if(isHorizontalScrollEnabled || isVerticalScrollEnabled)
     {
-      const Vector2& targetSize = mModel->mVisualModel->mControlSize;
-      const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
+      const Vector2& targetSize     = mModel->mVisualModel->mControlSize;
+      const Vector2& layoutSize     = mModel->mVisualModel->GetLayoutSize();
       const Vector2& scrollPosition = mModel->mScrollPosition;
 
       if(isHorizontalScrollEnabled)
       {
         const float displacementX = displacement.x;
-        const float positionX = scrollPosition.x + displacementX;
+        const float positionX     = scrollPosition.x + displacementX;
         if(layoutSize.width > targetSize.width && -positionX > 0.f && -positionX < layoutSize.width - targetSize.width)
         {
           isScrollable = true;
@@ -1607,7 +1692,7 @@ bool Controller::Impl::IsScrollable(const Vector2& displacement)
       if(isVerticalScrollEnabled)
       {
         const float displacementY = displacement.y;
-        const float positionY = scrollPosition.y + displacementY;
+        const float positionY     = scrollPosition.y + displacementY;
         if(layoutSize.height > targetSize.height && -positionY > 0 && -positionY < layoutSize.height - targetSize.height)
         {
           isScrollable = true;
@@ -1937,6 +2022,8 @@ void Controller::Impl::SetUserInteractionEnabled(bool enabled)
   {
     bool editable = mEventData->mEditingEnabled && enabled;
     mEventData->mDecorator->SetEditable(editable);
+    mEventData->mDecoratorUpdated = true;
+    RequestRelayout();
   }
 }
 
@@ -1972,6 +2059,7 @@ void Controller::Impl::ClearStyleData()
   mModel->mLogicalModel->mColorRuns.Clear();
   mModel->mLogicalModel->ClearFontDescriptionRuns();
   mModel->mLogicalModel->ClearStrikethroughRuns();
+  mModel->mLogicalModel->ClearUnderlineRuns();
 }
 
 void Controller::Impl::ResetScrollPosition()