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