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