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