Further refactoring of text-controller-impl
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-selection-handle-controller.cpp
1 /*
2  * Copyright (c) 2020 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/text-controller-impl-event-handler.h>
27
28 using namespace Dali;
29
30 namespace
31 {
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
57 namespace Toolkit
58 {
59
60 namespace Text
61 {
62
63 void SelectionHandleController::Reposition(Controller::Impl& impl)
64 {
65   EventData*& eventData = impl.mEventData;
66
67   CharacterIndex selectionStart = eventData->mLeftSelectionPosition;
68   CharacterIndex selectionEnd = eventData->mRightSelectionPosition;
69
70   DecoratorPtr& decorator = eventData->mDecorator;
71
72   if( selectionStart == selectionEnd )
73   {
74     // Nothing to select if handles are in the same place.
75     // So, deactive Highlight box.
76     decorator->SetHighlightActive( false );
77     return;
78   }
79
80   decorator->ClearHighlights();
81
82   ModelPtr& model = impl.mModel;
83   VisualModelPtr& visualModel = model->mVisualModel;
84   LogicalModelPtr& logicalModel = model->mLogicalModel;
85
86   const GlyphIndex* const charactersToGlyphBuffer = visualModel->mCharactersToGlyph.Begin();
87   const Length* const glyphsPerCharacterBuffer = visualModel->mGlyphsPerCharacter.Begin();
88   const GlyphInfo* const glyphsBuffer = visualModel->mGlyphs.Begin();
89   const Vector2* const positionsBuffer = visualModel->mGlyphPositions.Begin();
90   const Length* const charactersPerGlyphBuffer = visualModel->mCharactersPerGlyph.Begin();
91   const CharacterIndex* const glyphToCharacterBuffer = visualModel->mGlyphsToCharacters.Begin();
92   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != logicalModel->mCharacterDirections.Count() ) ? logicalModel->mCharacterDirections.Begin() : NULL;
93
94   const bool isLastCharacter = selectionEnd >= logicalModel->mText.Count();
95   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
96   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
97
98   // Swap the indices if the start is greater than the end.
99   const bool indicesSwapped = selectionStart > selectionEnd;
100
101   // Tell the decorator to flip the selection handles if needed.
102   decorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
103
104   if( indicesSwapped )
105   {
106     std::swap( selectionStart, selectionEnd );
107   }
108
109   // Get the indices to the first and last selected glyphs.
110   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
111   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
112   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
113   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
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   // The line height is the addition of the line ascender and the line descender.
152   // However, the line descender has a negative value, hence the subtraction.
153   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
154
155   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
156
157   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
158   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
159   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionStart ) );
160
161   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
162   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
163   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionEndMinusOne ) );
164
165   // The number of quads of the selection box.
166   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
167   decorator->ResizeHighlightQuads( numberOfQuads );
168
169   // Count the actual number of quads.
170   unsigned int actualNumberOfQuads = 0u;
171   Vector4 quad;
172
173   // Traverse the glyphs.
174   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
175   {
176     const GlyphInfo& glyph = *( glyphsBuffer + index );
177     const Vector2& position = *( positionsBuffer + index );
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 = glyph.advance / 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 = glyph.advance / 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 + glyph.advance;
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         // The line height is the addition of the line ascender and the line descender.
283         // However, the line descender has a negative value, hence the subtraction.
284         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
285       }
286     }
287   }
288
289   // Traverses all the lines and updates the min and max 'x' positions and the total height.
290   // The final width is calculated after 'boxifying' the selection.
291   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
292          endIt = selectionBoxLinesInfo.End();
293        it != endIt;
294        ++it )
295   {
296     const SelectionBoxInfo& info = *it;
297
298     // Update the size of the highlighted text.
299     highLightSize.height += info.lineHeight;
300     minHighlightX = std::min( minHighlightX, info.minX );
301     maxHighlightX = std::max( maxHighlightX, info.maxX );
302   }
303
304   // Add extra geometry to 'boxify' the selection.
305
306   if( 1u < numberOfLines )
307   {
308     // Boxify the first line.
309     lineRun = visualModel->mLines.Begin() + firstLineIndex;
310     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
311
312     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
313     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
314
315     if( boxifyBegin )
316     {
317       quad.x = 0.f;
318       quad.y = firstSelectionBoxLineInfo.lineOffset;
319       quad.z = firstSelectionBoxLineInfo.minX;
320       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
321
322       // Boxify at the beginning of the line.
323       decorator->AddHighlight( actualNumberOfQuads,
324                                             quad );
325       ++actualNumberOfQuads;
326
327       // Update the size of the highlighted text.
328       minHighlightX = 0.f;
329     }
330
331     if( boxifyEnd )
332     {
333       quad.x = firstSelectionBoxLineInfo.maxX;
334       quad.y = firstSelectionBoxLineInfo.lineOffset;
335       quad.z = visualModel->mControlSize.width;
336       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
337
338       // Boxify at the end of the line.
339       decorator->AddHighlight( actualNumberOfQuads,
340                                             quad );
341       ++actualNumberOfQuads;
342
343       // Update the size of the highlighted text.
344       maxHighlightX = visualModel->mControlSize.width;
345     }
346
347     // Boxify the central lines.
348     if( 2u < numberOfLines )
349     {
350       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
351              endIt = selectionBoxLinesInfo.End() - 1u;
352            it != endIt;
353            ++it )
354       {
355         const SelectionBoxInfo& info = *it;
356
357         quad.x = 0.f;
358         quad.y = info.lineOffset;
359         quad.z = info.minX;
360         quad.w = info.lineOffset + info.lineHeight;
361
362         decorator->AddHighlight( actualNumberOfQuads,
363                                               quad );
364         ++actualNumberOfQuads;
365
366         quad.x = info.maxX;
367         quad.y = info.lineOffset;
368         quad.z = visualModel->mControlSize.width;
369         quad.w = info.lineOffset + info.lineHeight;
370
371         decorator->AddHighlight( actualNumberOfQuads,
372                                               quad );
373         ++actualNumberOfQuads;
374       }
375
376       // Update the size of the highlighted text.
377       minHighlightX = 0.f;
378       maxHighlightX = visualModel->mControlSize.width;
379     }
380
381     // Boxify the last line.
382     lineRun = visualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
383     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
384
385     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
386     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
387
388     if( boxifyBegin )
389     {
390       quad.x = 0.f;
391       quad.y = lastSelectionBoxLineInfo.lineOffset;
392       quad.z = lastSelectionBoxLineInfo.minX;
393       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
394
395       // Boxify at the beginning of the line.
396       decorator->AddHighlight( actualNumberOfQuads,
397                                             quad );
398       ++actualNumberOfQuads;
399
400       // Update the size of the highlighted text.
401       minHighlightX = 0.f;
402     }
403
404     if( boxifyEnd )
405     {
406       quad.x = lastSelectionBoxLineInfo.maxX;
407       quad.y = lastSelectionBoxLineInfo.lineOffset;
408       quad.z = visualModel->mControlSize.width;
409       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
410
411       // Boxify at the end of the line.
412       decorator->AddHighlight(actualNumberOfQuads, quad);
413       ++actualNumberOfQuads;
414
415       // Update the size of the highlighted text.
416       maxHighlightX = visualModel->mControlSize.width;
417     }
418   }
419
420   // Set the actual number of quads.
421   decorator->ResizeHighlightQuads( actualNumberOfQuads );
422
423   // Sets the highlight's size and position. In decorator's coords.
424   // The highlight's height has been calculated above (before 'boxifying' the highlight).
425   highLightSize.width = maxHighlightX - minHighlightX;
426
427   highLightPosition.x = minHighlightX;
428   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
429   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
430
431   decorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( model->GetOutlineWidth() ) );
432
433   if( !decorator->IsSmoothHandlePanEnabled() )
434   {
435     CursorInfo primaryCursorInfo;
436     impl.GetCursorPosition(eventData->mLeftSelectionPosition, primaryCursorInfo);
437
438     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + model->mScrollPosition;
439
440     decorator->SetPosition(LEFT_SELECTION_HANDLE,
441                            primaryPosition.x,
442                            primaryCursorInfo.lineOffset + model->mScrollPosition.y,
443                            primaryCursorInfo.lineHeight );
444
445     CursorInfo secondaryCursorInfo;
446     impl.GetCursorPosition(eventData->mRightSelectionPosition, secondaryCursorInfo);
447
448     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + model->mScrollPosition;
449
450     decorator->SetPosition(RIGHT_SELECTION_HANDLE,
451                            secondaryPosition.x,
452                            secondaryCursorInfo.lineOffset + model->mScrollPosition.y,
453                            secondaryCursorInfo.lineHeight );
454   }
455
456   // Set the flag to update the decorator.
457   eventData->mDecoratorUpdated = true;
458 }
459
460 void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action)
461 {
462   EventData*& eventData = impl.mEventData;
463   if(nullptr == eventData)
464   {
465     // Nothing to do if there is no text input.
466     return;
467   }
468
469   if(impl.IsShowingPlaceholderText())
470   {
471     // Nothing to do if there is the place-holder text.
472     return;
473   }
474
475   ModelPtr& model = impl.mModel;
476   VisualModelPtr& visualModel = model->mVisualModel;
477   const Length numberOfGlyphs = visualModel->mGlyphs.Count();
478   const Length numberOfLines  = visualModel->mLines.Count();
479   if( ( 0 == numberOfGlyphs ) ||
480       ( 0 == numberOfLines ) )
481   {
482     // Nothing to do if there is no text.
483     return;
484   }
485
486   // Find which word was selected
487   CharacterIndex selectionStart( 0 );
488   CharacterIndex selectionEnd( 0 );
489   CharacterIndex noTextHitIndex( 0 );
490   const bool characterHit = FindSelectionIndices( visualModel,
491                                                   model->mLogicalModel,
492                                                   impl.mMetrics,
493                                                   visualX,
494                                                   visualY,
495                                                   selectionStart,
496                                                   selectionEnd,
497                                                   noTextHitIndex );
498   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", &impl, selectionStart, selectionEnd );
499
500   if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
501   {
502     impl.ChangeState( EventData::SELECTING );
503
504     eventData->mLeftSelectionPosition = selectionStart;
505     eventData->mRightSelectionPosition = selectionEnd;
506
507     eventData->mUpdateLeftSelectionPosition = true;
508     eventData->mUpdateRightSelectionPosition = true;
509     eventData->mUpdateHighlightBox = true;
510
511     // It may happen an InputMethodContext commit event arrives before the selection event
512     // if the InputMethodContext is in pre-edit state. The commit event will set the
513     // eventData->mUpdateCursorPosition flag to true. If it's not set back
514     // to false, the highlight box won't be updated.
515     eventData->mUpdateCursorPosition = false;
516
517     eventData->mScrollAfterUpdatePosition = ( eventData->mLeftSelectionPosition != eventData->mRightSelectionPosition );
518
519     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
520     eventData->mPrimaryCursorPosition = std::max( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
521   }
522   else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
523   {
524     // Nothing to select. i.e. a white space, out of bounds
525     impl.ChangeState( EventData::EDITING_WITH_POPUP );
526
527     eventData->mPrimaryCursorPosition = noTextHitIndex;
528
529     eventData->mUpdateCursorPosition = true;
530     eventData->mUpdateGrabHandlePosition = true;
531     eventData->mScrollAfterUpdatePosition = true;
532     eventData->mUpdateInputStyle = true;
533   }
534   else if( Controller::NoTextTap::NO_ACTION == action )
535   {
536     // Nothing to select. i.e. a white space, out of bounds
537     eventData->mPrimaryCursorPosition = noTextHitIndex;
538
539     eventData->mUpdateCursorPosition = true;
540     eventData->mUpdateGrabHandlePosition = true;
541     eventData->mScrollAfterUpdatePosition = true;
542     eventData->mUpdateInputStyle = true;
543   }
544 }
545
546 void SelectionHandleController::Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo)
547 {
548   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
549       ( RIGHT_SELECTION_HANDLE != handleType ) )
550   {
551     return;
552   }
553
554   ModelPtr& model = impl.mModel;
555   const Vector2 cursorPosition = cursorInfo.primaryPosition + model->mScrollPosition;
556
557   // Sets the handle's position.
558   EventData*& eventData = impl.mEventData;
559   eventData->mDecorator->SetPosition(handleType,
560                                      cursorPosition.x,
561                                      cursorInfo.lineOffset + model->mScrollPosition.y,
562                                      cursorInfo.lineHeight );
563
564   // If selection handle at start of the text and other at end of the text then all text is selected.
565   const CharacterIndex startOfSelection = std::min( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
566   const CharacterIndex endOfSelection = std::max ( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
567   eventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == model->mLogicalModel->mText.Count() );
568 }
569
570 } // namespace Text
571
572 } // namespace Toolkit
573
574 } // namespace Dali