[dali_2.1.0] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-relayouter.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-relayouter.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <limits>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
27 #include <dali-toolkit/internal/text/text-controller-event-handler.h>
28 #include <dali-toolkit/internal/text/text-controller-impl.h>
29
30 namespace
31 {
32 #if defined(DEBUG_ENABLED)
33 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
34 #endif
35
36 constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
37
38 float ConvertToEven(float value)
39 {
40   int intValue(static_cast<int>(value));
41   return static_cast<float>(intValue + (intValue & 1));
42 }
43
44 } // namespace
45
46 namespace Dali
47 {
48 namespace Toolkit
49 {
50 namespace Text
51 {
52 Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask, bool restoreLinesAndGlyphPositions)
53 {
54   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->CalculateLayoutSizeOnRequiredControllerSize\n");
55   Size calculatedLayoutSize;
56
57   Controller::Impl& impl        = *controller.mImpl;
58   ModelPtr&         model       = impl.mModel;
59   VisualModelPtr&   visualModel = model->mVisualModel;
60
61   // Store the pending operations mask so that it can be restored later on with no modifications made on it
62   // while getting the natural size were reflected on the original mask.
63   OperationsMask operationsPendingBackUp = static_cast<OperationsMask>(impl.mOperationsPending);
64
65   // This is a hotfix for side effect on Scrolling, LineWrap and Invalid position of cursor in TextEditor after calling CalculateLayoutSizeOnRequiredControllerSize.
66   // The number of lines and glyph-positions inside visualModel have been changed by calling DoRelayout with requestedControllerSize.
67   // Store the mLines and mGlyphPositions from visualModel so that they can be restored later on with no modifications made on them.
68   //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel, and then blah, blah, etc.
69   Vector<LineRun> linesBackup          = visualModel->mLines;
70   Vector<Vector2> glyphPositionsBackup = visualModel->mGlyphPositions;
71
72   // Operations that can be done only once until the text changes.
73   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
74                                                                         GET_SCRIPTS |
75                                                                         VALIDATE_FONTS |
76                                                                         GET_LINE_BREAKS |
77                                                                         BIDI_INFO |
78                                                                         SHAPE_TEXT |
79                                                                         GET_GLYPH_METRICS);
80
81   // Set the update info to relayout the whole text.
82   TextUpdateInfo& textUpdateInfo              = impl.mTextUpdateInfo;
83   textUpdateInfo.mParagraphCharacterIndex     = 0u;
84   textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
85
86   // Make sure the model is up-to-date before layouting
87   impl.UpdateModel(onlyOnceOperations);
88
89   // Get a reference to the pending operations member
90   OperationsMask& operationsPending = impl.mOperationsPending;
91
92   // Layout the text for the new width.
93   operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask);
94
95   // Store the actual control's size to restore later.
96   const Size actualControlSize = visualModel->mControlSize;
97
98   DoRelayout(impl,
99              requestedControllerSize,
100              static_cast<OperationsMask>(onlyOnceOperations |
101                                          requestedOperationsMask),
102              calculatedLayoutSize);
103
104   // Clear the update info. This info will be set the next time the text is updated.
105   textUpdateInfo.Clear();
106   textUpdateInfo.mClearAll = true;
107
108   // Restore the actual control's size.
109   visualModel->mControlSize = actualControlSize;
110   // Restore the previously backed-up pending operations' mask without the only once operations.
111   impl.mOperationsPending = static_cast<OperationsMask>(operationsPendingBackUp & ~onlyOnceOperations);
112
113   // Restore the previously backed-up mLines and mGlyphPositions from visualModel.
114   if(restoreLinesAndGlyphPositions)
115   {
116     visualModel->mLines          = linesBackup;
117     visualModel->mGlyphPositions = glyphPositionsBackup;
118   }
119
120   return calculatedLayoutSize;
121 }
122
123 Vector3 Controller::Relayouter::GetNaturalSize(Controller& controller)
124 {
125   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n");
126   Vector3 naturalSizeVec3;
127
128   // Make sure the model is up-to-date before layouting
129   EventHandler::ProcessModifyEvents(controller);
130
131   Controller::Impl& impl        = *controller.mImpl;
132   ModelPtr&         model       = impl.mModel;
133   VisualModelPtr&   visualModel = model->mVisualModel;
134
135   if(impl.mRecalculateNaturalSize)
136   {
137     Size naturalSize;
138
139     // Layout the text for the new width.
140     OperationsMask requestedOperationsMask  = static_cast<OperationsMask>(LAYOUT | REORDER);
141     Size           sizeMaxWidthAndMaxHeight = Size(MAX_FLOAT, MAX_FLOAT);
142
143     naturalSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeMaxWidthAndMaxHeight, requestedOperationsMask, true);
144
145     // Stores the natural size to avoid recalculate it again
146     // unless the text/style changes.
147     visualModel->SetNaturalSize(naturalSize);
148     naturalSizeVec3 = naturalSize;
149
150     impl.mRecalculateNaturalSize = false;
151
152     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSizeVec3.x, naturalSizeVec3.y, naturalSizeVec3.z);
153   }
154   else
155   {
156     naturalSizeVec3 = visualModel->GetNaturalSize();
157
158     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSizeVec3.x, naturalSizeVec3.y, naturalSizeVec3.z);
159   }
160
161   naturalSizeVec3.x = ConvertToEven(naturalSizeVec3.x);
162   naturalSizeVec3.y = ConvertToEven(naturalSizeVec3.y);
163
164   return naturalSizeVec3;
165 }
166
167 bool Controller::Relayouter::CheckForTextFit(Controller& controller, float pointSize, const Size& layoutSize)
168 {
169   Size              textSize;
170   Controller::Impl& impl            = *controller.mImpl;
171   TextUpdateInfo&   textUpdateInfo  = impl.mTextUpdateInfo;
172   impl.mFontDefaults->mFitPointSize = pointSize;
173   impl.mFontDefaults->sizeDefined   = true;
174   impl.ClearFontData();
175
176   // Operations that can be done only once until the text changes.
177   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
178                                                                         GET_SCRIPTS |
179                                                                         VALIDATE_FONTS |
180                                                                         GET_LINE_BREAKS |
181                                                                         BIDI_INFO |
182                                                                         SHAPE_TEXT |
183                                                                         GET_GLYPH_METRICS);
184
185   textUpdateInfo.mParagraphCharacterIndex     = 0u;
186   textUpdateInfo.mRequestedNumberOfCharacters = impl.mModel->mLogicalModel->mText.Count();
187
188   // Make sure the model is up-to-date before layouting
189   impl.UpdateModel(onlyOnceOperations);
190
191   DoRelayout(impl,
192              Size(layoutSize.width, MAX_FLOAT),
193              static_cast<OperationsMask>(onlyOnceOperations | LAYOUT),
194              textSize);
195
196   // Clear the update info. This info will be set the next time the text is updated.
197   textUpdateInfo.Clear();
198   textUpdateInfo.mClearAll = true;
199
200   if(textSize.width > layoutSize.width || textSize.height > layoutSize.height)
201   {
202     return false;
203   }
204   return true;
205 }
206
207 void Controller::Relayouter::FitPointSizeforLayout(Controller& controller, const Size& layoutSize)
208 {
209   Controller::Impl& impl = *controller.mImpl;
210
211   const OperationsMask operations = impl.mOperationsPending;
212   if(NO_OPERATION != (UPDATE_LAYOUT_SIZE & operations) || impl.mTextFitContentSize != layoutSize)
213   {
214     ModelPtr& model = impl.mModel;
215
216     bool  actualellipsis = model->mElideEnabled;
217     float minPointSize   = impl.mTextFitMinSize;
218     float maxPointSize   = impl.mTextFitMaxSize;
219     float pointInterval  = impl.mTextFitStepSize;
220     float currentFitPointSize = impl.mFontDefaults->mFitPointSize;
221
222     model->mElideEnabled = false;
223     Vector<float> pointSizeArray;
224
225     // check zero value
226     if(pointInterval < 1.f)
227     {
228       impl.mTextFitStepSize = pointInterval = 1.0f;
229     }
230
231     pointSizeArray.Reserve(static_cast<unsigned int>(ceil((maxPointSize - minPointSize) / pointInterval)));
232
233     for(float i = minPointSize; i < maxPointSize; i += pointInterval)
234     {
235       pointSizeArray.PushBack(i);
236     }
237
238     pointSizeArray.PushBack(maxPointSize);
239
240     int bestSizeIndex = 0;
241     int min           = bestSizeIndex + 1;
242     int max           = pointSizeArray.Size() - 1;
243     while(min <= max)
244     {
245       int destI = (min + max) / 2;
246
247       if(CheckForTextFit(controller, pointSizeArray[destI], layoutSize))
248       {
249         bestSizeIndex = min;
250         min           = destI + 1;
251       }
252       else
253       {
254         max           = destI - 1;
255         bestSizeIndex = max;
256       }
257     }
258
259     model->mElideEnabled              = actualellipsis;
260     if(currentFitPointSize != pointSizeArray[bestSizeIndex])
261     {
262       impl.mTextFitChanged = true;
263     }
264     impl.mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex];
265     impl.mFontDefaults->sizeDefined   = true;
266     impl.ClearFontData();
267   }
268 }
269
270 float Controller::Relayouter::GetHeightForWidth(Controller& controller, float width)
271 {
272   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", &controller, width);
273
274   // Make sure the model is up-to-date before layouting
275   EventHandler::ProcessModifyEvents(controller);
276
277   Controller::Impl& impl           = *controller.mImpl;
278   ModelPtr&         model          = impl.mModel;
279   VisualModelPtr&   visualModel    = model->mVisualModel;
280   TextUpdateInfo&   textUpdateInfo = impl.mTextUpdateInfo;
281
282   Size layoutSize;
283
284   if(fabsf(width - visualModel->mControlSize.width) > Math::MACHINE_EPSILON_1000 ||
285      textUpdateInfo.mFullRelayoutNeeded ||
286      textUpdateInfo.mClearAll)
287   {
288     // Layout the text for the new width.
289     OperationsMask requestedOperationsMask        = static_cast<OperationsMask>(LAYOUT);
290     Size           sizeRequestedWidthAndMaxHeight = Size(width, MAX_FLOAT);
291
292     // Skip restore, because if GetHeightForWidth called before rendering and layouting then visualModel->mControlSize will be zero which will make LineCount zero.
293     // The implementation of Get LineCount property depends on calling GetHeightForWidth then read mLines.Count() from visualModel direct.
294     // If the LineCount property is requested before rendering and layouting then the value will be zero, which is incorrect.
295     // So we will not restore the previously backed-up mLines and mGlyphPositions from visualModel in such case.
296     // Another case to skip restore is when the requested width equals the Control's width which means the caller need to update the old values.
297     // For example, when the text is changed.
298     bool restoreLinesAndGlyphPositions = (visualModel->mControlSize.width > 0 && visualModel->mControlSize.height > 0) && (visualModel->mControlSize.width != width);
299
300     layoutSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeRequestedWidthAndMaxHeight, requestedOperationsMask, restoreLinesAndGlyphPositions);
301
302     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height);
303   }
304   else
305   {
306     layoutSize = visualModel->GetLayoutSize();
307     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height);
308   }
309
310   return layoutSize.height;
311 }
312
313 Controller::UpdateTextType Controller::Relayouter::Relayout(Controller& controller, const Size& size, Dali::LayoutDirection::Type layoutDirection)
314 {
315   Controller::Impl& impl           = *controller.mImpl;
316   ModelPtr&         model          = impl.mModel;
317   VisualModelPtr&   visualModel    = model->mVisualModel;
318   TextUpdateInfo&   textUpdateInfo = impl.mTextUpdateInfo;
319
320   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", &controller, size.width, size.height, impl.mIsAutoScrollEnabled ? "true" : "false");
321
322   UpdateTextType updateTextType = NONE_UPDATED;
323
324   if((size.width < Math::MACHINE_EPSILON_1000) || (size.height < Math::MACHINE_EPSILON_1000))
325   {
326     if(0u != visualModel->mGlyphPositions.Count())
327     {
328       visualModel->mGlyphPositions.Clear();
329       updateTextType = MODEL_UPDATED;
330     }
331
332     // Clear the update info. This info will be set the next time the text is updated.
333     textUpdateInfo.Clear();
334
335     // Not worth to relayout if width or height is equal to zero.
336     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n");
337
338     return updateTextType;
339   }
340
341   // Whether a new size has been set.
342   const bool newSize = (size != visualModel->mControlSize);
343
344   // Get a reference to the pending operations member
345   OperationsMask& operationsPending = impl.mOperationsPending;
346
347   if(newSize)
348   {
349     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", visualModel->mControlSize.width, visualModel->mControlSize.height);
350
351     if((0 == textUpdateInfo.mNumberOfCharactersToAdd) &&
352        (0 == textUpdateInfo.mPreviousNumberOfCharacters) &&
353        ((visualModel->mControlSize.width < Math::MACHINE_EPSILON_1000) || (visualModel->mControlSize.height < Math::MACHINE_EPSILON_1000)))
354     {
355       textUpdateInfo.mNumberOfCharactersToAdd = model->mLogicalModel->mText.Count();
356     }
357
358     // Layout operations that need to be done if the size changes.
359     operationsPending = static_cast<OperationsMask>(operationsPending |
360                                                     LAYOUT |
361                                                     ALIGN |
362                                                     UPDATE_LAYOUT_SIZE |
363                                                     REORDER);
364     // Set the update info to relayout the whole text.
365     textUpdateInfo.mFullRelayoutNeeded = true;
366     textUpdateInfo.mCharacterIndex     = 0u;
367
368     // Store the size used to layout the text.
369     visualModel->mControlSize = size;
370   }
371
372   // Whether there are modify events.
373   if(0u != impl.mModifyEvents.Count())
374   {
375     // Style operations that need to be done if the text is modified.
376     operationsPending = static_cast<OperationsMask>(operationsPending | COLOR);
377   }
378
379   // Set the update info to elide the text.
380   if(model->mElideEnabled ||
381      ((NULL != impl.mEventData) && impl.mEventData->mIsPlaceholderElideEnabled))
382   {
383     // Update Text layout for applying elided
384     operationsPending                  = static_cast<OperationsMask>(operationsPending |
385                                                     ALIGN |
386                                                     LAYOUT |
387                                                     UPDATE_LAYOUT_SIZE |
388                                                     REORDER);
389     textUpdateInfo.mFullRelayoutNeeded = true;
390     textUpdateInfo.mCharacterIndex     = 0u;
391   }
392
393   if(impl.mLayoutDirection != layoutDirection)
394   {
395     // Clear the update info. This info will be set the next time the text is updated.
396     textUpdateInfo.mClearAll = true;
397     // Apply modifications to the model
398     // Shape the text again is needed because characters like '()[]{}' have to be mirrored and the glyphs generated again.
399     operationsPending     = static_cast<OperationsMask>(operationsPending |
400                                                     GET_GLYPH_METRICS |
401                                                     SHAPE_TEXT |
402                                                     UPDATE_DIRECTION |
403                                                     ALIGN |
404                                                     LAYOUT |
405                                                     BIDI_INFO |
406                                                     REORDER);
407     impl.mLayoutDirection = layoutDirection;
408   }
409
410   // Make sure the model is up-to-date before layouting.
411   EventHandler::ProcessModifyEvents(controller);
412   bool updated = impl.UpdateModel(operationsPending);
413
414   // Layout the text.
415   Size layoutSize;
416   updated = DoRelayout(impl, size, operationsPending, layoutSize) || updated;
417
418   if(updated)
419   {
420     updateTextType = MODEL_UPDATED;
421   }
422
423   // Do not re-do any operation until something changes.
424   operationsPending          = NO_OPERATION;
425   model->mScrollPositionLast = model->mScrollPosition;
426
427   // Whether the text control is editable
428   const bool isEditable = NULL != impl.mEventData;
429
430   // Keep the current offset as it will be used to update the decorator's positions (if the size changes).
431   Vector2 offset;
432   if(newSize && isEditable)
433   {
434     offset = model->mScrollPosition;
435   }
436
437   if(!isEditable || !controller.IsMultiLineEnabled())
438   {
439     // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
440     CalculateVerticalOffset(controller, size);
441   }
442
443   if(isEditable)
444   {
445     if(newSize)
446     {
447       // If there is a new size, the scroll position needs to be clamped.
448       impl.ClampHorizontalScroll(layoutSize);
449
450       // Update the decorator's positions is needed if there is a new size.
451       impl.mEventData->mDecorator->UpdatePositions(model->mScrollPosition - offset);
452     }
453
454     // Move the cursor, grab handle etc.
455     if(impl.ProcessInputEvents())
456     {
457       updateTextType = static_cast<UpdateTextType>(updateTextType | DECORATOR_UPDATED);
458     }
459   }
460
461   // Clear the update info. This info will be set the next time the text is updated.
462   textUpdateInfo.Clear();
463   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::Relayout\n");
464
465   return updateTextType;
466 }
467
468 bool Controller::Relayouter::DoRelayout(Controller::Impl& impl, const Size& size, OperationsMask operationsRequired, Size& layoutSize)
469 {
470   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::Relayouter::DoRelayout %p size %f,%f\n", &impl, size.width, size.height);
471   bool viewUpdated(false);
472
473   // Calculate the operations to be done.
474   const OperationsMask operations = static_cast<OperationsMask>(impl.mOperationsPending & operationsRequired);
475
476   TextUpdateInfo&      textUpdateInfo              = impl.mTextUpdateInfo;
477   const CharacterIndex startIndex                  = textUpdateInfo.mParagraphCharacterIndex;
478   const Length         requestedNumberOfCharacters = textUpdateInfo.mRequestedNumberOfCharacters;
479
480   // Get the current layout size.
481   VisualModelPtr& visualModel = impl.mModel->mVisualModel;
482   layoutSize                  = visualModel->GetLayoutSize();
483
484   if(NO_OPERATION != (LAYOUT & operations))
485   {
486     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
487
488     // Some vectors with data needed to layout and reorder may be void
489     // after the first time the text has been laid out.
490     // Fill the vectors again.
491
492     // Calculate the number of glyphs to layout.
493     const Vector<GlyphIndex>& charactersToGlyph        = visualModel->mCharactersToGlyph;
494     const Vector<Length>&     glyphsPerCharacter       = visualModel->mGlyphsPerCharacter;
495     const GlyphIndex* const   charactersToGlyphBuffer  = charactersToGlyph.Begin();
496     const Length* const       glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
497
498     const CharacterIndex lastIndex       = startIndex + ((requestedNumberOfCharacters > 0u) ? requestedNumberOfCharacters - 1u : 0u);
499     const GlyphIndex     startGlyphIndex = textUpdateInfo.mStartGlyphIndex;
500
501     // Make sure the index is not out of bound
502     if(charactersToGlyph.Count() != glyphsPerCharacter.Count() ||
503        requestedNumberOfCharacters > charactersToGlyph.Count() ||
504        (lastIndex > charactersToGlyph.Count() && charactersToGlyph.Count() > 0u))
505     {
506       std::string currentText;
507       impl.GetText(currentText);
508
509       DALI_LOG_ERROR("Controller::DoRelayout: Attempting to access invalid buffer\n");
510       DALI_LOG_ERROR("Current text is: %s\n", currentText.c_str());
511       DALI_LOG_ERROR("startIndex: %u, lastIndex: %u, requestedNumberOfCharacters: %u, charactersToGlyph.Count = %lu, glyphsPerCharacter.Count = %lu\n", startIndex, lastIndex, requestedNumberOfCharacters, charactersToGlyph.Count(), glyphsPerCharacter.Count());
512
513       return false;
514     }
515
516     const Length numberOfGlyphs      = (requestedNumberOfCharacters > 0u) ? *(charactersToGlyphBuffer + lastIndex) + *(glyphsPerCharacterBuffer + lastIndex) - startGlyphIndex : 0u;
517     const Length totalNumberOfGlyphs = visualModel->mGlyphs.Count();
518
519     if(0u == totalNumberOfGlyphs)
520     {
521       if(NO_OPERATION != (UPDATE_LAYOUT_SIZE & operations))
522       {
523         visualModel->SetLayoutSize(Size::ZERO);
524       }
525
526       // Nothing else to do if there is no glyphs.
527       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n");
528       return true;
529     }
530
531     // Set the layout parameters.
532     Layout::Parameters layoutParameters(size, impl.mModel);
533
534     // Resize the vector of positions to have the same size than the vector of glyphs.
535     Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
536     glyphPositions.Resize(totalNumberOfGlyphs);
537
538     // Whether the last character is a new paragraph character.
539     const Character* const textBuffer           = impl.mModel->mLogicalModel->mText.Begin();
540     textUpdateInfo.mIsLastCharacterNewParagraph = TextAbstraction::IsNewParagraph(*(textBuffer + (impl.mModel->mLogicalModel->mText.Count() - 1u)));
541     layoutParameters.isLastNewParagraph         = textUpdateInfo.mIsLastCharacterNewParagraph;
542
543     // The initial glyph and the number of glyphs to layout.
544     layoutParameters.startGlyphIndex        = startGlyphIndex;
545     layoutParameters.numberOfGlyphs         = numberOfGlyphs;
546     layoutParameters.startLineIndex         = textUpdateInfo.mStartLineIndex;
547     layoutParameters.estimatedNumberOfLines = textUpdateInfo.mEstimatedNumberOfLines;
548
549     // Update the ellipsis
550     bool elideTextEnabled = impl.mModel->mElideEnabled;
551     auto ellipsisPosition = impl.mModel->mEllipsisPosition;
552
553     if(NULL != impl.mEventData)
554     {
555       if(impl.mEventData->mPlaceholderEllipsisFlag && impl.IsShowingPlaceholderText())
556       {
557         elideTextEnabled = impl.mEventData->mIsPlaceholderElideEnabled;
558       }
559       else if(EventData::INACTIVE != impl.mEventData->mState)
560       {
561         // Disable ellipsis when editing
562         elideTextEnabled = false;
563       }
564
565       // Reset the scroll position in inactive state
566       if(elideTextEnabled && (impl.mEventData->mState == EventData::INACTIVE))
567       {
568         impl.ResetScrollPosition();
569       }
570     }
571
572     // Update the visual model.
573     bool isAutoScrollEnabled = impl.mIsAutoScrollEnabled;
574     Size newLayoutSize;
575     viewUpdated               = impl.mLayoutEngine.LayoutText(layoutParameters,
576                                                 newLayoutSize,
577                                                 elideTextEnabled,
578                                                 isAutoScrollEnabled,
579                                                 ellipsisPosition);
580     impl.mIsAutoScrollEnabled = isAutoScrollEnabled;
581
582     viewUpdated = viewUpdated || (newLayoutSize != layoutSize);
583
584     if(viewUpdated)
585     {
586       layoutSize = newLayoutSize;
587
588       if(NO_OPERATION != (UPDATE_DIRECTION & operations))
589       {
590         impl.mIsTextDirectionRTL = false;
591       }
592
593       if((NO_OPERATION != (UPDATE_DIRECTION & operations)) && !visualModel->mLines.Empty())
594       {
595         impl.mIsTextDirectionRTL = visualModel->mLines[0u].direction;
596       }
597
598       // Sets the layout size.
599       if(NO_OPERATION != (UPDATE_LAYOUT_SIZE & operations))
600       {
601         visualModel->SetLayoutSize(layoutSize);
602       }
603     } // view updated
604   }
605
606   if(NO_OPERATION != (ALIGN & operations))
607   {
608     // The laid-out lines.
609     Vector<LineRun>& lines = visualModel->mLines;
610
611     CharacterIndex alignStartIndex                  = startIndex;
612     Length         alignRequestedNumberOfCharacters = requestedNumberOfCharacters;
613
614     // the whole text needs to be full aligned.
615     // If you do not do a full aligned, only the last line of the multiline input is aligned.
616     if(impl.mEventData && impl.mEventData->mUpdateAlignment)
617     {
618       alignStartIndex                   = 0u;
619       alignRequestedNumberOfCharacters  = impl.mModel->mLogicalModel->mText.Count();
620       impl.mEventData->mUpdateAlignment = false;
621     }
622
623     // Need to align with the control's size as the text may contain lines
624     // starting either with left to right text or right to left.
625     impl.mLayoutEngine.Align(size,
626                              alignStartIndex,
627                              alignRequestedNumberOfCharacters,
628                              impl.mModel->mHorizontalAlignment,
629                              lines,
630                              impl.mModel->mAlignmentOffset,
631                              impl.mLayoutDirection,
632                              (impl.mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS));
633
634     viewUpdated = true;
635   }
636 #if defined(DEBUG_ENABLED)
637   std::string currentText;
638   impl.GetText(currentText);
639   DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::Relayouter::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", &impl, (impl.mIsTextDirectionRTL) ? "true" : "false", currentText.c_str());
640 #endif
641   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::Relayouter::DoRelayout, view updated %s\n", (viewUpdated ? "true" : "false"));
642   return viewUpdated;
643 }
644
645 void Controller::Relayouter::CalculateVerticalOffset(Controller& controller, const Size& controlSize)
646 {
647   Controller::Impl& impl                  = *controller.mImpl;
648   ModelPtr&         model                 = impl.mModel;
649   VisualModelPtr&   visualModel           = model->mVisualModel;
650   Size              layoutSize            = model->mVisualModel->GetLayoutSize();
651   Size              oldLayoutSize         = layoutSize;
652   float             offsetY               = 0.f;
653   bool              needRecalc            = false;
654   float             defaultFontLineHeight = impl.GetDefaultFontLineHeight();
655
656   if(fabsf(layoutSize.height) < Math::MACHINE_EPSILON_1000)
657   {
658     // Get the line height of the default font.
659     layoutSize.height = defaultFontLineHeight;
660   }
661
662   // Whether the text control is editable
663   const bool isEditable = NULL != impl.mEventData;
664   if(isEditable && layoutSize.height != defaultFontLineHeight && impl.IsShowingPlaceholderText())
665   {
666     // This code prevents the wrong positioning of cursor when the layout size is bigger/smaller than defaultFontLineHeight.
667     // This situation occurs when the size of placeholder text is different from the default text.
668     layoutSize.height = defaultFontLineHeight;
669     needRecalc        = true;
670   }
671
672   switch(model->mVerticalAlignment)
673   {
674     case VerticalAlignment::TOP:
675     {
676       model->mScrollPosition.y = 0.f;
677       offsetY                  = 0.f;
678       break;
679     }
680     case VerticalAlignment::CENTER:
681     {
682       model->mScrollPosition.y = floorf(0.5f * (controlSize.height - layoutSize.height)); // try to avoid pixel alignment.
683       if(needRecalc) offsetY = floorf(0.5f * (layoutSize.height - oldLayoutSize.height));
684       break;
685     }
686     case VerticalAlignment::BOTTOM:
687     {
688       model->mScrollPosition.y = controlSize.height - layoutSize.height;
689       if(needRecalc) offsetY = layoutSize.height - oldLayoutSize.height;
690       break;
691     }
692   }
693
694   if(needRecalc)
695   {
696     // Update glyphPositions according to recalculation.
697     const Length     positionCount  = visualModel->mGlyphPositions.Count();
698     Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
699     for(Length index = 0u; index < positionCount; index++)
700     {
701       glyphPositions[index].y += offsetY;
702     }
703   }
704 }
705
706 } // namespace Text
707
708 } // namespace Toolkit
709
710 } // namespace Dali