Support character-spacing tag in markup
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-selection-handle-controller.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
20
21 #include <dali/integration-api/debug.h>
22 #include <limits>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
26 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
27 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
28 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
29
30 using namespace Dali;
31
32 namespace
33 {
34 /**
35  * @brief Struct used to calculate the selection box.
36  */
37 struct SelectionBoxInfo
38 {
39   float lineOffset;
40   float lineHeight;
41   float minX;
42   float maxX;
43 };
44
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
47 #endif
48
49 const float                                   MAX_FLOAT = std::numeric_limits<float>::max();
50 const float                                   MIN_FLOAT = std::numeric_limits<float>::min();
51 const Dali::Toolkit::Text::CharacterDirection LTR       = false; ///< Left To Right direction
52
53 } // namespace
54
55 namespace Dali
56 {
57 namespace Toolkit
58 {
59 namespace Text
60 {
61 void SelectionHandleController::Reposition(Controller::Impl& impl)
62 {
63   EventData*& eventData = impl.mEventData;
64
65   CharacterIndex selectionStart = eventData->mLeftSelectionPosition;
66   CharacterIndex selectionEnd   = eventData->mRightSelectionPosition;
67
68   DecoratorPtr& decorator = eventData->mDecorator;
69
70   if(selectionStart == selectionEnd)
71   {
72     // Nothing to select if handles are in the same place.
73     // So, deactive Highlight box.
74     decorator->SetHighlightActive(false);
75     return;
76   }
77
78   decorator->ClearHighlights();
79
80   ModelPtr&        model        = impl.mModel;
81   VisualModelPtr&  visualModel  = model->mVisualModel;
82   LogicalModelPtr& logicalModel = model->mLogicalModel;
83
84   const GlyphIndex* const         charactersToGlyphBuffer        = visualModel->mCharactersToGlyph.Begin();
85   const Length* const             glyphsPerCharacterBuffer       = visualModel->mGlyphsPerCharacter.Begin();
86   const GlyphInfo* const          glyphsBuffer                   = visualModel->mGlyphs.Begin();
87   const Vector2* const            positionsBuffer                = visualModel->mGlyphPositions.Begin();
88   const Length* const             charactersPerGlyphBuffer       = visualModel->mCharactersPerGlyph.Begin();
89   const CharacterIndex* const     glyphToCharacterBuffer         = visualModel->mGlyphsToCharacters.Begin();
90   const CharacterDirection* const modelCharacterDirectionsBuffer = (0u != logicalModel->mCharacterDirections.Count()) ? logicalModel->mCharacterDirections.Begin() : NULL;
91
92   const bool               isLastCharacter = selectionEnd >= logicalModel->mText.Count();
93   const CharacterDirection startDirection  = ((NULL == modelCharacterDirectionsBuffer) ? false : *(modelCharacterDirectionsBuffer + selectionStart));
94   const CharacterDirection endDirection    = ((NULL == modelCharacterDirectionsBuffer) ? false : *(modelCharacterDirectionsBuffer + (selectionEnd - (isLastCharacter ? 1u : 0u))));
95
96   // Swap the indices if the start is greater than the end.
97   const bool indicesSwapped = selectionStart > selectionEnd;
98
99   // Tell the decorator to flip the selection handles if needed.
100   decorator->SetSelectionHandleFlipState(indicesSwapped, startDirection, endDirection);
101
102   if(indicesSwapped)
103   {
104     std::swap(selectionStart, selectionEnd);
105   }
106
107   // Get the indices to the first and last selected glyphs.
108   const CharacterIndex    selectionEndMinusOne      = selectionEnd - 1u;
109   const GlyphIndex        glyphStart                = *(charactersToGlyphBuffer + selectionStart);
110   const Length            numberOfGlyphs            = *(glyphsPerCharacterBuffer + selectionEndMinusOne);
111   const GlyphIndex        glyphEnd                  = *(charactersToGlyphBuffer + selectionEndMinusOne) + ((numberOfGlyphs > 0) ? numberOfGlyphs - 1u : 0u);
112   const float             modelCharacterSpacing     = visualModel->GetCharacterSpacing();
113   Vector<CharacterIndex>& glyphToCharacterMap       = visualModel->mGlyphsToCharacters;
114   const CharacterIndex*   glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
115
116   // Get the character-spacing runs.
117   const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = visualModel->GetCharacterSpacingGlyphRuns();
118
119   // Get the lines where the glyphs are laid-out.
120   const LineRun* lineRun = visualModel->mLines.Begin();
121
122   LineIndex lineIndex     = 0u;
123   Length    numberOfLines = 0u;
124   visualModel->GetNumberOfLines(glyphStart,
125                                 1u + glyphEnd - glyphStart,
126                                 lineIndex,
127                                 numberOfLines);
128   const LineIndex firstLineIndex = lineIndex;
129
130   // Create the structure to store some selection box info.
131   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
132   selectionBoxLinesInfo.Resize(numberOfLines);
133
134   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
135   selectionBoxInfo->minX             = MAX_FLOAT;
136   selectionBoxInfo->maxX             = MIN_FLOAT;
137
138   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
139   float   minHighlightX = std::numeric_limits<float>::max();
140   float   maxHighlightX = std::numeric_limits<float>::min();
141   Size    highLightSize;
142   Vector2 highLightPosition; // The highlight position in decorator's coords.
143
144   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
145
146   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
147   selectionBoxInfo->lineOffset = CalculateLineOffset(visualModel->mLines,
148                                                      firstLineIndex);
149
150   // Transform to decorator's (control) coords.
151   selectionBoxInfo->lineOffset += model->mScrollPosition.y;
152
153   lineRun += firstLineIndex;
154
155   // The line height is the addition of the line ascender and the line descender.
156   // However, the line descender has a negative value, hence the subtraction also line spacing should not be included in selection height.
157   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
158
159   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
160
161   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
162   const Length numberOfCharactersStart = *(charactersPerGlyphBuffer + glyphStart);
163   bool         splitStartGlyph         = (numberOfCharactersStart > 1u) && HasLigatureMustBreak(logicalModel->GetScript(selectionStart));
164
165   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
166   const Length numberOfCharactersEnd = *(charactersPerGlyphBuffer + glyphEnd);
167   bool         splitEndGlyph         = (glyphStart != glyphEnd) && (numberOfCharactersEnd > 1u) && HasLigatureMustBreak(logicalModel->GetScript(selectionEndMinusOne));
168
169   // The number of quads of the selection box.
170   const unsigned int numberOfQuads = 1u + (glyphEnd - glyphStart) + ((numberOfLines > 1u) ? 2u * numberOfLines : 0u);
171   decorator->ResizeHighlightQuads(numberOfQuads);
172
173   // Count the actual number of quads.
174   unsigned int actualNumberOfQuads = 0u;
175   Vector4      quad;
176   float        calculatedAdvance = 0.f;
177
178   // Traverse the glyphs.
179   for(GlyphIndex index = glyphStart; index <= glyphEnd; ++index)
180   {
181     const float      characterSpacing = GetGlyphCharacterSpacing(index, characterSpacingGlyphRuns, modelCharacterSpacing);
182     const GlyphInfo& glyph            = *(glyphsBuffer + index);
183     const Vector2&   position         = *(positionsBuffer + index);
184     calculatedAdvance                 = GetCalculatedAdvance(*(logicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + index))), characterSpacing, glyph.advance);
185
186     if(splitStartGlyph)
187     {
188       // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
189
190       const float          glyphAdvance    = calculatedAdvance / static_cast<float>(numberOfCharactersStart);
191       const CharacterIndex interGlyphIndex = selectionStart - *(glyphToCharacterBuffer + glyphStart);
192       // Get the direction of the character.
193       CharacterDirection isCurrentRightToLeft = false;
194       if(nullptr != modelCharacterDirectionsBuffer) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
195       {
196         isCurrentRightToLeft = *(modelCharacterDirectionsBuffer + selectionStart);
197       }
198
199       // The end point could be in the middle of the ligature.
200       // Calculate the number of characters selected.
201       const Length numberOfCharacters = (glyphStart == glyphEnd) ? (selectionEnd - selectionStart) : (numberOfCharactersStart - interGlyphIndex);
202
203       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + glyphAdvance * static_cast<float>(isCurrentRightToLeft ? (numberOfCharactersStart - interGlyphIndex - numberOfCharacters) : interGlyphIndex);
204       quad.y = selectionBoxInfo->lineOffset;
205       quad.z = quad.x + static_cast<float>(numberOfCharacters) * glyphAdvance;
206       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
207
208       // Store the min and max 'x' for each line.
209       selectionBoxInfo->minX = std::min(selectionBoxInfo->minX, quad.x);
210       selectionBoxInfo->maxX = std::max(selectionBoxInfo->maxX, quad.z);
211
212       decorator->AddHighlight(actualNumberOfQuads, quad);
213       ++actualNumberOfQuads;
214
215       splitStartGlyph = false;
216       continue;
217     }
218
219     if(splitEndGlyph && (index == glyphEnd))
220     {
221       // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
222
223       const float          glyphAdvance    = calculatedAdvance / static_cast<float>(numberOfCharactersEnd);
224       const CharacterIndex interGlyphIndex = selectionEnd - *(glyphToCharacterBuffer + glyphEnd);
225       // Get the direction of the character.
226       CharacterDirection isCurrentRightToLeft = false;
227       if(nullptr != modelCharacterDirectionsBuffer) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
228       {
229         isCurrentRightToLeft = *(modelCharacterDirectionsBuffer + selectionEnd);
230       }
231
232       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
233
234       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + (isCurrentRightToLeft ? (glyphAdvance * static_cast<float>(numberOfCharacters)) : 0.f);
235       quad.y = selectionBoxInfo->lineOffset;
236       quad.z = quad.x + static_cast<float>(interGlyphIndex) * glyphAdvance;
237       quad.w = quad.y + selectionBoxInfo->lineHeight;
238
239       // Store the min and max 'x' for each line.
240       selectionBoxInfo->minX = std::min(selectionBoxInfo->minX, quad.x);
241       selectionBoxInfo->maxX = std::max(selectionBoxInfo->maxX, quad.z);
242
243       decorator->AddHighlight(actualNumberOfQuads,
244                               quad);
245       ++actualNumberOfQuads;
246
247       splitEndGlyph = false;
248       continue;
249     }
250
251     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x;
252     quad.y = selectionBoxInfo->lineOffset;
253     quad.z = quad.x + calculatedAdvance;
254     quad.w = quad.y + selectionBoxInfo->lineHeight;
255
256     // Store the min and max 'x' for each line.
257     selectionBoxInfo->minX = std::min(selectionBoxInfo->minX, quad.x);
258     selectionBoxInfo->maxX = std::max(selectionBoxInfo->maxX, quad.z);
259
260     decorator->AddHighlight(actualNumberOfQuads,
261                             quad);
262     ++actualNumberOfQuads;
263
264     // Whether to retrieve the next line.
265     if(index == lastGlyphOfLine)
266     {
267       ++lineIndex;
268       if(lineIndex < firstLineIndex + numberOfLines)
269       {
270         float currentLineSpacing = lineRun->lineSpacing;
271
272         // Retrieve the next line.
273         ++lineRun;
274
275         // Get the last glyph of the new line.
276         lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
277
278         // Keep the offset and height of the current selection box.
279         const float currentLineOffset = selectionBoxInfo->lineOffset;
280         const float currentLineHeight = selectionBoxInfo->lineHeight;
281
282         // Get the selection box info for the next line.
283         ++selectionBoxInfo;
284
285         selectionBoxInfo->minX = MAX_FLOAT;
286         selectionBoxInfo->maxX = MIN_FLOAT;
287
288         // Update the line's vertical offset.
289         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
290
291         if(currentLineSpacing < 0)
292         {
293           selectionBoxInfo->lineOffset += currentLineSpacing;
294         }
295
296         // The line height is the addition of the line ascender and the line descender.
297         // However, the line descender has a negative value, hence the subtraction also line spacing should not be included in selection height.
298         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
299
300         if(lineRun->lineSpacing > 0)
301         {
302           selectionBoxInfo->lineHeight += lineRun->lineSpacing;
303         }
304       }
305     }
306   }
307
308   // Traverses all the lines and updates the min and max 'x' positions and the total height.
309   // The final width is calculated after 'boxifying' the selection.
310   for(Vector<SelectionBoxInfo>::ConstIterator it    = selectionBoxLinesInfo.Begin(),
311                                               endIt = selectionBoxLinesInfo.End();
312       it != endIt;
313       ++it)
314   {
315     const SelectionBoxInfo& info = *it;
316
317     // Update the size of the highlighted text.
318     highLightSize.height += info.lineHeight;
319     minHighlightX = std::min(minHighlightX, info.minX);
320     maxHighlightX = std::max(maxHighlightX, info.maxX);
321   }
322
323   // Add extra geometry to 'boxify' the selection.
324
325   if(1u < numberOfLines)
326   {
327     // Boxify the first line.
328     lineRun                                           = visualModel->mLines.Begin() + firstLineIndex;
329     const SelectionBoxInfo& firstSelectionBoxLineInfo = *(selectionBoxLinesInfo.Begin());
330
331     bool boxifyBegin = (LTR != lineRun->direction) && (LTR != startDirection);
332     bool boxifyEnd   = (LTR == lineRun->direction) && (LTR == startDirection);
333
334     if(boxifyBegin)
335     {
336       quad.x = 0.f;
337       quad.y = firstSelectionBoxLineInfo.lineOffset;
338       quad.z = firstSelectionBoxLineInfo.minX;
339       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
340
341       // Boxify at the beginning of the line.
342       decorator->AddHighlight(actualNumberOfQuads,
343                               quad);
344       ++actualNumberOfQuads;
345
346       // Update the size of the highlighted text.
347       minHighlightX = 0.f;
348     }
349
350     if(boxifyEnd)
351     {
352       quad.x = firstSelectionBoxLineInfo.maxX;
353       quad.y = firstSelectionBoxLineInfo.lineOffset;
354       quad.z = visualModel->mControlSize.width;
355       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
356
357       // Boxify at the end of the line.
358       decorator->AddHighlight(actualNumberOfQuads,
359                               quad);
360       ++actualNumberOfQuads;
361
362       // Update the size of the highlighted text.
363       maxHighlightX = visualModel->mControlSize.width;
364     }
365
366     // Boxify the central lines.
367     if(2u < numberOfLines)
368     {
369       for(Vector<SelectionBoxInfo>::ConstIterator it    = selectionBoxLinesInfo.Begin() + 1u,
370                                                   endIt = selectionBoxLinesInfo.End() - 1u;
371           it != endIt;
372           ++it)
373       {
374         const SelectionBoxInfo& info = *it;
375
376         quad.x = 0.f;
377         quad.y = info.lineOffset;
378         quad.z = info.minX;
379         quad.w = info.lineOffset + info.lineHeight;
380
381         decorator->AddHighlight(actualNumberOfQuads,
382                                 quad);
383         ++actualNumberOfQuads;
384
385         quad.x = info.maxX;
386         quad.y = info.lineOffset;
387         quad.z = visualModel->mControlSize.width;
388         quad.w = info.lineOffset + info.lineHeight;
389
390         decorator->AddHighlight(actualNumberOfQuads,
391                                 quad);
392         ++actualNumberOfQuads;
393       }
394
395       // Update the size of the highlighted text.
396       minHighlightX = 0.f;
397       maxHighlightX = visualModel->mControlSize.width;
398     }
399
400     // Boxify the last line.
401     lineRun                                          = visualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
402     const SelectionBoxInfo& lastSelectionBoxLineInfo = *(selectionBoxLinesInfo.End() - 1u);
403
404     boxifyBegin = (LTR == lineRun->direction) && (LTR == endDirection);
405     boxifyEnd   = (LTR != lineRun->direction) && (LTR != endDirection);
406
407     if(boxifyBegin)
408     {
409       quad.x = 0.f;
410       quad.y = lastSelectionBoxLineInfo.lineOffset;
411       quad.z = lastSelectionBoxLineInfo.minX;
412       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
413
414       // Boxify at the beginning of the line.
415       decorator->AddHighlight(actualNumberOfQuads,
416                               quad);
417       ++actualNumberOfQuads;
418
419       // Update the size of the highlighted text.
420       minHighlightX = 0.f;
421     }
422
423     if(boxifyEnd)
424     {
425       quad.x = lastSelectionBoxLineInfo.maxX;
426       quad.y = lastSelectionBoxLineInfo.lineOffset;
427       quad.z = visualModel->mControlSize.width;
428       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
429
430       // Boxify at the end of the line.
431       decorator->AddHighlight(actualNumberOfQuads, quad);
432       ++actualNumberOfQuads;
433
434       // Update the size of the highlighted text.
435       maxHighlightX = visualModel->mControlSize.width;
436     }
437   }
438
439   // Set the actual number of quads.
440   decorator->ResizeHighlightQuads(actualNumberOfQuads);
441
442   // Sets the highlight's size and position. In decorator's coords.
443   // The highlight's height has been calculated above (before 'boxifying' the highlight).
444   highLightSize.width = maxHighlightX - minHighlightX;
445
446   highLightPosition.x                               = minHighlightX;
447   const SelectionBoxInfo& firstSelectionBoxLineInfo = *(selectionBoxLinesInfo.Begin());
448   highLightPosition.y                               = firstSelectionBoxLineInfo.lineOffset;
449
450   decorator->SetHighLightBox(highLightPosition, highLightSize, static_cast<float>(model->GetOutlineWidth()));
451
452   if(!decorator->IsSmoothHandlePanEnabled())
453   {
454     CursorInfo primaryCursorInfo;
455     impl.GetCursorPosition(eventData->mLeftSelectionPosition, primaryCursorInfo);
456
457     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + model->mScrollPosition;
458
459     decorator->SetPosition(LEFT_SELECTION_HANDLE,
460                            primaryPosition.x,
461                            primaryCursorInfo.lineOffset + model->mScrollPosition.y,
462                            primaryCursorInfo.lineHeight);
463
464     CursorInfo secondaryCursorInfo;
465     impl.GetCursorPosition(eventData->mRightSelectionPosition, secondaryCursorInfo);
466
467     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + model->mScrollPosition;
468
469     decorator->SetPosition(RIGHT_SELECTION_HANDLE,
470                            secondaryPosition.x,
471                            secondaryCursorInfo.lineOffset + model->mScrollPosition.y,
472                            secondaryCursorInfo.lineHeight);
473   }
474
475   // Set the flag to update the decorator.
476   eventData->mDecoratorUpdated = true;
477 }
478
479 void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action)
480 {
481   EventData*& eventData = impl.mEventData;
482   if(nullptr == eventData)
483   {
484     // Nothing to do if there is no text input.
485     return;
486   }
487
488   if(impl.IsShowingPlaceholderText())
489   {
490     // Nothing to do if there is the place-holder text.
491     return;
492   }
493
494   ModelPtr&       model          = impl.mModel;
495   VisualModelPtr& visualModel    = model->mVisualModel;
496   const Length    numberOfGlyphs = visualModel->mGlyphs.Count();
497   const Length    numberOfLines  = visualModel->mLines.Count();
498   if((0 == numberOfGlyphs) ||
499      (0 == numberOfLines))
500   {
501     // Nothing to do if there is no text.
502     return;
503   }
504
505   // Find which word was selected
506   CharacterIndex selectionStart(0);
507   CharacterIndex selectionEnd(0);
508   CharacterIndex noTextHitIndex(0);
509   const bool     characterHit = FindSelectionIndices(visualModel,
510                                                  model->mLogicalModel,
511                                                  impl.mMetrics,
512                                                  visualX,
513                                                  visualY,
514                                                  selectionStart,
515                                                  selectionEnd,
516                                                  noTextHitIndex);
517   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", &impl, selectionStart, selectionEnd);
518
519   if(characterHit || (Controller::NoTextTap::HIGHLIGHT == action))
520   {
521     uint32_t oldStart = eventData->mLeftSelectionPosition;
522     uint32_t oldEnd   = eventData->mRightSelectionPosition;
523
524     impl.ChangeState(EventData::SELECTING);
525
526     eventData->mLeftSelectionPosition  = selectionStart;
527     eventData->mRightSelectionPosition = selectionEnd;
528
529     eventData->mUpdateLeftSelectionPosition  = true;
530     eventData->mUpdateRightSelectionPosition = true;
531     eventData->mUpdateHighlightBox           = true;
532
533     // It may happen an InputMethodContext commit event arrives before the selection event
534     // if the InputMethodContext is in pre-edit state. The commit event will set the
535     // eventData->mUpdateCursorPosition flag to true. If it's not set back
536     // to false, the highlight box won't be updated.
537     eventData->mUpdateCursorPosition = false;
538
539     eventData->mScrollAfterUpdatePosition = (eventData->mLeftSelectionPosition != eventData->mRightSelectionPosition);
540
541     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
542     eventData->mPrimaryCursorPosition = std::max(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
543
544     if(impl.mSelectableControlInterface != nullptr)
545     {
546       impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
547     }
548   }
549   else if(Controller::NoTextTap::SHOW_SELECTION_POPUP == action)
550   {
551     // Nothing to select. i.e. a white space, out of bounds
552     impl.ChangeState(EventData::EDITING_WITH_POPUP);
553
554     eventData->mPrimaryCursorPosition = noTextHitIndex;
555
556     eventData->mUpdateCursorPosition      = true;
557     eventData->mUpdateGrabHandlePosition  = true;
558     eventData->mScrollAfterUpdatePosition = true;
559     eventData->mUpdateInputStyle          = true;
560   }
561   else if(Controller::NoTextTap::NO_ACTION == action)
562   {
563     // Nothing to select. i.e. a white space, out of bounds
564     eventData->mPrimaryCursorPosition = noTextHitIndex;
565
566     eventData->mUpdateCursorPosition      = true;
567     eventData->mUpdateGrabHandlePosition  = true;
568     eventData->mScrollAfterUpdatePosition = true;
569     eventData->mUpdateInputStyle          = true;
570   }
571 }
572
573 void SelectionHandleController::Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo)
574 {
575   if((LEFT_SELECTION_HANDLE != handleType) &&
576      (RIGHT_SELECTION_HANDLE != handleType))
577   {
578     return;
579   }
580
581   ModelPtr&     model          = impl.mModel;
582   const Vector2 cursorPosition = cursorInfo.primaryPosition + model->mScrollPosition;
583
584   // Sets the handle's position.
585   EventData*& eventData = impl.mEventData;
586   eventData->mDecorator->SetPosition(handleType,
587                                      cursorPosition.x,
588                                      cursorInfo.lineOffset + model->mScrollPosition.y,
589                                      cursorInfo.lineHeight);
590
591   // If selection handle at start of the text and other at end of the text then all text is selected.
592   const CharacterIndex startOfSelection = std::min(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
593   const CharacterIndex endOfSelection   = std::max(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
594   eventData->mAllTextSelected           = (startOfSelection == 0) && (endOfSelection == model->mLogicalModel->mText.Count());
595 }
596
597 } // namespace Text
598
599 } // namespace Toolkit
600
601 } // namespace Dali