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