Moved Text Controller & Markup Processor to sub-folders
[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 std::string 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
448     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
449
450     // Update the cursor position by removing the initial white spaces.
451     if(cursorPosition < numberOfWhiteSpaces)
452     {
453       cursorPosition = 0u;
454     }
455     else
456     {
457       cursorPosition -= numberOfWhiteSpaces;
458     }
459
460     mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
461     mEventData->mInputMethodContext.NotifyCursorPosition();
462   }
463 }
464
465 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
466 {
467   if(mEventData && mEventData->mInputMethodContext)
468   {
469     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
470     mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
471   }
472 }
473
474 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
475 {
476   CharacterIndex cursorPosition = 0u;
477
478   if(mEventData)
479   {
480     if((EventData::SELECTING == mEventData->mState) ||
481        (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
482     {
483       cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
484     }
485     else
486     {
487       cursorPosition = mEventData->mPrimaryCursorPosition;
488     }
489   }
490
491   return cursorPosition;
492 }
493
494 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
495 {
496   Length numberOfWhiteSpaces = 0u;
497
498   // Get the buffer to the text.
499   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
500
501   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
502   for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
503   {
504     if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
505     {
506       break;
507     }
508   }
509
510   return numberOfWhiteSpaces;
511 }
512
513 void Controller::Impl::GetText(std::string& text) const
514 {
515   if(!IsShowingPlaceholderText())
516   {
517     // Retrieves the text string.
518     GetText(0u, text);
519   }
520   else
521   {
522     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
523   }
524 }
525
526 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
527 {
528   // Get the total number of characters.
529   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
530
531   // Retrieve the text.
532   if(0u != numberOfCharacters)
533   {
534     Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
535   }
536 }
537
538 Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const
539 {
540   if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
541      (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
542   {
543     Window window = DevelWindow::Get(actor);
544     return static_cast<Dali::LayoutDirection::Type>(window ? window.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
545   }
546   else
547   {
548     return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
549   }
550 }
551
552 Toolkit::DevelText::TextDirection::Type Controller::Impl::GetTextDirection()
553 {
554   if(mUpdateTextDirection)
555   {
556     // Operations that can be done only once until the text changes.
557     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
558                                                                           GET_SCRIPTS |
559                                                                           VALIDATE_FONTS |
560                                                                           GET_LINE_BREAKS |
561                                                                           BIDI_INFO |
562                                                                           SHAPE_TEXT |
563                                                                           GET_GLYPH_METRICS);
564
565     // Set the update info to relayout the whole text.
566     mTextUpdateInfo.mParagraphCharacterIndex     = 0u;
567     mTextUpdateInfo.mRequestedNumberOfCharacters = mModel->mLogicalModel->mText.Count();
568
569     // Make sure the model is up-to-date before layouting
570     UpdateModel(onlyOnceOperations);
571
572     Vector3 naturalSize;
573     Relayouter::DoRelayout(*this,
574                            Size(MAX_FLOAT, MAX_FLOAT),
575                            static_cast<OperationsMask>(onlyOnceOperations |
576                                                        LAYOUT | REORDER | UPDATE_DIRECTION),
577                            naturalSize.GetVectorXY());
578
579     // Do not do again the only once operations.
580     mOperationsPending = static_cast<OperationsMask>(mOperationsPending & ~onlyOnceOperations);
581
582     // Clear the update info. This info will be set the next time the text is updated.
583     mTextUpdateInfo.Clear();
584
585     // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
586     mTextUpdateInfo.mFullRelayoutNeeded = true;
587
588     mUpdateTextDirection = false;
589   }
590
591   return mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
592 }
593
594 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
595 {
596   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
597   mTextUpdateInfo.mStartGlyphIndex         = 0u;
598   mTextUpdateInfo.mStartLineIndex          = 0u;
599   numberOfCharacters                       = 0u;
600
601   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
602   if(0u == numberOfParagraphs)
603   {
604     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
605     numberOfCharacters                       = 0u;
606
607     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
608
609     // Nothing else to do if there are no paragraphs.
610     return;
611   }
612
613   // Find the paragraphs to be updated.
614   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
615   if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
616   {
617     // Text is being added at the end of the current text.
618     if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
619     {
620       // Text is being added in a new paragraph after the last character of the text.
621       mTextUpdateInfo.mParagraphCharacterIndex     = mTextUpdateInfo.mPreviousNumberOfCharacters;
622       numberOfCharacters                           = 0u;
623       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
624
625       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
626       mTextUpdateInfo.mStartLineIndex  = mModel->mVisualModel->mLines.Count() - 1u;
627
628       // Nothing else to do;
629       return;
630     }
631
632     paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
633   }
634   else
635   {
636     Length numberOfCharactersToUpdate = 0u;
637     if(mTextUpdateInfo.mFullRelayoutNeeded)
638     {
639       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
640     }
641     else
642     {
643       numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
644     }
645     mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
646                                           numberOfCharactersToUpdate,
647                                           paragraphsToBeUpdated);
648   }
649
650   if(0u != paragraphsToBeUpdated.Count())
651   {
652     const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
653     const ParagraphRun&     firstParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
654     mTextUpdateInfo.mParagraphCharacterIndex    = firstParagraph.characterRun.characterIndex;
655
656     ParagraphRunIndex   lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
657     const ParagraphRun& lastParagraph      = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
658
659     if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) &&                                           // Some character are removed.
660        (lastParagraphIndex < numberOfParagraphs - 1u) &&                                               // There is a next paragraph.
661        ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
662         (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
663     {
664       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
665       const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
666
667       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
668     }
669     else
670     {
671       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
672     }
673   }
674
675   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
676   mTextUpdateInfo.mStartGlyphIndex             = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
677 }
678
679 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
680 {
681   ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
682 }
683
684 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
685 {
686   return ControllerImplModelUpdater::Update(*this, operationsRequired);
687 }
688
689 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
690 {
691   SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
692 }
693
694 float Controller::Impl::GetDefaultFontLineHeight()
695 {
696   FontId defaultFontId = 0u;
697   if(nullptr == mFontDefaults)
698   {
699     TextAbstraction::FontDescription fontDescription;
700     defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * GetFontSizeScale());
701   }
702   else
703   {
704     defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale());
705   }
706
707   Text::FontMetrics fontMetrics;
708   mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
709
710   return (fontMetrics.ascender - fontMetrics.descender);
711 }
712
713 bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
714 {
715   if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
716   {
717     mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
718
719     RelayoutAllCharacters();
720     return true;
721   }
722   return false;
723 }
724
725 bool Controller::Impl::SetDefaultLineSize(float lineSize)
726 {
727   if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
728   {
729     mLayoutEngine.SetDefaultLineSize(lineSize);
730
731     RelayoutAllCharacters();
732     return true;
733   }
734   return false;
735 }
736
737 bool Controller::Impl::SetRelativeLineSize(float relativeLineSize)
738 {
739   if(std::fabs(relativeLineSize - GetRelativeLineSize()) > Math::MACHINE_EPSILON_1000)
740   {
741     mLayoutEngine.SetRelativeLineSize(relativeLineSize);
742
743     RelayoutAllCharacters();
744     return true;
745   }
746   return false;
747 }
748
749 float Controller::Impl::GetRelativeLineSize()
750 {
751   return mLayoutEngine.GetRelativeLineSize();
752 }
753
754 string Controller::Impl::GetSelectedText()
755 {
756   string text;
757   if(EventData::SELECTING == mEventData->mState)
758   {
759     RetrieveSelection(text, false);
760   }
761   return text;
762 }
763
764 string Controller::Impl::CopyText()
765 {
766   string text;
767   RetrieveSelection(text, false);
768   SendSelectionToClipboard(false); // Text not modified
769
770   mEventData->mUpdateCursorPosition = true;
771
772   RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
773
774   return text;
775 }
776
777 string Controller::Impl::CutText()
778 {
779   string text;
780   RetrieveSelection(text, false);
781
782   if(!IsEditable())
783   {
784     return EMPTY_STRING;
785   }
786
787   SendSelectionToClipboard(true); // Synchronous call to modify text
788   mOperationsPending = ALL_OPERATIONS;
789
790   if((0u != mModel->mLogicalModel->mText.Count()) ||
791      !IsPlaceholderAvailable())
792   {
793     QueueModifyEvent(ModifyEvent::TEXT_DELETED);
794   }
795   else
796   {
797     PlaceholderHandler::ShowPlaceholderText(*this);
798   }
799
800   mEventData->mUpdateCursorPosition = true;
801   mEventData->mScrollAfterDelete    = true;
802
803   RequestRelayout();
804
805   if(nullptr != mEditableControlInterface)
806   {
807     mEditableControlInterface->TextChanged(true);
808   }
809   return text;
810 }
811
812 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
813 {
814   if(nullptr == mEventData)
815   {
816     // Nothing to do if there is no text.
817     return;
818   }
819
820   if(mEventData->mSelectionEnabled && (pStart || pEnd))
821   {
822     uint32_t length   = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
823     uint32_t oldStart = mEventData->mLeftSelectionPosition;
824     uint32_t oldEnd   = mEventData->mRightSelectionPosition;
825
826     if(pStart)
827     {
828       mEventData->mLeftSelectionPosition = std::min(*pStart, length);
829     }
830     if(pEnd)
831     {
832       mEventData->mRightSelectionPosition = std::min(*pEnd, length);
833     }
834
835     if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
836     {
837       ChangeState(EventData::EDITING);
838       mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
839       mEventData->mUpdateCursorPosition                                       = true;
840     }
841     else
842     {
843       ChangeState(EventData::SELECTING);
844       mEventData->mUpdateHighlightBox           = true;
845       mEventData->mUpdateLeftSelectionPosition  = true;
846       mEventData->mUpdateRightSelectionPosition = true;
847     }
848
849     if(mSelectableControlInterface != nullptr)
850     {
851       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
852     }
853   }
854 }
855
856 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
857 {
858   if(nullptr == mEventData)
859   {
860     return 0;
861   }
862   return mEventData->mPrimaryCursorPosition;
863 }
864
865 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
866 {
867   if(nullptr == mEventData)
868   {
869     // Nothing to do if there is no text.
870     return false;
871   }
872
873   if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
874   {
875     // Nothing for same cursor position.
876     return false;
877   }
878
879   uint32_t length                    = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
880   uint32_t oldCursorPos              = mEventData->mPrimaryCursorPosition;
881   mEventData->mPrimaryCursorPosition = std::min(index, length);
882   // If there is no focus, only the value is updated.
883   if(focused)
884   {
885     bool     wasInSelectingState = mEventData->mState == EventData::SELECTING;
886     uint32_t oldStart            = mEventData->mLeftSelectionPosition;
887     uint32_t oldEnd              = mEventData->mRightSelectionPosition;
888     ChangeState(EventData::EDITING);
889     mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
890     mEventData->mUpdateCursorPosition                                        = true;
891
892     if(mSelectableControlInterface != nullptr && wasInSelectingState)
893     {
894       mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
895     }
896
897     ScrollTextToMatchCursor();
898   }
899
900   if(nullptr != mEditableControlInterface)
901   {
902     mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
903   }
904
905   return true;
906 }
907
908 Uint32Pair Controller::Impl::GetTextSelectionRange() const
909 {
910   Uint32Pair range;
911
912   if(mEventData)
913   {
914     range.first  = mEventData->mLeftSelectionPosition;
915     range.second = mEventData->mRightSelectionPosition;
916   }
917
918   return range;
919 }
920
921 bool Controller::Impl::IsEditable() const
922 {
923   return mEventData && mEventData->mEditingEnabled;
924 }
925
926 void Controller::Impl::SetEditable(bool editable)
927 {
928   if(mEventData)
929   {
930     mEventData->mEditingEnabled = editable;
931
932     if(mEventData->mDecorator)
933     {
934       bool decoratorEditable = editable && mIsUserInteractionEnabled;
935       mEventData->mDecorator->SetEditable(decoratorEditable);
936     }
937   }
938 }
939
940 void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont)
941 {
942   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
943
944   if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes
945   {
946     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str());
947     mFontDefaults->mFontDescription.family = newDefaultFont;
948
949     ClearFontData();
950
951     RequestRelayout();
952   }
953 }
954
955 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
956 {
957   if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
958   {
959     // Nothing to select if handles are in the same place.
960     selectedText.clear();
961     return;
962   }
963
964   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
965
966   //Get start and end position of selection
967   const CharacterIndex startOfSelectedText  = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
968   const Length         lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
969
970   Vector<Character>& utf32Characters    = mModel->mLogicalModel->mText;
971   const Length       numberOfCharacters = utf32Characters.Count();
972
973   // Validate the start and end selection points
974   if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
975   {
976     //Get text as a UTF8 string
977     Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
978
979     if(deleteAfterRetrieval) // Only delete text if copied successfully
980     {
981       // Keep a copy of the current input style.
982       InputStyle currentInputStyle;
983       currentInputStyle.Copy(mEventData->mInputStyle);
984
985       // Set as input style the style of the first deleted character.
986       mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
987
988       // Compare if the input style has changed.
989       const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
990
991       if(hasInputStyleChanged)
992       {
993         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
994         // Queue the input style changed signal.
995         mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
996       }
997
998       mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
999
1000       // Mark the paragraphs to be updated.
1001       if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1002       {
1003         mTextUpdateInfo.mCharacterIndex             = 0;
1004         mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1005         mTextUpdateInfo.mNumberOfCharactersToAdd    = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1006         mTextUpdateInfo.mClearAll                   = true;
1007       }
1008       else
1009       {
1010         mTextUpdateInfo.mCharacterIndex             = startOfSelectedText;
1011         mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1012       }
1013
1014       // Delete text between handles
1015       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1016       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1017       utf32Characters.Erase(first, last);
1018
1019       // Will show the cursor at the first character of the selection.
1020       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1021     }
1022     else
1023     {
1024       // Will show the cursor at the last character of the selection.
1025       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1026     }
1027
1028     mEventData->mDecoratorUpdated = true;
1029   }
1030 }
1031
1032 void Controller::Impl::SetSelection(int start, int end)
1033 {
1034   uint32_t oldStart = mEventData->mLeftSelectionPosition;
1035   uint32_t oldEnd   = mEventData->mRightSelectionPosition;
1036
1037   mEventData->mLeftSelectionPosition  = start;
1038   mEventData->mRightSelectionPosition = end;
1039   mEventData->mUpdateCursorPosition   = true;
1040
1041   if(mSelectableControlInterface != nullptr)
1042   {
1043     mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
1044   }
1045 }
1046
1047 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1048 {
1049   return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1050 }
1051
1052 void Controller::Impl::ShowClipboard()
1053 {
1054   if(mClipboard)
1055   {
1056     mClipboard.ShowClipboard();
1057   }
1058 }
1059
1060 void Controller::Impl::HideClipboard()
1061 {
1062   if(mClipboard && mClipboardHideEnabled)
1063   {
1064     mClipboard.HideClipboard();
1065   }
1066 }
1067
1068 void Controller::Impl::SetClipboardHideEnable(bool enable)
1069 {
1070   mClipboardHideEnabled = enable;
1071 }
1072
1073 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1074 {
1075   //Send string to clipboard
1076   return (mClipboard && mClipboard.SetItem(source));
1077 }
1078
1079 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1080 {
1081   std::string selectedText;
1082   RetrieveSelection(selectedText, deleteAfterSending);
1083   CopyStringToClipboard(selectedText);
1084   ChangeState(EventData::EDITING);
1085 }
1086
1087 void Controller::Impl::RequestGetTextFromClipboard()
1088 {
1089   if(mClipboard)
1090   {
1091     mClipboard.RequestItem();
1092   }
1093 }
1094
1095 void Controller::Impl::RepositionSelectionHandles()
1096 {
1097   SelectionHandleController::Reposition(*this);
1098 }
1099 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1100 {
1101   SelectionHandleController::Reposition(*this, visualX, visualY, action);
1102 }
1103
1104 void Controller::Impl::SetPopupButtons()
1105 {
1106   /**
1107    *  Sets the Popup buttons to be shown depending on State.
1108    *
1109    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1110    *
1111    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1112    */
1113
1114   bool                        isEditable    = IsEditable();
1115   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1116
1117   if(EventData::SELECTING == mEventData->mState)
1118   {
1119     buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1120     if(isEditable)
1121     {
1122       buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1123     }
1124
1125     if(!IsClipboardEmpty())
1126     {
1127       if(isEditable)
1128       {
1129         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1130       }
1131       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1132     }
1133
1134     if(!mEventData->mAllTextSelected)
1135     {
1136       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1137     }
1138   }
1139   else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1140   {
1141     if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1142     {
1143       buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1144     }
1145
1146     if(!IsClipboardEmpty())
1147     {
1148       if(isEditable)
1149       {
1150         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1151       }
1152       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1153     }
1154   }
1155   else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1156   {
1157     if(!IsClipboardEmpty())
1158     {
1159       if(isEditable)
1160       {
1161         buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1162       }
1163       buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1164     }
1165   }
1166
1167   mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1168 }
1169
1170 void Controller::Impl::ChangeState(EventData::State newState)
1171 {
1172   ChangeTextControllerState(*this, newState);
1173 }
1174
1175 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1176                                          CursorInfo&    cursorInfo)
1177 {
1178   if(!IsShowingRealText())
1179   {
1180     // Do not want to use the place-holder text to set the cursor position.
1181
1182     // Use the line's height of the font's family set to set the cursor's size.
1183     // If there is no font's family set, use the default font.
1184     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1185
1186     cursorInfo.lineOffset          = 0.f;
1187     cursorInfo.lineHeight          = GetDefaultFontLineHeight();
1188     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1189
1190     bool isRTL = false;
1191     if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1192     {
1193       isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1194     }
1195
1196     switch(mModel->mHorizontalAlignment)
1197     {
1198       case Text::HorizontalAlignment::BEGIN:
1199       {
1200         if(isRTL)
1201         {
1202           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1203         }
1204         else
1205         {
1206           cursorInfo.primaryPosition.x = 0.f;
1207         }
1208         break;
1209       }
1210       case Text::HorizontalAlignment::CENTER:
1211       {
1212         cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1213         break;
1214       }
1215       case Text::HorizontalAlignment::END:
1216       {
1217         if(isRTL)
1218         {
1219           cursorInfo.primaryPosition.x = 0.f;
1220         }
1221         else
1222         {
1223           cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1224         }
1225         break;
1226       }
1227     }
1228
1229     // Nothing else to do.
1230     return;
1231   }
1232
1233   const bool                  isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1234   GetCursorPositionParameters parameters;
1235   parameters.visualModel  = mModel->mVisualModel;
1236   parameters.logicalModel = mModel->mLogicalModel;
1237   parameters.metrics      = mMetrics;
1238   parameters.logical      = logical;
1239   parameters.isMultiline  = isMultiLine;
1240
1241   float defaultFontLineHeight = GetDefaultFontLineHeight();
1242
1243   Text::GetCursorPosition(parameters,
1244                           defaultFontLineHeight,
1245                           cursorInfo);
1246
1247   // Adds Outline offset.
1248   const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1249   cursorInfo.primaryPosition.x += outlineWidth;
1250   cursorInfo.primaryPosition.y += outlineWidth;
1251   cursorInfo.secondaryPosition.x += outlineWidth;
1252   cursorInfo.secondaryPosition.y += outlineWidth;
1253
1254   if(isMultiLine)
1255   {
1256     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1257
1258     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1259     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1260
1261     if(0.f > cursorInfo.primaryPosition.x)
1262     {
1263       cursorInfo.primaryPosition.x = 0.f;
1264     }
1265
1266     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1267     if(cursorInfo.primaryPosition.x > edgeWidth)
1268     {
1269       cursorInfo.primaryPosition.x = edgeWidth;
1270     }
1271   }
1272 }
1273
1274 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1275 {
1276   if(nullptr == mEventData)
1277   {
1278     // Nothing to do if there is no text input.
1279     return 0u;
1280   }
1281
1282   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1283
1284   const GlyphIndex* const charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
1285   const Length* const     charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1286
1287   GlyphIndex glyphIndex         = *(charactersToGlyphBuffer + index);
1288   Length     numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1289
1290   if(numberOfCharacters > 1u)
1291   {
1292     const Script script = mModel->mLogicalModel->GetScript(index);
1293     if(HasLigatureMustBreak(script))
1294     {
1295       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1296       numberOfCharacters = 1u;
1297     }
1298   }
1299   else
1300   {
1301     while(0u == numberOfCharacters)
1302     {
1303       ++glyphIndex;
1304       numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1305     }
1306   }
1307
1308   if(index < mEventData->mPrimaryCursorPosition)
1309   {
1310     cursorIndex -= numberOfCharacters;
1311   }
1312   else
1313   {
1314     cursorIndex += numberOfCharacters;
1315   }
1316
1317   // Will update the cursor hook position.
1318   mEventData->mUpdateCursorHookPosition = true;
1319
1320   return cursorIndex;
1321 }
1322
1323 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1324 {
1325   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1326   if(nullptr == mEventData)
1327   {
1328     // Nothing to do if there is no text input.
1329     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1330     return;
1331   }
1332
1333   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1334
1335   mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1336
1337   // Sets the cursor position.
1338   mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1339                                       cursorPosition.x,
1340                                       cursorPosition.y,
1341                                       cursorInfo.primaryCursorHeight,
1342                                       cursorInfo.lineHeight);
1343   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1344
1345   if(mEventData->mUpdateGrabHandlePosition)
1346   {
1347     // Sets the grab handle position.
1348     mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1349                                         cursorPosition.x,
1350                                         cursorInfo.lineOffset + mModel->mScrollPosition.y,
1351                                         cursorInfo.lineHeight);
1352   }
1353
1354   if(cursorInfo.isSecondaryCursor)
1355   {
1356     mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1357                                         cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1358                                         cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1359                                         cursorInfo.secondaryCursorHeight,
1360                                         cursorInfo.lineHeight);
1361     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1362   }
1363
1364   // Set which cursors are active according the state.
1365   if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1366   {
1367     if(cursorInfo.isSecondaryCursor)
1368     {
1369       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1370     }
1371     else
1372     {
1373       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1374     }
1375   }
1376   else
1377   {
1378     mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1379   }
1380
1381   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1382 }
1383
1384 void Controller::Impl::UpdateSelectionHandle(HandleType        handleType,
1385                                              const CursorInfo& cursorInfo)
1386 {
1387   SelectionHandleController::Update(*this, handleType, cursorInfo);
1388 }
1389
1390 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1391 {
1392   // Clamp between -space & -alignment offset.
1393
1394   if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1395   {
1396     const float space         = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1397     mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1398     mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1399
1400     mEventData->mDecoratorUpdated = true;
1401   }
1402   else
1403   {
1404     mModel->mScrollPosition.x = 0.f;
1405   }
1406 }
1407
1408 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1409 {
1410   if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1411   {
1412     // Nothing to do if the text is single line.
1413     return;
1414   }
1415
1416   // Clamp between -space & 0.
1417   if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1418   {
1419     const float space         = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1420     mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1421     mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1422
1423     mEventData->mDecoratorUpdated = true;
1424   }
1425   else
1426   {
1427     mModel->mScrollPosition.y = 0.f;
1428   }
1429 }
1430
1431 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1432 {
1433   const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1434
1435   // position is in actor's coords.
1436   const float positionEndX = position.x + cursorWidth;
1437   const float positionEndY = position.y + lineHeight;
1438
1439   // Transform the position to decorator coords.
1440   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1441   const float decoratorPositionEndX   = positionEndX + mModel->mScrollPosition.x;
1442
1443   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1444   const float decoratorPositionEndY   = positionEndY + mModel->mScrollPosition.y;
1445
1446   if(decoratorPositionBeginX < 0.f)
1447   {
1448     mModel->mScrollPosition.x = -position.x;
1449   }
1450   else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1451   {
1452     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1453   }
1454
1455   if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1456   {
1457     if(decoratorPositionBeginY < 0.f)
1458     {
1459       mModel->mScrollPosition.y = -position.y;
1460     }
1461     else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1462     {
1463       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1464     }
1465     else if(mModel->mLogicalModel->mText.Count() == 0u)
1466     {
1467       Relayouter::CalculateVerticalOffset(*this, mModel->mVisualModel->mControlSize);
1468     }
1469   }
1470 }
1471
1472 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1473 {
1474   // Get the current cursor position in decorator coords.
1475   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1476
1477   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1478
1479   // Calculate the offset to match the cursor position before the character was deleted.
1480   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1481
1482   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1483   if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1484   {
1485     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1486     mModel->mScrollPosition.y            = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1487   }
1488
1489   ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1490   ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1491
1492   // Makes the new cursor position visible if needed.
1493   ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1494 }
1495
1496 void Controller::Impl::ScrollTextToMatchCursor()
1497 {
1498   CursorInfo cursorInfo;
1499   GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1500   ScrollTextToMatchCursor(cursorInfo);
1501 }
1502
1503 void Controller::Impl::RequestRelayout()
1504 {
1505   if(nullptr != mControlInterface)
1506   {
1507     mControlInterface->RequestTextRelayout();
1508   }
1509 }
1510
1511 void Controller::Impl::RelayoutAllCharacters()
1512 {
1513   // relayout all characters
1514   mTextUpdateInfo.mCharacterIndex             = 0;
1515   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1516   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1517   mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
1518
1519   mTextUpdateInfo.mFullRelayoutNeeded = true;
1520
1521   // Need to recalculate natural size
1522   mRecalculateNaturalSize = true;
1523
1524   //remove selection
1525   if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
1526   {
1527     ChangeState(EventData::EDITING);
1528   }
1529
1530   RequestRelayout();
1531 }
1532
1533 bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
1534 {
1535   return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
1536 }
1537
1538 void Controller::Impl::ProcessInputStyleChangedSignals()
1539 {
1540   if(mEventData)
1541   {
1542     if(mEditableControlInterface)
1543     {
1544       // Emit the input style changed signal for each mask
1545       std::for_each(mEventData->mInputStyleChangedQueue.begin(),
1546                     mEventData->mInputStyleChangedQueue.end(),
1547                     [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
1548     }
1549
1550     mEventData->mInputStyleChangedQueue.Clear();
1551   }
1552 }
1553
1554 void Controller::Impl::ScrollBy(Vector2 scroll)
1555 {
1556   if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
1557   {
1558     const Vector2& layoutSize    = mModel->mVisualModel->GetLayoutSize();
1559     const Vector2  currentScroll = mModel->mScrollPosition;
1560
1561     scroll.x = -scroll.x;
1562     scroll.y = -scroll.y;
1563
1564     if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
1565     {
1566       mModel->mScrollPosition.x += scroll.x;
1567       ClampHorizontalScroll(layoutSize);
1568     }
1569
1570     if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
1571     {
1572       mModel->mScrollPosition.y += scroll.y;
1573       ClampVerticalScroll(layoutSize);
1574     }
1575
1576     if(mModel->mScrollPosition != currentScroll)
1577     {
1578       mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
1579       RequestRelayout();
1580     }
1581   }
1582 }
1583
1584 bool Controller::Impl::IsScrollable(const Vector2& displacement)
1585 {
1586   bool isScrollable = false;
1587   if(mEventData)
1588   {
1589     const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1590     const bool isVerticalScrollEnabled   = mEventData->mDecorator->IsVerticalScrollEnabled();
1591     if(isHorizontalScrollEnabled ||isVerticalScrollEnabled)
1592     {
1593       const Vector2& targetSize = mModel->mVisualModel->mControlSize;
1594       const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1595       const Vector2& scrollPosition = mModel->mScrollPosition;
1596
1597       if(isHorizontalScrollEnabled)
1598       {
1599         const float displacementX = displacement.x;
1600         const float positionX = scrollPosition.x + displacementX;
1601         if(layoutSize.width > targetSize.width && -positionX > 0.f && -positionX < layoutSize.width - targetSize.width)
1602         {
1603           isScrollable = true;
1604         }
1605       }
1606
1607       if(isVerticalScrollEnabled)
1608       {
1609         const float displacementY = displacement.y;
1610         const float positionY = scrollPosition.y + displacementY;
1611         if(layoutSize.height > targetSize.height && -positionY > 0 && -positionY < layoutSize.height - targetSize.height)
1612         {
1613           isScrollable = true;
1614         }
1615       }
1616     }
1617   }
1618   return isScrollable;
1619 }
1620
1621 float Controller::Impl::GetHorizontalScrollPosition()
1622 {
1623   // Scroll values are negative internally so we convert them to positive numbers
1624   return mEventData ? -mModel->mScrollPosition.x : 0.0f;
1625 }
1626
1627 float Controller::Impl::GetVerticalScrollPosition()
1628 {
1629   // Scroll values are negative internally so we convert them to positive numbers
1630   return mEventData ? -mModel->mScrollPosition.y : 0.0f;
1631 }
1632
1633 Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
1634 {
1635   //TODO
1636   return Vector3(10.f, 10.f, 10.f);
1637 }
1638
1639 Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
1640 {
1641   //TODO
1642   return Vector2(10.f, 10.f);
1643 }
1644
1645 Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
1646 {
1647   auto actor = Toolkit::TextAnchor::New();
1648   actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1649   actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1650   const Vector3 anchorPosition = GetAnchorPosition(anchor);
1651   actor.SetProperty(Actor::Property::POSITION, anchorPosition);
1652   const Vector2 anchorSize = GetAnchorSize(anchor);
1653   actor.SetProperty(Actor::Property::SIZE, anchorSize);
1654   std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
1655   actor.SetProperty(Actor::Property::NAME, anchorText);
1656   actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
1657   actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
1658   actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
1659   return actor;
1660 }
1661
1662 void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
1663 {
1664   /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
1665            we need to create and destroy potentially many actors. Some optimization can be considered here.
1666            Maybe a "dirty" flag in mLogicalModel? */
1667   anchorActors.clear();
1668   for(auto& anchor : mModel->mLogicalModel->mAnchors)
1669   {
1670     auto actor = CreateAnchorActor(anchor);
1671     anchorActors.push_back(actor);
1672   }
1673 }
1674
1675 int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
1676 {
1677   Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
1678
1679   while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
1680   {
1681     it++;
1682   }
1683
1684   return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
1685 }
1686
1687 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1688 {
1689   //Underlined character runs for markup-processor
1690   const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1691   const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
1692   const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
1693
1694   if(shouldClearPreUnderlineRuns)
1695   {
1696     mModel->mVisualModel->mUnderlineRuns.Clear();
1697   }
1698
1699   for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1700   {
1701     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1702     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1703
1704     if(numberOfCharacters == 0)
1705     {
1706       continue;
1707     }
1708
1709     // Create one run for all glyphs of all run's characters that has same properties
1710     // This enhance performance and reduce the needed memory to store glyphs-runs
1711     UnderlinedGlyphRun underlineGlyphRun;
1712     underlineGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1713     underlineGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1714     //Copy properties (attributes)
1715     underlineGlyphRun.properties = it->properties;
1716
1717     for(Length index = 1u; index < numberOfCharacters; index++)
1718     {
1719       underlineGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1720     }
1721
1722     mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1723   }
1724 }
1725
1726 void Controller::Impl::CopyStrikethroughFromLogicalToVisualModels()
1727 {
1728   //Strikethrough character runs from markup-processor
1729   const Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns = mModel->mLogicalModel->mStrikethroughCharacterRuns;
1730   const Vector<GlyphIndex>&                charactersToGlyph          = mModel->mVisualModel->mCharactersToGlyph;
1731   const Vector<Length>&                    glyphsPerCharacter         = mModel->mVisualModel->mGlyphsPerCharacter;
1732
1733   mModel->mVisualModel->mStrikethroughRuns.Clear();
1734
1735   for(Vector<StrikethroughCharacterRun>::ConstIterator it = strikethroughCharacterRuns.Begin(), endIt = strikethroughCharacterRuns.End(); it != endIt; ++it)
1736   {
1737     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1738     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1739
1740     if(numberOfCharacters == 0)
1741     {
1742       continue;
1743     }
1744
1745     StrikethroughGlyphRun strikethroughGlyphRun;
1746     strikethroughGlyphRun.properties              = it->properties;
1747     strikethroughGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1748     strikethroughGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1749
1750     for(Length index = 1u; index < numberOfCharacters; index++)
1751     {
1752       strikethroughGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1753     }
1754
1755     mModel->mVisualModel->mStrikethroughRuns.PushBack(strikethroughGlyphRun);
1756   }
1757 }
1758
1759 void Controller::Impl::CopyCharacterSpacingFromLogicalToVisualModels()
1760 {
1761   //CharacterSpacing character runs from markup-processor
1762   const Vector<CharacterSpacingCharacterRun>& characterSpacingCharacterRuns = mModel->mLogicalModel->mCharacterSpacingCharacterRuns;
1763   const Vector<GlyphIndex>&                   charactersToGlyph             = mModel->mVisualModel->mCharactersToGlyph;
1764   const Vector<Length>&                       glyphsPerCharacter            = mModel->mVisualModel->mGlyphsPerCharacter;
1765
1766   mModel->mVisualModel->mCharacterSpacingRuns.Clear();
1767
1768   for(Vector<CharacterSpacingCharacterRun>::ConstIterator it = characterSpacingCharacterRuns.Begin(), endIt = characterSpacingCharacterRuns.End(); it != endIt; ++it)
1769   {
1770     const CharacterIndex& characterIndex     = it->characterRun.characterIndex;
1771     const Length&         numberOfCharacters = it->characterRun.numberOfCharacters;
1772
1773     if(numberOfCharacters == 0)
1774     {
1775       continue;
1776     }
1777
1778     CharacterSpacingGlyphRun characterSpacingGlyphRun;
1779     characterSpacingGlyphRun.value                   = it->value;
1780     characterSpacingGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
1781     characterSpacingGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
1782
1783     for(Length index = 1u; index < numberOfCharacters; index++)
1784     {
1785       characterSpacingGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
1786     }
1787
1788     mModel->mVisualModel->mCharacterSpacingRuns.PushBack(characterSpacingGlyphRun);
1789   }
1790 }
1791
1792 void Controller::Impl::SetAutoScrollEnabled(bool enable)
1793 {
1794   if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
1795   {
1796     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1797                                                      LAYOUT |
1798                                                      ALIGN |
1799                                                      UPDATE_LAYOUT_SIZE |
1800                                                      REORDER);
1801
1802     if(enable)
1803     {
1804       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
1805       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
1806     }
1807     else
1808     {
1809       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
1810     }
1811
1812     mIsAutoScrollEnabled = enable;
1813     RequestRelayout();
1814   }
1815   else
1816   {
1817     DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
1818     mIsAutoScrollEnabled = false;
1819   }
1820 }
1821
1822 void Controller::Impl::SetEnableCursorBlink(bool enable)
1823 {
1824   DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
1825
1826   if(mEventData)
1827   {
1828     mEventData->mCursorBlinkEnabled = enable;
1829
1830     if(!enable && mEventData->mDecorator)
1831     {
1832       mEventData->mDecorator->StopCursorBlink();
1833     }
1834   }
1835 }
1836
1837 void Controller::Impl::SetMultiLineEnabled(bool enable)
1838 {
1839   const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
1840
1841   if(layout != mLayoutEngine.GetLayout())
1842   {
1843     // Set the layout type.
1844     mLayoutEngine.SetLayout(layout);
1845
1846     // Set the flags to redo the layout operations
1847     const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
1848                                                                         UPDATE_LAYOUT_SIZE |
1849                                                                         ALIGN |
1850                                                                         REORDER);
1851
1852     mTextUpdateInfo.mFullRelayoutNeeded = true;
1853     mOperationsPending                  = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
1854
1855     // Need to recalculate natural size
1856     mRecalculateNaturalSize = true;
1857
1858     RequestRelayout();
1859   }
1860 }
1861
1862 void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
1863 {
1864   if(alignment != mModel->mHorizontalAlignment)
1865   {
1866     // Set the alignment.
1867     mModel->mHorizontalAlignment = alignment;
1868     UpdateCursorPositionForAlignment(*this, true);
1869     RequestRelayout();
1870   }
1871 }
1872
1873 void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
1874 {
1875   if(alignment != mModel->mVerticalAlignment)
1876   {
1877     // Set the alignment.
1878     mModel->mVerticalAlignment = alignment;
1879     UpdateCursorPositionForAlignment(*this, false);
1880     RequestRelayout();
1881   }
1882 }
1883
1884 void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
1885 {
1886   if(lineWrapMode != mModel->mLineWrapMode)
1887   {
1888     // Update Text layout for applying wrap mode
1889     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1890                                                      ALIGN |
1891                                                      LAYOUT |
1892                                                      UPDATE_LAYOUT_SIZE |
1893                                                      REORDER);
1894
1895     if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
1896        (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
1897     {
1898       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
1899     }
1900
1901     // Set the text wrap mode.
1902     mModel->mLineWrapMode = lineWrapMode;
1903
1904     mTextUpdateInfo.mCharacterIndex             = 0u;
1905     mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1906     mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1907
1908     // Request relayout
1909     RequestRelayout();
1910   }
1911 }
1912
1913 void Controller::Impl::SetDefaultColor(const Vector4& color)
1914 {
1915   mTextColor = color;
1916
1917   if(!IsShowingPlaceholderText())
1918   {
1919     mModel->mVisualModel->SetTextColor(color);
1920     mModel->mLogicalModel->mColorRuns.Clear();
1921     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
1922     RequestRelayout();
1923   }
1924 }
1925
1926 void Controller::Impl::SetUserInteractionEnabled(bool enabled)
1927 {
1928   mIsUserInteractionEnabled = enabled;
1929
1930   if(mEventData && mEventData->mDecorator)
1931   {
1932     bool editable = mEventData->mEditingEnabled && enabled;
1933     mEventData->mDecorator->SetEditable(editable);
1934   }
1935 }
1936
1937 void Controller::Impl::ClearFontData()
1938 {
1939   if(mFontDefaults)
1940   {
1941     mFontDefaults->mFontId = 0u; // Remove old font ID
1942   }
1943
1944   // Set flags to update the model.
1945   mTextUpdateInfo.mCharacterIndex             = 0u;
1946   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1947   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1948
1949   mTextUpdateInfo.mClearAll           = true;
1950   mTextUpdateInfo.mFullRelayoutNeeded = true;
1951   mRecalculateNaturalSize             = true;
1952
1953   mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1954                                                    VALIDATE_FONTS |
1955                                                    SHAPE_TEXT |
1956                                                    BIDI_INFO |
1957                                                    GET_GLYPH_METRICS |
1958                                                    LAYOUT |
1959                                                    UPDATE_LAYOUT_SIZE |
1960                                                    REORDER |
1961                                                    ALIGN);
1962 }
1963
1964 void Controller::Impl::ClearStyleData()
1965 {
1966   mModel->mLogicalModel->mColorRuns.Clear();
1967   mModel->mLogicalModel->ClearFontDescriptionRuns();
1968   mModel->mLogicalModel->ClearStrikethroughRuns();
1969 }
1970
1971 void Controller::Impl::ResetScrollPosition()
1972 {
1973   if(mEventData)
1974   {
1975     // Reset the scroll position.
1976     mModel->mScrollPosition                = Vector2::ZERO;
1977     mEventData->mScrollAfterUpdatePosition = true;
1978   }
1979 }
1980
1981 } // namespace Dali::Toolkit::Text