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