8a1dd762f60c308de911f13cb7ea704af01c67d9
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/rendering/renderer.h>
25 #include <dali/devel-api/adaptor-framework/window-devel.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
29 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
30 #include <dali-toolkit/internal/text/character-set-conversion.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/text-control-interface.h>
33 #include <dali-toolkit/internal/text/text-controller-impl-data-clearer.h>
34 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
35 #include <dali-toolkit/internal/text/text-controller-impl-model-updater.h>
36 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
37 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
38 #include <dali-toolkit/internal/text/text-run-container.h>
39 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
40
41 using namespace Dali;
42
43 namespace
44 {
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
47 #endif
48
49 struct BackgroundVertex
50 {
51   Vector2 mPosition; ///< Vertex posiiton
52   Vector4 mColor;    ///< Vertex color
53 };
54
55 struct BackgroundMesh
56 {
57   Vector<BackgroundVertex> mVertices; ///< container of vertices
58   Vector<unsigned short>   mIndices;  ///< container of indices
59 };
60
61 } // namespace
62
63 namespace Dali::Toolkit::Text
64 {
65
66 namespace
67 {
68
69 void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
70 {
71   // Sets the default text's color.
72   inputStyle.textColor      = textColor;
73   inputStyle.isDefaultColor = true;
74
75   inputStyle.familyName.clear();
76   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
77   inputStyle.width  = TextAbstraction::FontWidth::NORMAL;
78   inputStyle.slant  = TextAbstraction::FontSlant::NORMAL;
79   inputStyle.size   = 0.f;
80
81   inputStyle.lineSpacing = 0.f;
82
83   inputStyle.underlineProperties.clear();
84   inputStyle.shadowProperties.clear();
85   inputStyle.embossProperties.clear();
86   inputStyle.outlineProperties.clear();
87
88   inputStyle.isFamilyDefined = false;
89   inputStyle.isWeightDefined = false;
90   inputStyle.isWidthDefined  = false;
91   inputStyle.isSlantDefined  = false;
92   inputStyle.isSizeDefined   = false;
93
94   inputStyle.isLineSpacingDefined = false;
95
96   inputStyle.isUnderlineDefined = false;
97   inputStyle.isShadowDefined    = false;
98   inputStyle.isEmbossDefined    = false;
99   inputStyle.isOutlineDefined   = false;
100
101   // Sets the default font's family name, weight, width, slant and size.
102   if(fontDefaults)
103   {
104     if(fontDefaults->familyDefined)
105     {
106       inputStyle.familyName      = fontDefaults->mFontDescription.family;
107       inputStyle.isFamilyDefined = true;
108     }
109
110     if(fontDefaults->weightDefined)
111     {
112       inputStyle.weight          = fontDefaults->mFontDescription.weight;
113       inputStyle.isWeightDefined = true;
114     }
115
116     if(fontDefaults->widthDefined)
117     {
118       inputStyle.width          = fontDefaults->mFontDescription.width;
119       inputStyle.isWidthDefined = true;
120     }
121
122     if(fontDefaults->slantDefined)
123     {
124       inputStyle.slant          = fontDefaults->mFontDescription.slant;
125       inputStyle.isSlantDefined = true;
126     }
127
128     if(fontDefaults->sizeDefined)
129     {
130       inputStyle.size          = fontDefaults->mDefaultPointSize;
131       inputStyle.isSizeDefined = true;
132     }
133   }
134 }
135
136 void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState)
137 {
138   EventData* eventData = impl.mEventData;
139
140   if(nullptr == eventData)
141   {
142     // Nothing to do if there is no text input.
143     return;
144   }
145
146   DecoratorPtr& decorator = eventData->mDecorator;
147   if(!decorator)
148   {
149     // Nothing to do if there is no decorator.
150     return;
151   }
152
153   DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", eventData->mState, newState);
154
155   if(eventData->mState != newState)
156   {
157     eventData->mPreviousState = eventData->mState;
158     eventData->mState         = newState;
159
160     switch(eventData->mState)
161     {
162       case EventData::INACTIVE:
163       {
164         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
165         decorator->StopCursorBlink();
166         decorator->SetHandleActive(GRAB_HANDLE, false);
167         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
168         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
169         decorator->SetHighlightActive(false);
170         decorator->SetPopupActive(false);
171         eventData->mDecoratorUpdated = true;
172         break;
173       }
174
175       case EventData::INTERRUPTED:
176       {
177         decorator->SetHandleActive(GRAB_HANDLE, false);
178         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
179         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
180         decorator->SetHighlightActive(false);
181         decorator->SetPopupActive(false);
182         eventData->mDecoratorUpdated = true;
183         break;
184       }
185
186       case EventData::SELECTING:
187       {
188         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
189         decorator->StopCursorBlink();
190         decorator->SetHandleActive(GRAB_HANDLE, false);
191         if(eventData->mGrabHandleEnabled)
192         {
193           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
194           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
195         }
196         decorator->SetHighlightActive(true);
197         if(eventData->mGrabHandlePopupEnabled)
198         {
199           impl.SetPopupButtons();
200           decorator->SetPopupActive(true);
201         }
202         eventData->mDecoratorUpdated = true;
203         break;
204       }
205
206       case EventData::EDITING:
207       {
208         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
209         if(eventData->mCursorBlinkEnabled)
210         {
211           decorator->StartCursorBlink();
212         }
213         // Grab handle is not shown until a tap is received whilst EDITING
214         decorator->SetHandleActive(GRAB_HANDLE, false);
215         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
216         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
217         decorator->SetHighlightActive(false);
218         if(eventData->mGrabHandlePopupEnabled)
219         {
220           decorator->SetPopupActive(false);
221         }
222         eventData->mDecoratorUpdated = true;
223         break;
224       }
225       case EventData::EDITING_WITH_POPUP:
226       {
227         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
228
229         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
230         if(eventData->mCursorBlinkEnabled)
231         {
232           decorator->StartCursorBlink();
233         }
234         if(eventData->mSelectionEnabled)
235         {
236           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
237           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
238           decorator->SetHighlightActive(false);
239         }
240         else if(eventData->mGrabHandleEnabled)
241         {
242           decorator->SetHandleActive(GRAB_HANDLE, true);
243         }
244         if(eventData->mGrabHandlePopupEnabled)
245         {
246           impl.SetPopupButtons();
247           decorator->SetPopupActive(true);
248         }
249         eventData->mDecoratorUpdated = true;
250         break;
251       }
252       case EventData::EDITING_WITH_GRAB_HANDLE:
253       {
254         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
255
256         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
257         if(eventData->mCursorBlinkEnabled)
258         {
259           decorator->StartCursorBlink();
260         }
261         // Grab handle is not shown until a tap is received whilst EDITING
262         if(eventData->mGrabHandleEnabled)
263         {
264           decorator->SetHandleActive(GRAB_HANDLE, true);
265         }
266         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
267         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
268         decorator->SetHighlightActive(false);
269         if(eventData->mGrabHandlePopupEnabled)
270         {
271           decorator->SetPopupActive(false);
272         }
273         eventData->mDecoratorUpdated = true;
274         break;
275       }
276
277       case EventData::SELECTION_HANDLE_PANNING:
278       {
279         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
280         decorator->StopCursorBlink();
281         decorator->SetHandleActive(GRAB_HANDLE, false);
282         if(eventData->mGrabHandleEnabled)
283         {
284           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
285           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
286         }
287         decorator->SetHighlightActive(true);
288         if(eventData->mGrabHandlePopupEnabled)
289         {
290           decorator->SetPopupActive(false);
291         }
292         eventData->mDecoratorUpdated = true;
293         break;
294       }
295
296       case EventData::GRAB_HANDLE_PANNING:
297       {
298         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
299
300         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
301         if(eventData->mCursorBlinkEnabled)
302         {
303           decorator->StartCursorBlink();
304         }
305         if(eventData->mGrabHandleEnabled)
306         {
307           decorator->SetHandleActive(GRAB_HANDLE, true);
308         }
309         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
310         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
311         decorator->SetHighlightActive(false);
312         if(eventData->mGrabHandlePopupEnabled)
313         {
314           decorator->SetPopupActive(false);
315         }
316         eventData->mDecoratorUpdated = true;
317         break;
318       }
319
320       case EventData::EDITING_WITH_PASTE_POPUP:
321       {
322         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
323
324         decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
325         if(eventData->mCursorBlinkEnabled)
326         {
327           decorator->StartCursorBlink();
328         }
329
330         if(eventData->mGrabHandleEnabled)
331         {
332           decorator->SetHandleActive(GRAB_HANDLE, true);
333         }
334         decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
335         decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
336         decorator->SetHighlightActive(false);
337
338         if(eventData->mGrabHandlePopupEnabled)
339         {
340           impl.SetPopupButtons();
341           decorator->SetPopupActive(true);
342         }
343         eventData->mDecoratorUpdated = true;
344         break;
345       }
346
347       case EventData::TEXT_PANNING:
348       {
349         decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
350         decorator->StopCursorBlink();
351         decorator->SetHandleActive(GRAB_HANDLE, false);
352         if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
353             decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
354         {
355           decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
356           decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
357           decorator->SetHighlightActive(true);
358         }
359
360         if(eventData->mGrabHandlePopupEnabled)
361         {
362           decorator->SetPopupActive(false);
363         }
364
365         eventData->mDecoratorUpdated = true;
366         break;
367       }
368     }
369   }
370 }
371
372 } // unnamed Namespace
373
374 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
375 : mDecorator(decorator),
376   mInputMethodContext(inputMethodContext),
377   mPlaceholderFont(nullptr),
378   mPlaceholderTextActive(),
379   mPlaceholderTextInactive(),
380   mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
381   mEventQueue(),
382   mInputStyleChangedQueue(),
383   mPreviousState(INACTIVE),
384   mState(INACTIVE),
385   mPrimaryCursorPosition(0u),
386   mLeftSelectionPosition(0u),
387   mRightSelectionPosition(0u),
388   mPreEditStartPosition(0u),
389   mPreEditLength(0u),
390   mCursorHookPositionX(0.f),
391   mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
392   mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
393   mIsShowingPlaceholderText(false),
394   mPreEditFlag(false),
395   mDecoratorUpdated(false),
396   mCursorBlinkEnabled(true),
397   mGrabHandleEnabled(true),
398   mGrabHandlePopupEnabled(true),
399   mSelectionEnabled(true),
400   mUpdateCursorHookPosition(false),
401   mUpdateCursorPosition(false),
402   mUpdateGrabHandlePosition(false),
403   mUpdateLeftSelectionPosition(false),
404   mUpdateRightSelectionPosition(false),
405   mIsLeftHandleSelected(false),
406   mIsRightHandleSelected(false),
407   mUpdateHighlightBox(false),
408   mScrollAfterUpdatePosition(false),
409   mScrollAfterDelete(false),
410   mAllTextSelected(false),
411   mUpdateInputStyle(false),
412   mPasswordInput(false),
413   mCheckScrollAmount(false),
414   mIsPlaceholderPixelSize(false),
415   mIsPlaceholderElideEnabled(false),
416   mPlaceholderEllipsisFlag(false),
417   mShiftSelectionFlag(true),
418   mUpdateAlignment(false),
419   mEditingEnabled(true)
420 {
421 }
422
423 bool Controller::Impl::ProcessInputEvents()
424 {
425   return ControllerImplEventHandler::ProcessInputEvents(*this);
426 }
427
428 void Controller::Impl::NotifyInputMethodContext()
429 {
430   if(mEventData && mEventData->mInputMethodContext)
431   {
432     CharacterIndex cursorPosition = GetLogicalCursorPosition();
433
434     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
435
436     // Update the cursor position by removing the initial white spaces.
437     if(cursorPosition < numberOfWhiteSpaces)
438     {
439       cursorPosition = 0u;
440     }
441     else
442     {
443       cursorPosition -= numberOfWhiteSpaces;
444     }
445
446     mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
447     mEventData->mInputMethodContext.NotifyCursorPosition();
448   }
449 }
450
451 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
452 {
453   if(mEventData && mEventData->mInputMethodContext)
454   {
455     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
456     mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
457   }
458 }
459
460 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
461 {
462   CharacterIndex cursorPosition = 0u;
463
464   if(mEventData)
465   {
466     if((EventData::SELECTING == mEventData->mState) ||
467        (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
468     {
469       cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
470     }
471     else
472     {
473       cursorPosition = mEventData->mPrimaryCursorPosition;
474     }
475   }
476
477   return cursorPosition;
478 }
479
480 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
481 {
482   Length numberOfWhiteSpaces = 0u;
483
484   // Get the buffer to the text.
485   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
486
487   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
488   for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
489   {
490     if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
491     {
492       break;
493     }
494   }
495
496   return numberOfWhiteSpaces;
497 }
498
499 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
500 {
501   // Get the total number of characters.
502   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
503
504   // Retrieve the text.
505   if(0u != numberOfCharacters)
506   {
507     Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
508   }
509 }
510
511 Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const
512 {
513   if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
514      (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
515   {
516     return static_cast<Dali::LayoutDirection::Type>(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
517   }
518   else
519   {
520     return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
521   }
522 }
523
524 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
525 {
526   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
527   mTextUpdateInfo.mStartGlyphIndex         = 0u;
528   mTextUpdateInfo.mStartLineIndex          = 0u;
529   numberOfCharacters                       = 0u;
530
531   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
532   if(0u == numberOfParagraphs)
533   {
534     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
535     numberOfCharacters                       = 0u;
536
537     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
538
539     // Nothing else to do if there are no paragraphs.
540     return;
541   }
542
543   // Find the paragraphs to be updated.
544   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
545   if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
546   {
547     // Text is being added at the end of the current text.
548     if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
549     {
550       // Text is being added in a new paragraph after the last character of the text.
551       mTextUpdateInfo.mParagraphCharacterIndex     = mTextUpdateInfo.mPreviousNumberOfCharacters;
552       numberOfCharacters                           = 0u;
553       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
554
555       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
556       mTextUpdateInfo.mStartLineIndex  = mModel->mVisualModel->mLines.Count() - 1u;
557
558       // Nothing else to do;
559       return;
560     }
561
562     paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
563   }
564   else
565   {
566     Length numberOfCharactersToUpdate = 0u;
567     if(mTextUpdateInfo.mFullRelayoutNeeded)
568     {
569       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
570     }
571     else
572     {
573       numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
574     }
575     mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
576                                           numberOfCharactersToUpdate,
577                                           paragraphsToBeUpdated);
578   }
579
580   if(0u != paragraphsToBeUpdated.Count())
581   {
582     const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
583     const ParagraphRun&     firstParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
584     mTextUpdateInfo.mParagraphCharacterIndex    = firstParagraph.characterRun.characterIndex;
585
586     ParagraphRunIndex   lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
587     const ParagraphRun& lastParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
588
589     if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) &&                                           // Some character are removed.
590        (lastParagraphIndex < numberOfParagraphs - 1u) &&                                               // There is a next paragraph.
591        ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
592         (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
593     {
594       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
595       const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
596
597       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
598     }
599     else
600     {
601       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
602     }
603   }
604
605   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
606   mTextUpdateInfo.mStartGlyphIndex             = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
607 }
608
609 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
610 {
611   ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
612 }
613
614 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
615 {
616   return ControllerImplModelUpdater::Update(*this, operationsRequired);
617 }
618
619 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
620 {
621   SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
622 }
623
624 float Controller::Impl::GetDefaultFontLineHeight()
625 {
626   FontId defaultFontId = 0u;
627   if(nullptr == mFontDefaults)
628   {
629     TextAbstraction::FontDescription fontDescription;
630     defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
631   }
632   else
633   {
634     defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
635   }
636
637   Text::FontMetrics fontMetrics;
638   mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
639
640   return (fontMetrics.ascender - fontMetrics.descender);
641 }
642
643 bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
644 {
645   if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
646   {
647     mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
648     mRecalculateNaturalSize = true;
649
650     RelayoutForNewLineSize();
651     return true;
652   }
653   return false;
654 }
655
656 bool Controller::Impl::SetDefaultLineSize(float lineSize)
657 {
658   if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
659   {
660     mLayoutEngine.SetDefaultLineSize(lineSize);
661     mRecalculateNaturalSize = true;
662
663     RelayoutForNewLineSize();
664     return true;
665   }
666   return false;
667 }
668
669 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
670 {
671   if(nullptr == mEventData)
672   {
673     // Nothing to do if there is no text.
674     return;
675   }
676
677   if(mEventData->mSelectionEnabled && (pStart || pEnd))
678   {
679     uint32_t length   = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
680     uint32_t oldStart = mEventData->mLeftSelectionPosition;
681     uint32_t oldEnd   = mEventData->mRightSelectionPosition;
682
683     if(pStart)
684     {
685       mEventData->mLeftSelectionPosition = std::min(*pStart, length);
686     }
687     if(pEnd)
688     {
689       mEventData->mRightSelectionPosition = std::min(*pEnd, length);
690     }
691
692     if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
693     {
694       ChangeState(EventData::EDITING);
695       mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
696       mEventData->mUpdateCursorPosition                                       = true;
697     }
698     else
699     {
700       ChangeState(EventData::SELECTING);
701       mEventData->mUpdateHighlightBox           = true;
702       mEventData->mUpdateLeftSelectionPosition  = true;
703       mEventData->mUpdateRightSelectionPosition = true;
704     }
705
706     if(mSelectableControlInterface != nullptr)
707     {
708       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
709     }
710   }
711 }
712
713 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
714 {
715   if(nullptr == mEventData)
716   {
717     return 0;
718   }
719   return mEventData->mPrimaryCursorPosition;
720 }
721
722 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
723 {
724   if(nullptr == mEventData)
725   {
726     // Nothing to do if there is no text.
727     return false;
728   }
729
730   if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
731   {
732     // Nothing for same cursor position.
733     return false;
734   }
735
736   uint32_t length                    = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
737   uint32_t oldCursorPos              = mEventData->mPrimaryCursorPosition;
738   mEventData->mPrimaryCursorPosition = std::min(index, length);
739   // If there is no focus, only the value is updated.
740   if(focused)
741   {
742     bool     wasInSelectingState = mEventData->mState == EventData::SELECTING;
743     uint32_t oldStart            = mEventData->mLeftSelectionPosition;
744     uint32_t oldEnd              = mEventData->mRightSelectionPosition;
745     ChangeState(EventData::EDITING);
746     mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
747     mEventData->mUpdateCursorPosition                                        = true;
748
749     if(mSelectableControlInterface != nullptr && wasInSelectingState)
750     {
751       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
752     }
753
754     ScrollTextToMatchCursor();
755   }
756
757   if(nullptr != mEditableControlInterface)
758   {
759     mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
760   }
761
762   return true;
763 }
764
765 Uint32Pair Controller::Impl::GetTextSelectionRange() const
766 {
767   Uint32Pair range;
768
769   if(mEventData)
770   {
771     range.first  = mEventData->mLeftSelectionPosition;
772     range.second = mEventData->mRightSelectionPosition;
773   }
774
775   return range;
776 }
777
778 bool Controller::Impl::IsEditable() const
779 {
780   return mEventData && mEventData->mEditingEnabled;
781 }
782
783 void Controller::Impl::SetEditable(bool editable)
784 {
785   if(mEventData)
786   {
787     mEventData->mEditingEnabled = editable;
788
789     if(mEventData->mDecorator)
790     {
791       mEventData->mDecorator->SetEditable(editable);
792     }
793   }
794 }
795
796 void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont)
797 {
798   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
799
800   if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes
801   {
802     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str());
803     mFontDefaults->mFontDescription.family = newDefaultFont;
804
805     ClearFontData();
806
807     RequestRelayout();
808   }
809 }
810
811 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
812 {
813   if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
814   {
815     // Nothing to select if handles are in the same place.
816     selectedText.clear();
817     return;
818   }
819
820   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
821
822   //Get start and end position of selection
823   const CharacterIndex startOfSelectedText  = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
824   const Length         lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
825
826   Vector<Character>& utf32Characters    = mModel->mLogicalModel->mText;
827   const Length       numberOfCharacters = utf32Characters.Count();
828
829   // Validate the start and end selection points
830   if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
831   {
832     //Get text as a UTF8 string
833     Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
834
835     if(deleteAfterRetrieval) // Only delete text if copied successfully
836     {
837       // Keep a copy of the current input style.
838       InputStyle currentInputStyle;
839       currentInputStyle.Copy(mEventData->mInputStyle);
840
841       // Set as input style the style of the first deleted character.
842       mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
843
844       // Compare if the input style has changed.
845       const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
846
847       if(hasInputStyleChanged)
848       {
849         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
850         // Queue the input style changed signal.
851         mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
852       }
853
854       mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
855
856       // Mark the paragraphs to be updated.
857       if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
858       {
859         mTextUpdateInfo.mCharacterIndex             = 0;
860         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
861         mTextUpdateInfo.mNumberOfCharactersToAdd    = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
862         mTextUpdateInfo.mClearAll                   = true;
863       }
864       else
865       {
866         mTextUpdateInfo.mCharacterIndex             = startOfSelectedText;
867         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
868       }
869
870       // Delete text between handles
871       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
872       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
873       utf32Characters.Erase(first, last);
874
875       // Will show the cursor at the first character of the selection.
876       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
877     }
878     else
879     {
880       // Will show the cursor at the last character of the selection.
881       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
882     }
883
884     mEventData->mDecoratorUpdated = true;
885   }
886 }
887
888 void Controller::Impl::SetSelection(int start, int end)
889 {
890   uint32_t oldStart = mEventData->mLeftSelectionPosition;
891   uint32_t oldEnd   = mEventData->mRightSelectionPosition;
892
893   mEventData->mLeftSelectionPosition  = start;
894   mEventData->mRightSelectionPosition = end;
895   mEventData->mUpdateCursorPosition   = true;
896
897   if(mSelectableControlInterface != nullptr)
898   {
899     mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
900   }
901 }
902
903 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
904 {
905   return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
906 }
907
908 void Controller::Impl::ShowClipboard()
909 {
910   if(mClipboard)
911   {
912     mClipboard.ShowClipboard();
913   }
914 }
915
916 void Controller::Impl::HideClipboard()
917 {
918   if(mClipboard && mClipboardHideEnabled)
919   {
920     mClipboard.HideClipboard();
921   }
922 }
923
924 void Controller::Impl::SetClipboardHideEnable(bool enable)
925 {
926   mClipboardHideEnabled = enable;
927 }
928
929 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
930 {
931   //Send string to clipboard
932   return (mClipboard && mClipboard.SetItem(source));
933 }
934
935 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
936 {
937   std::string selectedText;
938   RetrieveSelection(selectedText, deleteAfterSending);
939   CopyStringToClipboard(selectedText);
940   ChangeState(EventData::EDITING);
941 }
942
943 void Controller::Impl::RequestGetTextFromClipboard()
944 {
945   if(mClipboard)
946   {
947     mClipboard.RequestItem();
948   }
949 }
950
951 void Controller::Impl::RepositionSelectionHandles()
952 {
953   SelectionHandleController::Reposition(*this);
954 }
955 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
956 {
957   SelectionHandleController::Reposition(*this, visualX, visualY, action);
958 }
959
960 void Controller::Impl::SetPopupButtons()
961 {
962   /**
963    *  Sets the Popup buttons to be shown depending on State.
964    *
965    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
966    *
967    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
968    */
969
970   bool                        isEditable    = IsEditable();
971   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
972
973   if(EventData::SELECTING == mEventData->mState)
974   {
975     buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
976     if(isEditable)
977     {
978       buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
979     }
980
981     if(!IsClipboardEmpty())
982     {
983       if(isEditable)
984       {
985         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
986       }
987       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
988     }
989
990     if(!mEventData->mAllTextSelected)
991     {
992       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
993     }
994   }
995   else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
996   {
997     if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
998     {
999       buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1000     }
1001
1002     if(!IsClipboardEmpty())
1003     {
1004       if(isEditable)
1005       {
1006         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1007       }
1008       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1009     }
1010   }
1011   else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1012   {
1013     if(!IsClipboardEmpty())
1014     {
1015       if(isEditable)
1016       {
1017         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1018       }
1019       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1020     }
1021   }
1022
1023   mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1024 }
1025
1026 void Controller::Impl::ChangeState(EventData::State newState)
1027 {
1028   ChangeTextControllerState(*this, newState);
1029 }
1030
1031 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1032                                          CursorInfo&    cursorInfo)
1033 {
1034   if(!IsShowingRealText())
1035   {
1036     // Do not want to use the place-holder text to set the cursor position.
1037
1038     // Use the line's height of the font's family set to set the cursor's size.
1039     // If there is no font's family set, use the default font.
1040     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1041
1042     cursorInfo.lineOffset          = 0.f;
1043     cursorInfo.lineHeight          = GetDefaultFontLineHeight();
1044     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1045
1046     bool isRTL = false;
1047     if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1048     {
1049       isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1050     }
1051
1052     switch(mModel->mHorizontalAlignment)
1053     {
1054       case Text::HorizontalAlignment::BEGIN:
1055       {
1056         if(isRTL)
1057         {
1058           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1059         }
1060         else
1061         {
1062           cursorInfo.primaryPosition.x = 0.f;
1063         }
1064         break;
1065       }
1066       case Text::HorizontalAlignment::CENTER:
1067       {
1068         cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1069         break;
1070       }
1071       case Text::HorizontalAlignment::END:
1072       {
1073         if(isRTL)
1074         {
1075           cursorInfo.primaryPosition.x = 0.f;
1076         }
1077         else
1078         {
1079           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1080         }
1081         break;
1082       }
1083     }
1084
1085     // Nothing else to do.
1086     return;
1087   }
1088
1089   const bool                  isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1090   GetCursorPositionParameters parameters;
1091   parameters.visualModel  = mModel->mVisualModel;
1092   parameters.logicalModel = mModel->mLogicalModel;
1093   parameters.metrics      = mMetrics;
1094   parameters.logical      = logical;
1095   parameters.isMultiline  = isMultiLine;
1096
1097   Text::GetCursorPosition(parameters,
1098                           cursorInfo);
1099
1100   // Adds Outline offset.
1101   const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1102   cursorInfo.primaryPosition.x += outlineWidth;
1103   cursorInfo.primaryPosition.y += outlineWidth;
1104   cursorInfo.secondaryPosition.x += outlineWidth;
1105   cursorInfo.secondaryPosition.y += outlineWidth;
1106
1107   if(isMultiLine)
1108   {
1109     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1110
1111     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1112     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1113
1114     if(0.f > cursorInfo.primaryPosition.x)
1115     {
1116       cursorInfo.primaryPosition.x = 0.f;
1117     }
1118
1119     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1120     if(cursorInfo.primaryPosition.x > edgeWidth)
1121     {
1122       cursorInfo.primaryPosition.x = edgeWidth;
1123     }
1124   }
1125 }
1126
1127 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1128 {
1129   if(nullptr == mEventData)
1130   {
1131     // Nothing to do if there is no text input.
1132     return 0u;
1133   }
1134
1135   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1136
1137   const GlyphIndex* const charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
1138   const Length* const     charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1139
1140   GlyphIndex glyphIndex         = *(charactersToGlyphBuffer + index);
1141   Length     numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1142
1143   if(numberOfCharacters > 1u)
1144   {
1145     const Script script = mModel->mLogicalModel->GetScript(index);
1146     if(HasLigatureMustBreak(script))
1147     {
1148       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1149       numberOfCharacters = 1u;
1150     }
1151   }
1152   else
1153   {
1154     while(0u == numberOfCharacters)
1155     {
1156       ++glyphIndex;
1157       numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1158     }
1159   }
1160
1161   if(index < mEventData->mPrimaryCursorPosition)
1162   {
1163     cursorIndex -= numberOfCharacters;
1164   }
1165   else
1166   {
1167     cursorIndex += numberOfCharacters;
1168   }
1169
1170   // Will update the cursor hook position.
1171   mEventData->mUpdateCursorHookPosition = true;
1172
1173   return cursorIndex;
1174 }
1175
1176 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1177 {
1178   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1179   if(nullptr == mEventData)
1180   {
1181     // Nothing to do if there is no text input.
1182     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1183     return;
1184   }
1185
1186   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1187
1188   mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1189
1190   // Sets the cursor position.
1191   mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1192                                       cursorPosition.x,
1193                                       cursorPosition.y,
1194                                       cursorInfo.primaryCursorHeight,
1195                                       cursorInfo.lineHeight);
1196   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1197
1198   if(mEventData->mUpdateGrabHandlePosition)
1199   {
1200     // Sets the grab handle position.
1201     mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1202                                         cursorPosition.x,
1203                                         cursorInfo.lineOffset + mModel->mScrollPosition.y,
1204                                         cursorInfo.lineHeight);
1205   }
1206
1207   if(cursorInfo.isSecondaryCursor)
1208   {
1209     mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1210                                         cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1211                                         cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1212                                         cursorInfo.secondaryCursorHeight,
1213                                         cursorInfo.lineHeight);
1214     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1215   }
1216
1217   // Set which cursors are active according the state.
1218   if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1219   {
1220     if(cursorInfo.isSecondaryCursor)
1221     {
1222       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1223     }
1224     else
1225     {
1226       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1227     }
1228   }
1229   else
1230   {
1231     mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1232   }
1233
1234   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1235 }
1236
1237 void Controller::Impl::UpdateSelectionHandle(HandleType        handleType,
1238                                              const CursorInfo& cursorInfo)
1239 {
1240   SelectionHandleController::Update(*this, handleType, cursorInfo);
1241 }
1242
1243 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1244 {
1245   // Clamp between -space & -alignment offset.
1246
1247   if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1248   {
1249     const float space         = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1250     mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1251     mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1252
1253     mEventData->mDecoratorUpdated = true;
1254   }
1255   else
1256   {
1257     mModel->mScrollPosition.x = 0.f;
1258   }
1259 }
1260
1261 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1262 {
1263   if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1264   {
1265     // Nothing to do if the text is single line.
1266     return;
1267   }
1268
1269   // Clamp between -space & 0.
1270   if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1271   {
1272     const float space         = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1273     mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1274     mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1275
1276     mEventData->mDecoratorUpdated = true;
1277   }
1278   else
1279   {
1280     mModel->mScrollPosition.y = 0.f;
1281   }
1282 }
1283
1284 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1285 {
1286   const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1287
1288   // position is in actor's coords.
1289   const float positionEndX = position.x + cursorWidth;
1290   const float positionEndY = position.y + lineHeight;
1291
1292   // Transform the position to decorator coords.
1293   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1294   const float decoratorPositionEndX   = positionEndX + mModel->mScrollPosition.x;
1295
1296   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1297   const float decoratorPositionEndY   = positionEndY + mModel->mScrollPosition.y;
1298
1299   if(decoratorPositionBeginX < 0.f)
1300   {
1301     mModel->mScrollPosition.x = -position.x;
1302   }
1303   else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1304   {
1305     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1306   }
1307
1308   if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1309   {
1310     if(decoratorPositionBeginY < 0.f)
1311     {
1312       mModel->mScrollPosition.y = -position.y;
1313     }
1314     else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1315     {
1316       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1317     }
1318   }
1319 }
1320
1321 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1322 {
1323   // Get the current cursor position in decorator coords.
1324   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1325
1326   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1327
1328   // Calculate the offset to match the cursor position before the character was deleted.
1329   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1330
1331   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1332   if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1333   {
1334     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1335     mModel->mScrollPosition.y            = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1336   }
1337
1338   ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1339   ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1340
1341   // Makes the new cursor position visible if needed.
1342   ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1343 }
1344
1345 void Controller::Impl::ScrollTextToMatchCursor()
1346 {
1347   CursorInfo cursorInfo;
1348   GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1349   ScrollTextToMatchCursor(cursorInfo);
1350 }
1351
1352 void Controller::Impl::RequestRelayout()
1353 {
1354   if(nullptr != mControlInterface)
1355   {
1356     mControlInterface->RequestTextRelayout();
1357   }
1358 }
1359
1360 Actor Controller::Impl::CreateBackgroundActor()
1361 {
1362   // NOTE: Currently we only support background color for left-to-right text.
1363
1364   Actor actor;
1365
1366   Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1367   if(numberOfGlyphs > 0u)
1368   {
1369     Vector<GlyphInfo> glyphs;
1370     glyphs.Resize(numberOfGlyphs);
1371
1372     Vector<Vector2> positions;
1373     positions.Resize(numberOfGlyphs);
1374
1375     // Get the line where the glyphs are laid-out.
1376     const LineRun* lineRun         = mModel->mVisualModel->mLines.Begin();
1377     float          alignmentOffset = lineRun->alignmentOffset;
1378     numberOfGlyphs                 = mView.GetGlyphs(glyphs.Begin(),
1379                                      positions.Begin(),
1380                                      alignmentOffset,
1381                                      0u,
1382                                      numberOfGlyphs);
1383
1384     glyphs.Resize(numberOfGlyphs);
1385     positions.Resize(numberOfGlyphs);
1386
1387     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
1388     const Vector2* const   positionsBuffer = positions.Begin();
1389
1390     BackgroundMesh mesh;
1391     mesh.mVertices.Reserve(4u * glyphs.Size());
1392     mesh.mIndices.Reserve(6u * glyphs.Size());
1393
1394     const Vector2 textSize = mView.GetLayoutSize();
1395
1396     const float offsetX = alignmentOffset + textSize.width * 0.5f;
1397     const float offsetY = textSize.height * 0.5f;
1398
1399     const Vector4* const    backgroundColorsBuffer       = mView.GetBackgroundColors();
1400     const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
1401     const Vector4&          defaultBackgroundColor       = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
1402
1403     Vector4   quad;
1404     uint32_t  numberOfQuads = 0u;
1405     Length    yLineOffset   = 0;
1406     Length    prevLineIndex = 0;
1407     LineIndex lineIndex;
1408     Length    numberOfLines;
1409
1410     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
1411     {
1412       const GlyphInfo& glyph = *(glyphsBuffer + i);
1413
1414       // Get the background color of the character.
1415       // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
1416       const bool       isMarkupBackground       = mView.IsMarkupBackgroundColorSet();
1417       const ColorIndex backgroundColorIndex     = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
1418       const bool       isDefaultBackgroundColor = (0u == backgroundColorIndex);
1419       const Vector4&   backgroundColor          = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
1420
1421       mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
1422       Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
1423
1424       if(lineIndex != prevLineIndex)
1425       {
1426         yLineOffset += lineHeight;
1427       }
1428
1429       // Only create quads for glyphs with a background color
1430       if(backgroundColor != Color::TRANSPARENT)
1431       {
1432         const Vector2 position = *(positionsBuffer + i);
1433
1434         if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
1435         {
1436           quad.x = position.x;
1437           quad.y = yLineOffset;
1438           quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1439           quad.w = lineHeight;
1440         }
1441         else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
1442         {
1443           quad.x = position.x;
1444           quad.y = yLineOffset;
1445           quad.z = quad.x - glyph.xBearing + glyph.advance;
1446           quad.w = quad.y + lineHeight;
1447         }
1448         else if(i == glyphSize - 1u) // The last glyph in the whole text
1449         {
1450           quad.x = position.x - glyph.xBearing;
1451           quad.y = yLineOffset;
1452           quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1453           quad.w = quad.y + lineHeight;
1454         }
1455         else // The glyph in the middle of the text
1456         {
1457           quad.x = position.x - glyph.xBearing;
1458           quad.y = yLineOffset;
1459           quad.z = quad.x + glyph.advance;
1460           quad.w = quad.y + lineHeight;
1461         }
1462
1463         BackgroundVertex vertex;
1464
1465         // Top left
1466         vertex.mPosition.x = quad.x - offsetX;
1467         vertex.mPosition.y = quad.y - offsetY;
1468         vertex.mColor      = backgroundColor;
1469         mesh.mVertices.PushBack(vertex);
1470
1471         // Top right
1472         vertex.mPosition.x = quad.z - offsetX;
1473         vertex.mPosition.y = quad.y - offsetY;
1474         vertex.mColor      = backgroundColor;
1475         mesh.mVertices.PushBack(vertex);
1476
1477         // Bottom left
1478         vertex.mPosition.x = quad.x - offsetX;
1479         vertex.mPosition.y = quad.w - offsetY;
1480         vertex.mColor      = backgroundColor;
1481         mesh.mVertices.PushBack(vertex);
1482
1483         // Bottom right
1484         vertex.mPosition.x = quad.z - offsetX;
1485         vertex.mPosition.y = quad.w - offsetY;
1486         vertex.mColor      = backgroundColor;
1487         mesh.mVertices.PushBack(vertex);
1488
1489         // Six indices in counter clockwise winding
1490         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1491         mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
1492         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1493         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1494         mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
1495         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1496
1497         numberOfQuads++;
1498       }
1499
1500       if(lineIndex != prevLineIndex)
1501       {
1502         prevLineIndex = lineIndex;
1503       }
1504     }
1505
1506     // Only create the background actor if there are glyphs with background color
1507     if(mesh.mVertices.Count() > 0u)
1508     {
1509       Property::Map quadVertexFormat;
1510       quadVertexFormat["aPosition"] = Property::VECTOR2;
1511       quadVertexFormat["aColor"]    = Property::VECTOR4;
1512
1513       VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
1514       quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
1515
1516       Geometry quadGeometry = Geometry::New();
1517       quadGeometry.AddVertexBuffer(quadVertices);
1518       quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
1519
1520       if(!mShaderBackground)
1521       {
1522         mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
1523       }
1524
1525       Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
1526       renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
1527       renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
1528
1529       actor = Actor::New();
1530       actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
1531       actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1532       actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1533       actor.SetProperty(Actor::Property::SIZE, textSize);
1534       actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
1535       actor.AddRenderer(renderer);
1536     }
1537   }
1538
1539   return actor;
1540 }
1541
1542 void Controller::Impl::RelayoutForNewLineSize()
1543 {
1544   // relayout all characters
1545   mTextUpdateInfo.mCharacterIndex             = 0;
1546   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1547   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1548   mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
1549
1550   //remove selection
1551   if(mEventData && mEventData->mState == EventData::SELECTING)
1552   {
1553     ChangeState(EventData::EDITING);
1554   }
1555
1556   RequestRelayout();
1557 }
1558
1559 bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
1560 {
1561   return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
1562 }
1563
1564 void Controller::Impl::ProcessInputStyleChangedSignals()
1565 {
1566   if(mEventData)
1567   {
1568     if(mEditableControlInterface)
1569     {
1570       // Emit the input style changed signal for each mask
1571       std::for_each(mEventData->mInputStyleChangedQueue.begin(),
1572                     mEventData->mInputStyleChangedQueue.end(),
1573                     [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); } );
1574     }
1575
1576     mEventData->mInputStyleChangedQueue.Clear();
1577   }
1578 }
1579
1580 void Controller::Impl::ScrollBy(Vector2 scroll)
1581 {
1582   if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
1583   {
1584     const Vector2& layoutSize    = mModel->mVisualModel->GetLayoutSize();
1585     const Vector2  currentScroll = mModel->mScrollPosition;
1586
1587     scroll.x = -scroll.x;
1588     scroll.y = -scroll.y;
1589
1590     if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
1591     {
1592       mModel->mScrollPosition.x += scroll.x;
1593       ClampHorizontalScroll(layoutSize);
1594     }
1595
1596     if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
1597     {
1598       mModel->mScrollPosition.y += scroll.y;
1599       ClampVerticalScroll(layoutSize);
1600     }
1601
1602     if(mModel->mScrollPosition != currentScroll)
1603     {
1604       mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
1605       RequestRelayout();
1606     }
1607   }
1608 }
1609
1610 float Controller::Impl::GetHorizontalScrollPosition()
1611 {
1612   // Scroll values are negative internally so we convert them to positive numbers
1613   return mEventData ? -mModel->mScrollPosition.x : 0.0f;
1614 }
1615
1616 float Controller::Impl::GetVerticalScrollPosition()
1617 {
1618   // Scroll values are negative internally so we convert them to positive numbers
1619   return mEventData ? -mModel->mScrollPosition.y : 0.0f;
1620 }
1621
1622 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1623 {
1624   //Underlined character runs for markup-processor
1625   const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1626   const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
1627   const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
1628
1629   if(shouldClearPreUnderlineRuns)
1630   {
1631     mModel->mVisualModel->mUnderlineRuns.Clear();
1632   }
1633
1634   for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1635   {
1636     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1637     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1638     for(Length index = 0u; index < numberOfCharacters; index++)
1639     {
1640       GlyphRun underlineGlyphRun;
1641       underlineGlyphRun.glyphIndex     = charactersToGlyph[characterIndex + index];
1642       underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
1643       mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1644     }
1645   }
1646 }
1647
1648 void Controller::Impl::SetAutoScrollEnabled(bool enable)
1649 {
1650   if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
1651   {
1652     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1653                                                      LAYOUT |
1654                                                      ALIGN |
1655                                                      UPDATE_LAYOUT_SIZE |
1656                                                      REORDER);
1657
1658     if(enable)
1659     {
1660       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
1661       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
1662     }
1663     else
1664     {
1665       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
1666     }
1667
1668     mIsAutoScrollEnabled = enable;
1669     RequestRelayout();
1670   }
1671   else
1672   {
1673     DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
1674     mIsAutoScrollEnabled = false;
1675   }
1676 }
1677
1678 void Controller::Impl::SetEnableCursorBlink(bool enable)
1679 {
1680   DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
1681
1682   if(mEventData)
1683   {
1684     mEventData->mCursorBlinkEnabled = enable;
1685
1686     if(!enable && mEventData->mDecorator)
1687     {
1688       mEventData->mDecorator->StopCursorBlink();
1689     }
1690   }
1691 }
1692
1693 void Controller::Impl::SetMultiLineEnabled(bool enable)
1694 {
1695   const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
1696
1697   if(layout != mLayoutEngine.GetLayout())
1698   {
1699     // Set the layout type.
1700     mLayoutEngine.SetLayout(layout);
1701
1702     // Set the flags to redo the layout operations
1703     const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
1704                                                                         UPDATE_LAYOUT_SIZE |
1705                                                                         ALIGN |
1706                                                                         REORDER);
1707
1708     mTextUpdateInfo.mFullRelayoutNeeded = true;
1709     mOperationsPending                  = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
1710
1711     // Need to recalculate natural size
1712     mRecalculateNaturalSize = true;
1713
1714     RequestRelayout();
1715   }
1716 }
1717
1718 void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
1719 {
1720   if(alignment != mModel->mHorizontalAlignment)
1721   {
1722     // Set the alignment.
1723     mModel->mHorizontalAlignment = alignment;
1724
1725     // Set the flag to redo the alignment operation.
1726     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
1727
1728     if(mEventData)
1729     {
1730       mEventData->mUpdateAlignment = true;
1731
1732       // Update the cursor if it's in editing mode
1733       if(EventData::IsEditingState(mEventData->mState))
1734       {
1735         ChangeState(EventData::EDITING);
1736         mEventData->mUpdateCursorPosition = true;
1737       }
1738     }
1739
1740     RequestRelayout();
1741   }
1742 }
1743
1744 void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
1745 {
1746   if(alignment != mModel->mVerticalAlignment)
1747   {
1748     // Set the alignment.
1749     mModel->mVerticalAlignment = alignment;
1750     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
1751     RequestRelayout();
1752   }
1753 }
1754
1755 void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
1756 {
1757   if(lineWrapMode != mModel->mLineWrapMode)
1758   {
1759     // Update Text layout for applying wrap mode
1760     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1761                                                      ALIGN |
1762                                                      LAYOUT |
1763                                                      UPDATE_LAYOUT_SIZE |
1764                                                      REORDER);
1765
1766     if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
1767        (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
1768     {
1769       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
1770     }
1771
1772     // Set the text wrap mode.
1773     mModel->mLineWrapMode = lineWrapMode;
1774
1775     mTextUpdateInfo.mCharacterIndex             = 0u;
1776     mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1777     mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1778
1779     // Request relayout
1780     RequestRelayout();
1781   }
1782 }
1783
1784 void Controller::Impl::SetDefaultColor(const Vector4& color)
1785 {
1786   mTextColor = color;
1787
1788   if(!IsShowingPlaceholderText())
1789   {
1790     mModel->mVisualModel->SetTextColor(color);
1791     mModel->mLogicalModel->mColorRuns.Clear();
1792     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
1793     RequestRelayout();
1794   }
1795 }
1796
1797 void Controller::Impl::ClearFontData()
1798 {
1799   if(mFontDefaults)
1800   {
1801     mFontDefaults->mFontId = 0u; // Remove old font ID
1802   }
1803
1804   // Set flags to update the model.
1805   mTextUpdateInfo.mCharacterIndex             = 0u;
1806   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1807   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1808
1809   mTextUpdateInfo.mClearAll           = true;
1810   mTextUpdateInfo.mFullRelayoutNeeded = true;
1811   mRecalculateNaturalSize             = true;
1812
1813   mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1814                                                    VALIDATE_FONTS |
1815                                                    SHAPE_TEXT |
1816                                                    BIDI_INFO |
1817                                                    GET_GLYPH_METRICS |
1818                                                    LAYOUT |
1819                                                    UPDATE_LAYOUT_SIZE |
1820                                                    REORDER |
1821                                                    ALIGN);
1822 }
1823
1824 void Controller::Impl::ClearStyleData()
1825 {
1826   mModel->mLogicalModel->mColorRuns.Clear();
1827   mModel->mLogicalModel->ClearFontDescriptionRuns();
1828 }
1829
1830
1831 void Controller::Impl::ResetScrollPosition()
1832 {
1833   if(mEventData)
1834   {
1835     // Reset the scroll position.
1836     mModel->mScrollPosition                = Vector2::ZERO;
1837     mEventData->mScrollAfterUpdatePosition = true;
1838   }
1839 }
1840
1841 } // namespace Dali::Toolkit::Text