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