Add ENABLE_FONT_SIZE_SCALE property to text components
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / 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/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 * GetFontSizeScale());
676   }
677   else
678   {
679     defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale());
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   float defaultFontLineHeight = GetDefaultFontLineHeight();
1199
1200   Text::GetCursorPosition(parameters,
1201                           defaultFontLineHeight,
1202                           cursorInfo);
1203
1204   // Adds Outline offset.
1205   const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1206   cursorInfo.primaryPosition.x += outlineWidth;
1207   cursorInfo.primaryPosition.y += outlineWidth;
1208   cursorInfo.secondaryPosition.x += outlineWidth;
1209   cursorInfo.secondaryPosition.y += outlineWidth;
1210
1211   if(isMultiLine)
1212   {
1213     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1214
1215     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1216     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1217
1218     if(0.f > cursorInfo.primaryPosition.x)
1219     {
1220       cursorInfo.primaryPosition.x = 0.f;
1221     }
1222
1223     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1224     if(cursorInfo.primaryPosition.x > edgeWidth)
1225     {
1226       cursorInfo.primaryPosition.x = edgeWidth;
1227     }
1228   }
1229 }
1230
1231 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1232 {
1233   if(nullptr == mEventData)
1234   {
1235     // Nothing to do if there is no text input.
1236     return 0u;
1237   }
1238
1239   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1240
1241   const GlyphIndex* const charactersToGlyphBuffer  = mModel->mVisualModel->mCharactersToGlyph.Begin();
1242   const Length* const     charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1243
1244   GlyphIndex glyphIndex         = *(charactersToGlyphBuffer + index);
1245   Length     numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1246
1247   if(numberOfCharacters > 1u)
1248   {
1249     const Script script = mModel->mLogicalModel->GetScript(index);
1250     if(HasLigatureMustBreak(script))
1251     {
1252       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1253       numberOfCharacters = 1u;
1254     }
1255   }
1256   else
1257   {
1258     while(0u == numberOfCharacters)
1259     {
1260       ++glyphIndex;
1261       numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1262     }
1263   }
1264
1265   if(index < mEventData->mPrimaryCursorPosition)
1266   {
1267     cursorIndex -= numberOfCharacters;
1268   }
1269   else
1270   {
1271     cursorIndex += numberOfCharacters;
1272   }
1273
1274   // Will update the cursor hook position.
1275   mEventData->mUpdateCursorHookPosition = true;
1276
1277   return cursorIndex;
1278 }
1279
1280 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1281 {
1282   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1283   if(nullptr == mEventData)
1284   {
1285     // Nothing to do if there is no text input.
1286     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1287     return;
1288   }
1289
1290   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1291
1292   mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1293
1294   // Sets the cursor position.
1295   mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1296                                       cursorPosition.x,
1297                                       cursorPosition.y,
1298                                       cursorInfo.primaryCursorHeight,
1299                                       cursorInfo.lineHeight);
1300   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1301
1302   if(mEventData->mUpdateGrabHandlePosition)
1303   {
1304     // Sets the grab handle position.
1305     mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1306                                         cursorPosition.x,
1307                                         cursorInfo.lineOffset + mModel->mScrollPosition.y,
1308                                         cursorInfo.lineHeight);
1309   }
1310
1311   if(cursorInfo.isSecondaryCursor)
1312   {
1313     mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1314                                         cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1315                                         cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1316                                         cursorInfo.secondaryCursorHeight,
1317                                         cursorInfo.lineHeight);
1318     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1319   }
1320
1321   // Set which cursors are active according the state.
1322   if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1323   {
1324     if(cursorInfo.isSecondaryCursor)
1325     {
1326       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1327     }
1328     else
1329     {
1330       mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1331     }
1332   }
1333   else
1334   {
1335     mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1336   }
1337
1338   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1339 }
1340
1341 void Controller::Impl::UpdateSelectionHandle(HandleType        handleType,
1342                                              const CursorInfo& cursorInfo)
1343 {
1344   SelectionHandleController::Update(*this, handleType, cursorInfo);
1345 }
1346
1347 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1348 {
1349   // Clamp between -space & -alignment offset.
1350
1351   if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1352   {
1353     const float space         = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1354     mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1355     mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1356
1357     mEventData->mDecoratorUpdated = true;
1358   }
1359   else
1360   {
1361     mModel->mScrollPosition.x = 0.f;
1362   }
1363 }
1364
1365 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1366 {
1367   if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1368   {
1369     // Nothing to do if the text is single line.
1370     return;
1371   }
1372
1373   // Clamp between -space & 0.
1374   if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1375   {
1376     const float space         = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1377     mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1378     mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1379
1380     mEventData->mDecoratorUpdated = true;
1381   }
1382   else
1383   {
1384     mModel->mScrollPosition.y = 0.f;
1385   }
1386 }
1387
1388 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1389 {
1390   const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1391
1392   // position is in actor's coords.
1393   const float positionEndX = position.x + cursorWidth;
1394   const float positionEndY = position.y + lineHeight;
1395
1396   // Transform the position to decorator coords.
1397   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1398   const float decoratorPositionEndX   = positionEndX + mModel->mScrollPosition.x;
1399
1400   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1401   const float decoratorPositionEndY   = positionEndY + mModel->mScrollPosition.y;
1402
1403   if(decoratorPositionBeginX < 0.f)
1404   {
1405     mModel->mScrollPosition.x = -position.x;
1406   }
1407   else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1408   {
1409     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1410   }
1411
1412   if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1413   {
1414     if(decoratorPositionBeginY < 0.f)
1415     {
1416       mModel->mScrollPosition.y = -position.y;
1417     }
1418     else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1419     {
1420       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1421     }
1422   }
1423 }
1424
1425 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1426 {
1427   // Get the current cursor position in decorator coords.
1428   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1429
1430   const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1431
1432   // Calculate the offset to match the cursor position before the character was deleted.
1433   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1434
1435   //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1436   if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1437   {
1438     const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1439     mModel->mScrollPosition.y            = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1440   }
1441
1442   ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1443   ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1444
1445   // Makes the new cursor position visible if needed.
1446   ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1447 }
1448
1449 void Controller::Impl::ScrollTextToMatchCursor()
1450 {
1451   CursorInfo cursorInfo;
1452   GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1453   ScrollTextToMatchCursor(cursorInfo);
1454 }
1455
1456 void Controller::Impl::RequestRelayout()
1457 {
1458   if(nullptr != mControlInterface)
1459   {
1460     mControlInterface->RequestTextRelayout();
1461   }
1462 }
1463
1464 void Controller::Impl::RelayoutForNewLineSize()
1465 {
1466   // relayout all characters
1467   mTextUpdateInfo.mCharacterIndex             = 0;
1468   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1469   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1470   mOperationsPending                          = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
1471
1472   mTextUpdateInfo.mFullRelayoutNeeded = true;
1473
1474   // Need to recalculate natural size
1475   mRecalculateNaturalSize = true;
1476
1477   //remove selection
1478   if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
1479   {
1480     ChangeState(EventData::EDITING);
1481   }
1482
1483   RequestRelayout();
1484 }
1485
1486 bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
1487 {
1488   return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
1489 }
1490
1491 void Controller::Impl::ProcessInputStyleChangedSignals()
1492 {
1493   if(mEventData)
1494   {
1495     if(mEditableControlInterface)
1496     {
1497       // Emit the input style changed signal for each mask
1498       std::for_each(mEventData->mInputStyleChangedQueue.begin(),
1499                     mEventData->mInputStyleChangedQueue.end(),
1500                     [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
1501     }
1502
1503     mEventData->mInputStyleChangedQueue.Clear();
1504   }
1505 }
1506
1507 void Controller::Impl::ScrollBy(Vector2 scroll)
1508 {
1509   if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
1510   {
1511     const Vector2& layoutSize    = mModel->mVisualModel->GetLayoutSize();
1512     const Vector2  currentScroll = mModel->mScrollPosition;
1513
1514     scroll.x = -scroll.x;
1515     scroll.y = -scroll.y;
1516
1517     if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
1518     {
1519       mModel->mScrollPosition.x += scroll.x;
1520       ClampHorizontalScroll(layoutSize);
1521     }
1522
1523     if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
1524     {
1525       mModel->mScrollPosition.y += scroll.y;
1526       ClampVerticalScroll(layoutSize);
1527     }
1528
1529     if(mModel->mScrollPosition != currentScroll)
1530     {
1531       mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
1532       RequestRelayout();
1533     }
1534   }
1535 }
1536
1537 float Controller::Impl::GetHorizontalScrollPosition()
1538 {
1539   // Scroll values are negative internally so we convert them to positive numbers
1540   return mEventData ? -mModel->mScrollPosition.x : 0.0f;
1541 }
1542
1543 float Controller::Impl::GetVerticalScrollPosition()
1544 {
1545   // Scroll values are negative internally so we convert them to positive numbers
1546   return mEventData ? -mModel->mScrollPosition.y : 0.0f;
1547 }
1548
1549 Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
1550 {
1551   //TODO
1552   return Vector3(10.f, 10.f, 10.f);
1553 }
1554
1555 Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
1556 {
1557   //TODO
1558   return Vector2(10.f, 10.f);
1559 }
1560
1561 Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
1562 {
1563   auto actor = Toolkit::TextAnchor::New();
1564   actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1565   actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1566   const Vector3 anchorPosition = GetAnchorPosition(anchor);
1567   actor.SetProperty(Actor::Property::POSITION, anchorPosition);
1568   const Vector2 anchorSize = GetAnchorSize(anchor);
1569   actor.SetProperty(Actor::Property::SIZE, anchorSize);
1570   std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
1571   actor.SetProperty(Actor::Property::NAME, anchorText);
1572   actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
1573   actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
1574   actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
1575   return actor;
1576 }
1577
1578 void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
1579 {
1580   /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
1581            we need to create and destroy potentially many actors. Some optimization can be considered here.
1582            Maybe a "dirty" flag in mLogicalModel? */
1583   anchorActors.clear();
1584   for(auto& anchor : mModel->mLogicalModel->mAnchors)
1585   {
1586     auto actor = CreateAnchorActor(anchor);
1587     anchorActors.push_back(actor);
1588   }
1589 }
1590
1591 int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
1592 {
1593   Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
1594
1595   while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
1596   {
1597     it++;
1598   }
1599
1600   return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
1601 }
1602
1603 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1604 {
1605   //Underlined character runs for markup-processor
1606   const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1607   const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
1608   const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
1609
1610   if(shouldClearPreUnderlineRuns)
1611   {
1612     mModel->mVisualModel->mUnderlineRuns.Clear();
1613   }
1614
1615   for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1616   {
1617     CharacterIndex characterIndex     = it->characterRun.characterIndex;
1618     Length         numberOfCharacters = it->characterRun.numberOfCharacters;
1619     for(Length index = 0u; index < numberOfCharacters; index++)
1620     {
1621       GlyphRun underlineGlyphRun;
1622       underlineGlyphRun.glyphIndex     = charactersToGlyph[characterIndex + index];
1623       underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
1624       mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1625     }
1626   }
1627 }
1628
1629 void Controller::Impl::SetAutoScrollEnabled(bool enable)
1630 {
1631   if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
1632   {
1633     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1634                                                      LAYOUT |
1635                                                      ALIGN |
1636                                                      UPDATE_LAYOUT_SIZE |
1637                                                      REORDER);
1638
1639     if(enable)
1640     {
1641       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
1642       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
1643     }
1644     else
1645     {
1646       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
1647     }
1648
1649     mIsAutoScrollEnabled = enable;
1650     RequestRelayout();
1651   }
1652   else
1653   {
1654     DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
1655     mIsAutoScrollEnabled = false;
1656   }
1657 }
1658
1659 void Controller::Impl::SetEnableCursorBlink(bool enable)
1660 {
1661   DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
1662
1663   if(mEventData)
1664   {
1665     mEventData->mCursorBlinkEnabled = enable;
1666
1667     if(!enable && mEventData->mDecorator)
1668     {
1669       mEventData->mDecorator->StopCursorBlink();
1670     }
1671   }
1672 }
1673
1674 void Controller::Impl::SetMultiLineEnabled(bool enable)
1675 {
1676   const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
1677
1678   if(layout != mLayoutEngine.GetLayout())
1679   {
1680     // Set the layout type.
1681     mLayoutEngine.SetLayout(layout);
1682
1683     // Set the flags to redo the layout operations
1684     const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
1685                                                                         UPDATE_LAYOUT_SIZE |
1686                                                                         ALIGN |
1687                                                                         REORDER);
1688
1689     mTextUpdateInfo.mFullRelayoutNeeded = true;
1690     mOperationsPending                  = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
1691
1692     // Need to recalculate natural size
1693     mRecalculateNaturalSize = true;
1694
1695     RequestRelayout();
1696   }
1697 }
1698
1699 void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
1700 {
1701   if(alignment != mModel->mHorizontalAlignment)
1702   {
1703     // Set the alignment.
1704     mModel->mHorizontalAlignment = alignment;
1705
1706     // Set the flag to redo the alignment operation.
1707     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
1708
1709     if(mEventData)
1710     {
1711       mEventData->mUpdateAlignment = true;
1712
1713       // Update the cursor if it's in editing mode
1714       if(EventData::IsEditingState(mEventData->mState))
1715       {
1716         ChangeState(EventData::EDITING);
1717         mEventData->mUpdateCursorPosition = true;
1718       }
1719     }
1720
1721     RequestRelayout();
1722   }
1723 }
1724
1725 void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
1726 {
1727   if(alignment != mModel->mVerticalAlignment)
1728   {
1729     // Set the alignment.
1730     mModel->mVerticalAlignment = alignment;
1731     mOperationsPending         = static_cast<OperationsMask>(mOperationsPending | ALIGN);
1732     RequestRelayout();
1733   }
1734 }
1735
1736 void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
1737 {
1738   if(lineWrapMode != mModel->mLineWrapMode)
1739   {
1740     // Update Text layout for applying wrap mode
1741     mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1742                                                      ALIGN |
1743                                                      LAYOUT |
1744                                                      UPDATE_LAYOUT_SIZE |
1745                                                      REORDER);
1746
1747     if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
1748        (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
1749     {
1750       mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
1751     }
1752
1753     // Set the text wrap mode.
1754     mModel->mLineWrapMode = lineWrapMode;
1755
1756     mTextUpdateInfo.mCharacterIndex             = 0u;
1757     mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1758     mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1759
1760     // Request relayout
1761     RequestRelayout();
1762   }
1763 }
1764
1765 void Controller::Impl::SetDefaultColor(const Vector4& color)
1766 {
1767   mTextColor = color;
1768
1769   if(!IsShowingPlaceholderText())
1770   {
1771     mModel->mVisualModel->SetTextColor(color);
1772     mModel->mLogicalModel->mColorRuns.Clear();
1773     mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
1774     RequestRelayout();
1775   }
1776 }
1777
1778 void Controller::Impl::ClearFontData()
1779 {
1780   if(mFontDefaults)
1781   {
1782     mFontDefaults->mFontId = 0u; // Remove old font ID
1783   }
1784
1785   // Set flags to update the model.
1786   mTextUpdateInfo.mCharacterIndex             = 0u;
1787   mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1788   mTextUpdateInfo.mNumberOfCharactersToAdd    = mModel->mLogicalModel->mText.Count();
1789
1790   mTextUpdateInfo.mClearAll           = true;
1791   mTextUpdateInfo.mFullRelayoutNeeded = true;
1792   mRecalculateNaturalSize             = true;
1793
1794   mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
1795                                                    VALIDATE_FONTS |
1796                                                    SHAPE_TEXT |
1797                                                    BIDI_INFO |
1798                                                    GET_GLYPH_METRICS |
1799                                                    LAYOUT |
1800                                                    UPDATE_LAYOUT_SIZE |
1801                                                    REORDER |
1802                                                    ALIGN);
1803 }
1804
1805 void Controller::Impl::ClearStyleData()
1806 {
1807   mModel->mLogicalModel->mColorRuns.Clear();
1808   mModel->mLogicalModel->ClearFontDescriptionRuns();
1809 }
1810
1811 void Controller::Impl::ResetScrollPosition()
1812 {
1813   if(mEventData)
1814   {
1815     // Reset the scroll position.
1816     mModel->mScrollPosition                = Vector2::ZERO;
1817     mEventData->mScrollAfterUpdatePosition = true;
1818   }
1819 }
1820
1821 } // namespace Dali::Toolkit::Text