Merge "Improve the underline markup" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index 2edca46..cf49df5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
 #include <dali-toolkit/internal/text/text-controller-impl.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/layer.h>
 #include <dali/public-api/rendering/renderer.h>
+#include <cmath>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
-#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #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/text-controller-impl-data-clearer.h>
 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
 #include <dali-toolkit/internal/text/text-controller-impl-model-updater.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-relayouter.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>
 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
+#include <dali-toolkit/internal/text/underlined-glyph-run.h>
 
 using namespace Dali;
 
@@ -43,26 +49,16 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
-struct BackgroundVertex
-{
-  Vector2 mPosition; ///< Vertex posiiton
-  Vector4 mColor;    ///< Vertex color
-};
+constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
 
-struct BackgroundMesh
-{
-  Vector<BackgroundVertex> mVertices; ///< container of vertices
-  Vector<unsigned short>   mIndices;  ///< container of indices
-};
+const std::string EMPTY_STRING("");
 
 } // namespace
 
 namespace Dali::Toolkit::Text
 {
-
 namespace
 {
-
 void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
 {
   // Sets the default text's color.
@@ -129,6 +125,243 @@ void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const font
     }
   }
 }
+
+void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState)
+{
+  EventData* eventData = impl.mEventData;
+
+  if(nullptr == eventData)
+  {
+    // Nothing to do if there is no text input.
+    return;
+  }
+
+  DecoratorPtr& decorator = eventData->mDecorator;
+  if(!decorator)
+  {
+    // Nothing to do if there is no decorator.
+    return;
+  }
+
+  DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", eventData->mState, newState);
+
+  if(eventData->mState != newState)
+  {
+    eventData->mPreviousState = eventData->mState;
+    eventData->mState         = newState;
+
+    switch(eventData->mState)
+    {
+      case EventData::INACTIVE:
+      {
+        decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+        decorator->StopCursorBlink();
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+        decorator->SetPopupActive(false);
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::INTERRUPTED:
+      {
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+        decorator->SetPopupActive(false);
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::SELECTING:
+      {
+        decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+        decorator->StopCursorBlink();
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
+          decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
+        }
+        decorator->SetHighlightActive(true);
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          impl.SetPopupButtons();
+          decorator->SetPopupActive(true);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::EDITING:
+      {
+        decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+        if(eventData->mCursorBlinkEnabled)
+        {
+          decorator->StartCursorBlink();
+        }
+        // Grab handle is not shown until a tap is received whilst EDITING
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          decorator->SetPopupActive(false);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+      case EventData::EDITING_WITH_POPUP:
+      {
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
+
+        decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+        if(eventData->mCursorBlinkEnabled)
+        {
+          decorator->StartCursorBlink();
+        }
+        if(eventData->mSelectionEnabled)
+        {
+          decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+          decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+          decorator->SetHighlightActive(false);
+        }
+        else if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(GRAB_HANDLE, true);
+        }
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          impl.SetPopupButtons();
+          decorator->SetPopupActive(true);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+      case EventData::EDITING_WITH_GRAB_HANDLE:
+      {
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
+
+        decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+        if(eventData->mCursorBlinkEnabled)
+        {
+          decorator->StartCursorBlink();
+        }
+        // Grab handle is not shown until a tap is received whilst EDITING
+        if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(GRAB_HANDLE, true);
+        }
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          decorator->SetPopupActive(false);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::SELECTION_HANDLE_PANNING:
+      {
+        decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+        decorator->StopCursorBlink();
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
+          decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
+        }
+        decorator->SetHighlightActive(true);
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          decorator->SetPopupActive(false);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::GRAB_HANDLE_PANNING:
+      {
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
+
+        decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+        if(eventData->mCursorBlinkEnabled)
+        {
+          decorator->StartCursorBlink();
+        }
+        if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(GRAB_HANDLE, true);
+        }
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          decorator->SetPopupActive(false);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::EDITING_WITH_PASTE_POPUP:
+      {
+        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
+
+        decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+        if(eventData->mCursorBlinkEnabled)
+        {
+          decorator->StartCursorBlink();
+        }
+
+        if(eventData->mGrabHandleEnabled)
+        {
+          decorator->SetHandleActive(GRAB_HANDLE, true);
+        }
+        decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+        decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+        decorator->SetHighlightActive(false);
+
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          impl.SetPopupButtons();
+          decorator->SetPopupActive(true);
+        }
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+
+      case EventData::TEXT_PANNING:
+      {
+        decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+        decorator->StopCursorBlink();
+        decorator->SetHandleActive(GRAB_HANDLE, false);
+        if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
+           decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
+        {
+          decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+          decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+          decorator->SetHighlightActive(true);
+        }
+
+        if(eventData->mGrabHandlePopupEnabled)
+        {
+          decorator->SetPopupActive(false);
+        }
+
+        eventData->mDecoratorUpdated = true;
+        break;
+      }
+    }
+  }
+}
+
 } // unnamed Namespace
 
 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
@@ -256,6 +489,19 @@ Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
   return numberOfWhiteSpaces;
 }
 
+void Controller::Impl::GetText(std::string& text) const
+{
+  if(!IsShowingPlaceholderText())
+  {
+    // Retrieves the text string.
+    GetText(0u, text);
+  }
+  else
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
+  }
+}
+
 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
 {
   // Get the total number of characters.
@@ -268,6 +514,61 @@ void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
   }
 }
 
+Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const
+{
+  if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
+     (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
+  {
+    return static_cast<Dali::LayoutDirection::Type>(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+  }
+  else
+  {
+    return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+  }
+}
+
+Toolkit::DevelText::TextDirection::Type Controller::Impl::GetTextDirection()
+{
+  if(mUpdateTextDirection)
+  {
+    // Operations that can be done only once until the text changes.
+    const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
+                                                                          GET_SCRIPTS |
+                                                                          VALIDATE_FONTS |
+                                                                          GET_LINE_BREAKS |
+                                                                          BIDI_INFO |
+                                                                          SHAPE_TEXT |
+                                                                          GET_GLYPH_METRICS);
+
+    // Set the update info to relayout the whole text.
+    mTextUpdateInfo.mParagraphCharacterIndex     = 0u;
+    mTextUpdateInfo.mRequestedNumberOfCharacters = mModel->mLogicalModel->mText.Count();
+
+    // Make sure the model is up-to-date before layouting
+    UpdateModel(onlyOnceOperations);
+
+    Vector3 naturalSize;
+    Relayouter::DoRelayout(*this,
+                           Size(MAX_FLOAT, MAX_FLOAT),
+                           static_cast<OperationsMask>(onlyOnceOperations |
+                                                       LAYOUT | REORDER | UPDATE_DIRECTION),
+                           naturalSize.GetVectorXY());
+
+    // Do not do again the only once operations.
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending & ~onlyOnceOperations);
+
+    // Clear the update info. This info will be set the next time the text is updated.
+    mTextUpdateInfo.Clear();
+
+    // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
+    mTextUpdateInfo.mFullRelayoutNeeded = true;
+
+    mUpdateTextDirection = false;
+  }
+
+  return mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+}
+
 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
 {
   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
@@ -353,315 +654,137 @@ void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
   mTextUpdateInfo.mStartGlyphIndex             = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
 }
 
-void Controller::Impl::ClearFullModelData(OperationsMask operations)
+void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
 {
-  if(NO_OPERATION != (GET_LINE_BREAKS & operations))
-  {
-    mModel->mLogicalModel->mLineBreakInfo.Clear();
-    mModel->mLogicalModel->mParagraphInfo.Clear();
-  }
+  ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
+}
 
-  if(NO_OPERATION != (GET_SCRIPTS & operations))
-  {
-    mModel->mLogicalModel->mScriptRuns.Clear();
-  }
+bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
+{
+  return ControllerImplModelUpdater::Update(*this, operationsRequired);
+}
 
-  if(NO_OPERATION != (VALIDATE_FONTS & operations))
-  {
-    mModel->mLogicalModel->mFontRuns.Clear();
-  }
+void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
+{
+  SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
+}
 
-  if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
+float Controller::Impl::GetDefaultFontLineHeight()
+{
+  FontId defaultFontId = 0u;
+  if(nullptr == mFontDefaults)
   {
-    if(NO_OPERATION != (BIDI_INFO & operations))
-    {
-      mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
-      mModel->mLogicalModel->mCharacterDirections.Clear();
-    }
-
-    if(NO_OPERATION != (REORDER & operations))
-    {
-      // Free the allocated memory used to store the conversion table in the bidirectional line info run.
-      for(Vector<BidirectionalLineInfoRun>::Iterator it    = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
-                                                     endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
-          it != endIt;
-          ++it)
-      {
-        BidirectionalLineInfoRun& bidiLineInfo = *it;
-
-        free(bidiLineInfo.visualToLogicalMap);
-        bidiLineInfo.visualToLogicalMap = NULL;
-      }
-      mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
-    }
+    TextAbstraction::FontDescription fontDescription;
+    defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * GetFontSizeScale());
   }
-
-  if(NO_OPERATION != (SHAPE_TEXT & operations))
+  else
   {
-    mModel->mVisualModel->mGlyphs.Clear();
-    mModel->mVisualModel->mGlyphsToCharacters.Clear();
-    mModel->mVisualModel->mCharactersToGlyph.Clear();
-    mModel->mVisualModel->mCharactersPerGlyph.Clear();
-    mModel->mVisualModel->mGlyphsPerCharacter.Clear();
-    mModel->mVisualModel->mGlyphPositions.Clear();
+    defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale());
   }
 
-  if(NO_OPERATION != (LAYOUT & operations))
-  {
-    mModel->mVisualModel->mLines.Clear();
-  }
+  Text::FontMetrics fontMetrics;
+  mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
 
-  if(NO_OPERATION != (COLOR & operations))
-  {
-    mModel->mVisualModel->mColorIndices.Clear();
-    mModel->mVisualModel->mBackgroundColorIndices.Clear();
-  }
+  return (fontMetrics.ascender - fontMetrics.descender);
 }
 
-void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
 {
-  const CharacterIndex endIndexPlusOne = endIndex + 1u;
-
-  if(NO_OPERATION != (GET_LINE_BREAKS & operations))
+  if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
   {
-    // Clear the line break info.
-    LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
+    mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
 
-    mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
-                                                lineBreakInfoBuffer + endIndexPlusOne);
-
-    // Clear the paragraphs.
-    ClearCharacterRuns(startIndex,
-                       endIndex,
-                       mModel->mLogicalModel->mParagraphInfo);
+    RelayoutAllCharacters();
+    return true;
   }
+  return false;
+}
 
-  if(NO_OPERATION != (GET_SCRIPTS & operations))
+bool Controller::Impl::SetDefaultLineSize(float lineSize)
+{
+  if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
   {
-    // Clear the scripts.
-    ClearCharacterRuns(startIndex,
-                       endIndex,
-                       mModel->mLogicalModel->mScriptRuns);
-  }
+    mLayoutEngine.SetDefaultLineSize(lineSize);
 
-  if(NO_OPERATION != (VALIDATE_FONTS & operations))
-  {
-    // Clear the fonts.
-    ClearCharacterRuns(startIndex,
-                       endIndex,
-                       mModel->mLogicalModel->mFontRuns);
+    RelayoutAllCharacters();
+    return true;
   }
+  return false;
+}
 
-  if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
+bool Controller::Impl::SetRelativeLineSize(float relativeLineSize)
+{
+  if(std::fabs(relativeLineSize - GetRelativeLineSize()) > Math::MACHINE_EPSILON_1000)
   {
-    if(NO_OPERATION != (BIDI_INFO & operations))
-    {
-      // Clear the bidirectional paragraph info.
-      ClearCharacterRuns(startIndex,
-                         endIndex,
-                         mModel->mLogicalModel->mBidirectionalParagraphInfo);
-
-      // Clear the character's directions.
-      CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
-
-      mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
-                                                        characterDirectionsBuffer + endIndexPlusOne);
-    }
-
-    if(NO_OPERATION != (REORDER & operations))
-    {
-      uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
-      uint32_t endRemoveIndex   = startRemoveIndex;
-      ClearCharacterRuns(startIndex,
-                         endIndex,
-                         mModel->mLogicalModel->mBidirectionalLineInfo,
-                         startRemoveIndex,
-                         endRemoveIndex);
-
-      BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
-
-      // Free the allocated memory used to store the conversion table in the bidirectional line info run.
-      for(Vector<BidirectionalLineInfoRun>::Iterator it    = bidirectionalLineInfoBuffer + startRemoveIndex,
-                                                     endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
-          it != endIt;
-          ++it)
-      {
-        BidirectionalLineInfoRun& bidiLineInfo = *it;
+    mLayoutEngine.SetRelativeLineSize(relativeLineSize);
 
-        free(bidiLineInfo.visualToLogicalMap);
-        bidiLineInfo.visualToLogicalMap = NULL;
-      }
-
-      mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
-                                                          bidirectionalLineInfoBuffer + endRemoveIndex);
-    }
+    RelayoutAllCharacters();
+    return true;
   }
+  return false;
 }
 
-void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+float Controller::Impl::GetRelativeLineSize()
 {
-  const CharacterIndex endIndexPlusOne           = endIndex + 1u;
-  const Length         numberOfCharactersRemoved = endIndexPlusOne - startIndex;
-
-  // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
-  GlyphIndex* charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
-  Length*     glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
-
-  const GlyphIndex endGlyphIndexPlusOne  = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
-  const Length     numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
+  return mLayoutEngine.GetRelativeLineSize();
+}
 
-  if(NO_OPERATION != (SHAPE_TEXT & operations))
+string Controller::Impl::GetSelectedText()
+{
+  string text;
+  if(EventData::SELECTING == mEventData->mState)
   {
-    // Update the character to glyph indices.
-    for(Vector<GlyphIndex>::Iterator it    = charactersToGlyphBuffer + endIndexPlusOne,
-                                     endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
-        it != endIt;
-        ++it)
-    {
-      CharacterIndex& index = *it;
-      index -= numberOfGlyphsRemoved;
-    }
-
-    // Clear the character to glyph conversion table.
-    mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
-                                                   charactersToGlyphBuffer + endIndexPlusOne);
-
-    // Clear the glyphs per character table.
-    mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
-                                                    glyphsPerCharacterBuffer + endIndexPlusOne);
-
-    // Clear the glyphs buffer.
-    GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
-    mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                        glyphsBuffer + endGlyphIndexPlusOne);
+    RetrieveSelection(text, false);
+  }
+  return text;
+}
 
-    CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
+string Controller::Impl::CopyText()
+{
+  string text;
+  RetrieveSelection(text, false);
+  SendSelectionToClipboard(false); // Text not modified
 
-    // Update the glyph to character indices.
-    for(Vector<CharacterIndex>::Iterator it    = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
-                                         endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
-        it != endIt;
-        ++it)
-    {
-      CharacterIndex& index = *it;
-      index -= numberOfCharactersRemoved;
-    }
+  mEventData->mUpdateCursorPosition = true;
 
-    // Clear the glyphs to characters buffer.
-    mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                                    glyphsToCharactersBuffer + endGlyphIndexPlusOne);
+  RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
 
-    // Clear the characters per glyph buffer.
-    Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
-    mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                                    charactersPerGlyphBuffer + endGlyphIndexPlusOne);
+  return text;
+}
 
-    // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout
-    if(0u != mModel->mVisualModel->mGlyphPositions.Count())
-    {
-      // Clear the positions buffer.
-      Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
-      mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                                  positionsBuffer + endGlyphIndexPlusOne);
-    }
-  }
+string Controller::Impl::CutText()
+{
+  string text;
+  RetrieveSelection(text, false);
 
-  if(NO_OPERATION != (LAYOUT & operations))
+  if(!IsEditable())
   {
-    // Clear the lines.
-    uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
-    uint32_t endRemoveIndex   = startRemoveIndex;
-    ClearCharacterRuns(startIndex,
-                       endIndex,
-                       mModel->mVisualModel->mLines,
-                       startRemoveIndex,
-                       endRemoveIndex);
-
-    // Will update the glyph runs.
-    startRemoveIndex = mModel->mVisualModel->mLines.Count();
-    endRemoveIndex   = startRemoveIndex;
-    ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
-                   endGlyphIndexPlusOne - 1u,
-                   mModel->mVisualModel->mLines,
-                   startRemoveIndex,
-                   endRemoveIndex);
-
-    // Set the line index from where to insert the new laid-out lines.
-    mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
-
-    LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
-    mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
-                                       linesBuffer + endRemoveIndex);
+    return EMPTY_STRING;
   }
 
-  if(NO_OPERATION != (COLOR & operations))
-  {
-    if(0u != mModel->mVisualModel->mColorIndices.Count())
-    {
-      ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
-      mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                                colorIndexBuffer + endGlyphIndexPlusOne);
-    }
-
-    if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
-    {
-      ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
-      mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
-                                                          backgroundColorIndexBuffer + endGlyphIndexPlusOne);
-    }
-  }
-}
+  SendSelectionToClipboard(true); // Synchronous call to modify text
+  mOperationsPending = ALL_OPERATIONS;
 
-void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
-{
-  if(mTextUpdateInfo.mClearAll ||
-     ((0u == startIndex) &&
-      (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
+  if((0u != mModel->mLogicalModel->mText.Count()) ||
+     !IsPlaceholderAvailable())
   {
-    ClearFullModelData(operations);
+    QueueModifyEvent(ModifyEvent::TEXT_DELETED);
   }
   else
   {
-    // Clear the model data related with characters.
-    ClearCharacterModelData(startIndex, endIndex, operations);
-
-    // Clear the model data related with glyphs.
-    ClearGlyphModelData(startIndex, endIndex, operations);
+    PlaceholderHandler::ShowPlaceholderText(*this);
   }
 
-  // The estimated number of lines. Used to avoid reallocations when layouting.
-  mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
-
-  mModel->mVisualModel->ClearCaches();
-}
-
-bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
-{
-  return ControllerImplModelUpdater::Update(*this, operationsRequired);
-}
+  mEventData->mUpdateCursorPosition = true;
+  mEventData->mScrollAfterDelete    = true;
 
-void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
-{
-  SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
-}
+  RequestRelayout();
 
-float Controller::Impl::GetDefaultFontLineHeight()
-{
-  FontId defaultFontId = 0u;
-  if(nullptr == mFontDefaults)
-  {
-    TextAbstraction::FontDescription fontDescription;
-    defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
-  }
-  else
+  if(nullptr != mEditableControlInterface)
   {
-    defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
+    mEditableControlInterface->TextChanged(true);
   }
-
-  Text::FontMetrics fontMetrics;
-  mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
-
-  return (fontMetrics.ascender - fontMetrics.descender);
+  return text;
 }
 
 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
@@ -783,6 +906,26 @@ void Controller::Impl::SetEditable(bool editable)
   if(mEventData)
   {
     mEventData->mEditingEnabled = editable;
+
+    if(mEventData->mDecorator)
+    {
+      mEventData->mDecorator->SetEditable(editable);
+    }
+  }
+}
+
+void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont)
+{
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
+
+  if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str());
+    mFontDefaults->mFontDescription.family = newDefaultFont;
+
+    ClearFontData();
+
+    RequestRelayout();
   }
 }
 
@@ -927,298 +1070,83 @@ void Controller::Impl::RequestGetTextFromClipboard()
 }
 
 void Controller::Impl::RepositionSelectionHandles()
-{
-  SelectionHandleController::Reposition(*this);
-}
-void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
-{
-  SelectionHandleController::Reposition(*this, visualX, visualY, action);
-}
-
-void Controller::Impl::SetPopupButtons()
-{
-  /**
-   *  Sets the Popup buttons to be shown depending on State.
-   *
-   *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
-   *
-   *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
-   */
-
-  bool                        isEditable    = IsEditable();
-  TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
-
-  if(EventData::SELECTING == mEventData->mState)
-  {
-    buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
-    if(isEditable)
-    {
-      buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
-    }
-
-    if(!IsClipboardEmpty())
-    {
-      if(isEditable)
-      {
-        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
-      }
-      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
-    }
-
-    if(!mEventData->mAllTextSelected)
-    {
-      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
-    }
-  }
-  else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
-  {
-    if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
-    {
-      buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
-    }
-
-    if(!IsClipboardEmpty())
-    {
-      if(isEditable)
-      {
-        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
-      }
-      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
-    }
-  }
-  else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
-  {
-    if(!IsClipboardEmpty())
-    {
-      if(isEditable)
-      {
-        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
-      }
-      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
-    }
-  }
-
-  mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
-}
-
-void Controller::Impl::ChangeState(EventData::State newState)
-{
-  if(nullptr == mEventData)
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState);
-
-  if(mEventData->mState != newState)
-  {
-    mEventData->mPreviousState = mEventData->mState;
-    mEventData->mState         = newState;
-
-    switch(mEventData->mState)
-    {
-      case EventData::INACTIVE:
-      {
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
-        mEventData->mDecorator->StopCursorBlink();
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
-        mEventData->mDecorator->SetPopupActive(false);
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::INTERRUPTED:
-      {
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
-        mEventData->mDecorator->SetPopupActive(false);
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::SELECTING:
-      {
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
-        mEventData->mDecorator->StopCursorBlink();
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
-          mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
-        }
-        mEventData->mDecorator->SetHighlightActive(true);
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          SetPopupButtons();
-          mEventData->mDecorator->SetPopupActive(true);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::EDITING:
-      {
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
-        if(mEventData->mCursorBlinkEnabled)
-        {
-          mEventData->mDecorator->StartCursorBlink();
-        }
-        // Grab handle is not shown until a tap is received whilst EDITING
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          mEventData->mDecorator->SetPopupActive(false);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::EDITING_WITH_POPUP:
-      {
-        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
-
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
-        if(mEventData->mCursorBlinkEnabled)
-        {
-          mEventData->mDecorator->StartCursorBlink();
-        }
-        if(mEventData->mSelectionEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-          mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-          mEventData->mDecorator->SetHighlightActive(false);
-        }
-        else if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
-        }
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          SetPopupButtons();
-          mEventData->mDecorator->SetPopupActive(true);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::EDITING_WITH_GRAB_HANDLE:
-      {
-        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
-
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
-        if(mEventData->mCursorBlinkEnabled)
-        {
-          mEventData->mDecorator->StartCursorBlink();
-        }
-        // Grab handle is not shown until a tap is received whilst EDITING
-        if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
-        }
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          mEventData->mDecorator->SetPopupActive(false);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::SELECTION_HANDLE_PANNING:
-      {
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
-        mEventData->mDecorator->StopCursorBlink();
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
-          mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
-        }
-        mEventData->mDecorator->SetHighlightActive(true);
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          mEventData->mDecorator->SetPopupActive(false);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::GRAB_HANDLE_PANNING:
-      {
-        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
-
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
-        if(mEventData->mCursorBlinkEnabled)
-        {
-          mEventData->mDecorator->StartCursorBlink();
-        }
-        if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
-        }
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          mEventData->mDecorator->SetPopupActive(false);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::EDITING_WITH_PASTE_POPUP:
-      {
-        DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
+{
+  SelectionHandleController::Reposition(*this);
+}
+void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
+{
+  SelectionHandleController::Reposition(*this, visualX, visualY, action);
+}
 
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
-        if(mEventData->mCursorBlinkEnabled)
-        {
-          mEventData->mDecorator->StartCursorBlink();
-        }
+void Controller::Impl::SetPopupButtons()
+{
+  /**
+   *  Sets the Popup buttons to be shown depending on State.
+   *
+   *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
+   *
+   *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
+   */
 
-        if(mEventData->mGrabHandleEnabled)
-        {
-          mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
-        }
-        mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-        mEventData->mDecorator->SetHighlightActive(false);
+  bool                        isEditable    = IsEditable();
+  TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
 
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          SetPopupButtons();
-          mEventData->mDecorator->SetPopupActive(true);
-        }
-        mEventData->mDecoratorUpdated = true;
-        break;
-      }
-      case EventData::TEXT_PANNING:
+  if(EventData::SELECTING == mEventData->mState)
+  {
+    buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
+    if(isEditable)
+    {
+      buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
+    }
+
+    if(!IsClipboardEmpty())
+    {
+      if(isEditable)
       {
-        mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
-        mEventData->mDecorator->StopCursorBlink();
-        mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
-        if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
-           mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
-        {
-          mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
-          mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
-          mEventData->mDecorator->SetHighlightActive(true);
-        }
+        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
+      }
+      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+    }
 
-        if(mEventData->mGrabHandlePopupEnabled)
-        {
-          mEventData->mDecorator->SetPopupActive(false);
-        }
+    if(!mEventData->mAllTextSelected)
+    {
+      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
+    }
+  }
+  else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
+  {
+    if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
+    {
+      buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
+    }
 
-        mEventData->mDecoratorUpdated = true;
-        break;
+    if(!IsClipboardEmpty())
+    {
+      if(isEditable)
+      {
+        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
+      }
+      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+    }
+  }
+  else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
+  {
+    if(!IsClipboardEmpty())
+    {
+      if(isEditable)
+      {
+        buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
       }
+      buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
     }
   }
+
+  mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
+}
+
+void Controller::Impl::ChangeState(EventData::State newState)
+{
+  ChangeTextControllerState(*this, newState);
 }
 
 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
@@ -1287,7 +1215,10 @@ void Controller::Impl::GetCursorPosition(CharacterIndex logical,
   parameters.logical      = logical;
   parameters.isMultiline  = isMultiLine;
 
+  float defaultFontLineHeight = GetDefaultFontLineHeight();
+
   Text::GetCursorPosition(parameters,
+                          defaultFontLineHeight,
                           cursorInfo);
 
   // Adds Outline offset.
@@ -1550,186 +1481,143 @@ void Controller::Impl::RequestRelayout()
   }
 }
 
-Actor Controller::Impl::CreateBackgroundActor()
+void Controller::Impl::RelayoutAllCharacters()
 {
-  // NOTE: Currently we only support background color for left-to-right text.
-
-  Actor actor;
-
-  Length numberOfGlyphs = mView.GetNumberOfGlyphs();
-  if(numberOfGlyphs > 0u)
-  {
-    Vector<GlyphInfo> glyphs;
-    glyphs.Resize(numberOfGlyphs);
+  // relayout all characters
+  mTextUpdateInfo.mCharacterIndex             = 0;
+  mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+  mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
+  mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
 
-    Vector<Vector2> positions;
-    positions.Resize(numberOfGlyphs);
+  mTextUpdateInfo.mFullRelayoutNeeded = true;
 
-    // Get the line where the glyphs are laid-out.
-    const LineRun* lineRun         = mModel->mVisualModel->mLines.Begin();
-    float          alignmentOffset = lineRun->alignmentOffset;
-    numberOfGlyphs                 = mView.GetGlyphs(glyphs.Begin(),
-                                     positions.Begin(),
-                                     alignmentOffset,
-                                     0u,
-                                     numberOfGlyphs);
+  // Need to recalculate natural size
+  mRecalculateNaturalSize = true;
 
-    glyphs.Resize(numberOfGlyphs);
-    positions.Resize(numberOfGlyphs);
+  //remove selection
+  if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
+  {
+    ChangeState(EventData::EDITING);
+  }
 
-    const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
-    const Vector2* const   positionsBuffer = positions.Begin();
+  RequestRelayout();
+}
 
-    BackgroundMesh mesh;
-    mesh.mVertices.Reserve(4u * glyphs.Size());
-    mesh.mIndices.Reserve(6u * glyphs.Size());
+bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
+{
+  return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
+}
 
-    const Vector2 textSize = mView.GetLayoutSize();
+void Controller::Impl::ProcessInputStyleChangedSignals()
+{
+  if(mEventData)
+  {
+    if(mEditableControlInterface)
+    {
+      // Emit the input style changed signal for each mask
+      std::for_each(mEventData->mInputStyleChangedQueue.begin(),
+                    mEventData->mInputStyleChangedQueue.end(),
+                    [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
+    }
 
-    const float offsetX = alignmentOffset + textSize.width * 0.5f;
-    const float offsetY = textSize.height * 0.5f;
+    mEventData->mInputStyleChangedQueue.Clear();
+  }
+}
 
-    const Vector4* const    backgroundColorsBuffer       = mView.GetBackgroundColors();
-    const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
-    const Vector4&          defaultBackgroundColor       = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
+void Controller::Impl::ScrollBy(Vector2 scroll)
+{
+  if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
+  {
+    const Vector2& layoutSize    = mModel->mVisualModel->GetLayoutSize();
+    const Vector2  currentScroll = mModel->mScrollPosition;
 
-    Vector4   quad;
-    uint32_t  numberOfQuads = 0u;
-    Length    yLineOffset   = 0;
-    Length    prevLineIndex = 0;
-    LineIndex lineIndex;
-    Length    numberOfLines;
+    scroll.x = -scroll.x;
+    scroll.y = -scroll.y;
 
-    for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
+    if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
     {
-      const GlyphInfo& glyph = *(glyphsBuffer + i);
-
-      // Get the background color of the character.
-      // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
-      const bool       isMarkupBackground       = mView.IsMarkupBackgroundColorSet();
-      const ColorIndex backgroundColorIndex     = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
-      const bool       isDefaultBackgroundColor = (0u == backgroundColorIndex);
-      const Vector4&   backgroundColor          = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
-
-      mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
-      Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
+      mModel->mScrollPosition.x += scroll.x;
+      ClampHorizontalScroll(layoutSize);
+    }
 
-      if(lineIndex != prevLineIndex)
-      {
-        yLineOffset += lineHeight;
-      }
+    if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
+    {
+      mModel->mScrollPosition.y += scroll.y;
+      ClampVerticalScroll(layoutSize);
+    }
 
-      // Only create quads for glyphs with a background color
-      if(backgroundColor != Color::TRANSPARENT)
-      {
-        const Vector2 position = *(positionsBuffer + i);
+    if(mModel->mScrollPosition != currentScroll)
+    {
+      mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
+      RequestRelayout();
+    }
+  }
+}
 
-        if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
-        {
-          quad.x = position.x;
-          quad.y = yLineOffset;
-          quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
-          quad.w = lineHeight;
-        }
-        else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
-        {
-          quad.x = position.x;
-          quad.y = yLineOffset;
-          quad.z = quad.x - glyph.xBearing + glyph.advance;
-          quad.w = quad.y + lineHeight;
-        }
-        else if(i == glyphSize - 1u) // The last glyph in the whole text
-        {
-          quad.x = position.x - glyph.xBearing;
-          quad.y = yLineOffset;
-          quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
-          quad.w = quad.y + lineHeight;
-        }
-        else // The glyph in the middle of the text
-        {
-          quad.x = position.x - glyph.xBearing;
-          quad.y = yLineOffset;
-          quad.z = quad.x + glyph.advance;
-          quad.w = quad.y + lineHeight;
-        }
+float Controller::Impl::GetHorizontalScrollPosition()
+{
+  // Scroll values are negative internally so we convert them to positive numbers
+  return mEventData ? -mModel->mScrollPosition.x : 0.0f;
+}
 
-        BackgroundVertex vertex;
-
-        // Top left
-        vertex.mPosition.x = quad.x - offsetX;
-        vertex.mPosition.y = quad.y - offsetY;
-        vertex.mColor      = backgroundColor;
-        mesh.mVertices.PushBack(vertex);
-
-        // Top right
-        vertex.mPosition.x = quad.z - offsetX;
-        vertex.mPosition.y = quad.y - offsetY;
-        vertex.mColor      = backgroundColor;
-        mesh.mVertices.PushBack(vertex);
-
-        // Bottom left
-        vertex.mPosition.x = quad.x - offsetX;
-        vertex.mPosition.y = quad.w - offsetY;
-        vertex.mColor      = backgroundColor;
-        mesh.mVertices.PushBack(vertex);
-
-        // Bottom right
-        vertex.mPosition.x = quad.z - offsetX;
-        vertex.mPosition.y = quad.w - offsetY;
-        vertex.mColor      = backgroundColor;
-        mesh.mVertices.PushBack(vertex);
-
-        // Six indices in counter clockwise winding
-        mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
-        mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
-        mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
-        mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
-        mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
-        mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
-
-        numberOfQuads++;
-      }
+float Controller::Impl::GetVerticalScrollPosition()
+{
+  // Scroll values are negative internally so we convert them to positive numbers
+  return mEventData ? -mModel->mScrollPosition.y : 0.0f;
+}
 
-      if(lineIndex != prevLineIndex)
-      {
-        prevLineIndex = lineIndex;
-      }
-    }
+Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
+{
+  //TODO
+  return Vector3(10.f, 10.f, 10.f);
+}
 
-    // Only create the background actor if there are glyphs with background color
-    if(mesh.mVertices.Count() > 0u)
-    {
-      Property::Map quadVertexFormat;
-      quadVertexFormat["aPosition"] = Property::VECTOR2;
-      quadVertexFormat["aColor"]    = Property::VECTOR4;
+Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
+{
+  //TODO
+  return Vector2(10.f, 10.f);
+}
 
-      VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
-      quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
+Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
+{
+  auto actor = Toolkit::TextAnchor::New();
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  const Vector3 anchorPosition = GetAnchorPosition(anchor);
+  actor.SetProperty(Actor::Property::POSITION, anchorPosition);
+  const Vector2 anchorSize = GetAnchorSize(anchor);
+  actor.SetProperty(Actor::Property::SIZE, anchorSize);
+  std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
+  actor.SetProperty(Actor::Property::NAME, anchorText);
+  actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
+  actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
+  actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
+  return actor;
+}
 
-      Geometry quadGeometry = Geometry::New();
-      quadGeometry.AddVertexBuffer(quadVertices);
-      quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
+void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+  /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
+           we need to create and destroy potentially many actors. Some optimization can be considered here.
+           Maybe a "dirty" flag in mLogicalModel? */
+  anchorActors.clear();
+  for(auto& anchor : mModel->mLogicalModel->mAnchors)
+  {
+    auto actor = CreateAnchorActor(anchor);
+    anchorActors.push_back(actor);
+  }
+}
 
-      if(!mShaderBackground)
-      {
-        mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
-      }
+int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
+{
+  Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
 
-      Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
-      renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
-      renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
-
-      actor = Actor::New();
-      actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
-      actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
-      actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
-      actor.SetProperty(Actor::Property::SIZE, textSize);
-      actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
-      actor.AddRenderer(renderer);
-    }
+  while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
+  {
+    it++;
   }
 
-  return actor;
+  return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
 }
 
 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
@@ -1748,13 +1636,253 @@ void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearP
   {
     CharacterIndex characterIndex     = it->characterRun.characterIndex;
     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
-    for(Length index = 0u; index < numberOfCharacters; index++)
+
+    if(numberOfCharacters == 0)
+    {
+      continue;
+    }
+
+    // Create one run for all glyphs of all run's characters that has same properties
+    // This enhance performance and reduce the needed memory to store glyphs-runs
+    UnderlinedGlyphRun underlineGlyphRun;
+    underlineGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
+    underlineGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+    //Copy properties (attributes)
+    underlineGlyphRun.properties = it->properties;
+
+    for(Length index = 1u; index < numberOfCharacters; index++)
+    {
+      underlineGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+    }
+
+    mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+  }
+}
+
+void Controller::Impl::CopyStrikethroughFromLogicalToVisualModels()
+{
+  //Strikethrough character runs from markup-processor
+  const Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns = mModel->mLogicalModel->mStrikethroughCharacterRuns;
+  const Vector<GlyphIndex>&                charactersToGlyph          = mModel->mVisualModel->mCharactersToGlyph;
+  const Vector<Length>&                    glyphsPerCharacter         = mModel->mVisualModel->mGlyphsPerCharacter;
+
+  mModel->mVisualModel->mStrikethroughRuns.Clear();
+
+  for(Vector<StrikethroughCharacterRun>::ConstIterator it = strikethroughCharacterRuns.Begin(), endIt = strikethroughCharacterRuns.End(); it != endIt; ++it)
+  {
+    CharacterIndex characterIndex     = it->characterRun.characterIndex;
+    Length         numberOfCharacters = it->characterRun.numberOfCharacters;
+
+    if(numberOfCharacters == 0)
+    {
+      continue;
+    }
+
+    StrikethroughGlyphRun strikethroughGlyphRun;
+    strikethroughGlyphRun.color                   = it->color;
+    strikethroughGlyphRun.isColorSet              = it->isColorSet;
+    strikethroughGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
+    strikethroughGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+
+    for(Length index = 1u; index < numberOfCharacters; index++)
+    {
+      strikethroughGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+    }
+
+    mModel->mVisualModel->mStrikethroughRuns.PushBack(strikethroughGlyphRun);
+  }
+}
+
+void Controller::Impl::SetAutoScrollEnabled(bool enable)
+{
+  if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
+  {
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+                                                     LAYOUT |
+                                                     ALIGN |
+                                                     UPDATE_LAYOUT_SIZE |
+                                                     REORDER);
+
+    if(enable)
+    {
+      DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
+      mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
+    }
+    else
+    {
+      DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
+    }
+
+    mIsAutoScrollEnabled = enable;
+    RequestRelayout();
+  }
+  else
+  {
+    DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
+    mIsAutoScrollEnabled = false;
+  }
+}
+
+void Controller::Impl::SetEnableCursorBlink(bool enable)
+{
+  DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
+
+  if(mEventData)
+  {
+    mEventData->mCursorBlinkEnabled = enable;
+
+    if(!enable && mEventData->mDecorator)
+    {
+      mEventData->mDecorator->StopCursorBlink();
+    }
+  }
+}
+
+void Controller::Impl::SetMultiLineEnabled(bool enable)
+{
+  const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
+
+  if(layout != mLayoutEngine.GetLayout())
+  {
+    // Set the layout type.
+    mLayoutEngine.SetLayout(layout);
+
+    // Set the flags to redo the layout operations
+    const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
+                                                                        UPDATE_LAYOUT_SIZE |
+                                                                        ALIGN |
+                                                                        REORDER);
+
+    mTextUpdateInfo.mFullRelayoutNeeded = true;
+    mOperationsPending                  = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
+
+    // Need to recalculate natural size
+    mRecalculateNaturalSize = true;
+
+    RequestRelayout();
+  }
+}
+
+void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
+{
+  if(alignment != mModel->mHorizontalAlignment)
+  {
+    // Set the alignment.
+    mModel->mHorizontalAlignment = alignment;
+
+    // Set the flag to redo the alignment operation.
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+
+    if(mEventData)
+    {
+      mEventData->mUpdateAlignment = true;
+
+      // Update the cursor if it's in editing mode
+      if(EventData::IsEditingState(mEventData->mState))
+      {
+        ChangeState(EventData::EDITING);
+        mEventData->mUpdateCursorPosition = true;
+      }
+    }
+
+    RequestRelayout();
+  }
+}
+
+void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
+{
+  if(alignment != mModel->mVerticalAlignment)
+  {
+    // Set the alignment.
+    mModel->mVerticalAlignment = alignment;
+    mOperationsPending         = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+    RequestRelayout();
+  }
+}
+
+void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
+{
+  if(lineWrapMode != mModel->mLineWrapMode)
+  {
+    // Update Text layout for applying wrap mode
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+                                                     ALIGN |
+                                                     LAYOUT |
+                                                     UPDATE_LAYOUT_SIZE |
+                                                     REORDER);
+
+    if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
+       (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
     {
-      GlyphRun underlineGlyphRun;
-      underlineGlyphRun.glyphIndex     = charactersToGlyph[characterIndex + index];
-      underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
-      mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+      mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
     }
+
+    // Set the text wrap mode.
+    mModel->mLineWrapMode = lineWrapMode;
+
+    mTextUpdateInfo.mCharacterIndex             = 0u;
+    mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+    mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
+
+    // Request relayout
+    RequestRelayout();
+  }
+}
+
+void Controller::Impl::SetDefaultColor(const Vector4& color)
+{
+  mTextColor = color;
+
+  if(!IsShowingPlaceholderText())
+  {
+    mModel->mVisualModel->SetTextColor(color);
+    mModel->mLogicalModel->mColorRuns.Clear();
+    mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
+    RequestRelayout();
+  }
+}
+
+void Controller::Impl::ClearFontData()
+{
+  if(mFontDefaults)
+  {
+    mFontDefaults->mFontId = 0u; // Remove old font ID
+  }
+
+  // Set flags to update the model.
+  mTextUpdateInfo.mCharacterIndex             = 0u;
+  mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+  mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
+
+  mTextUpdateInfo.mClearAll           = true;
+  mTextUpdateInfo.mFullRelayoutNeeded = true;
+  mRecalculateNaturalSize             = true;
+
+  mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+                                                   VALIDATE_FONTS |
+                                                   SHAPE_TEXT |
+                                                   BIDI_INFO |
+                                                   GET_GLYPH_METRICS |
+                                                   LAYOUT |
+                                                   UPDATE_LAYOUT_SIZE |
+                                                   REORDER |
+                                                   ALIGN);
+}
+
+void Controller::Impl::ClearStyleData()
+{
+  mModel->mLogicalModel->mColorRuns.Clear();
+  mModel->mLogicalModel->ClearFontDescriptionRuns();
+  mModel->mLogicalModel->ClearStrikethroughRuns();
+}
+
+void Controller::Impl::ResetScrollPosition()
+{
+  if(mEventData)
+  {
+    // Reset the scroll position.
+    mModel->mScrollPosition                = Vector2::ZERO;
+    mEventData->mScrollAfterUpdatePosition = true;
   }
 }