[dali_2.3.20] Merge branch 'devel/master'
[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/controller/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 + currentLineSpacing;
290
291         // The line height is the addition of the line ascender and the line descender.
292         // However, the line descender has a negative value, hence the subtraction also line spacing should not be included in selection height.
293         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
294
295         if(lineRun->lineSpacing > 0)
296         {
297           selectionBoxInfo->lineHeight += lineRun->lineSpacing;
298         }
299       }
300     }
301   }
302
303   // Traverses all the lines and updates the min and max 'x' positions and the total height.
304   // The final width is calculated after 'boxifying' the selection.
305   for(Vector<SelectionBoxInfo>::ConstIterator it    = selectionBoxLinesInfo.Begin(),
306                                               endIt = selectionBoxLinesInfo.End();
307       it != endIt;
308       ++it)
309   {
310     const SelectionBoxInfo& info = *it;
311
312     // Update the size of the highlighted text.
313     highLightSize.height += info.lineHeight;
314     minHighlightX = std::min(minHighlightX, info.minX);
315     maxHighlightX = std::max(maxHighlightX, info.maxX);
316   }
317
318   // Add extra geometry to 'boxify' the selection.
319
320   if(1u < numberOfLines)
321   {
322     // Boxify the first line.
323     lineRun                                           = visualModel->mLines.Begin() + firstLineIndex;
324     const SelectionBoxInfo& firstSelectionBoxLineInfo = *(selectionBoxLinesInfo.Begin());
325
326     bool boxifyBegin = (LTR != lineRun->direction) && (LTR != startDirection);
327     bool boxifyEnd   = (LTR == lineRun->direction) && (LTR == startDirection);
328
329     if(boxifyBegin)
330     {
331       quad.x = 0.f;
332       quad.y = firstSelectionBoxLineInfo.lineOffset;
333       quad.z = firstSelectionBoxLineInfo.minX;
334       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
335
336       // Boxify at the beginning of the line.
337       decorator->AddHighlight(actualNumberOfQuads,
338                               quad);
339       ++actualNumberOfQuads;
340
341       // Update the size of the highlighted text.
342       minHighlightX = 0.f;
343     }
344
345     if(boxifyEnd)
346     {
347       quad.x = firstSelectionBoxLineInfo.maxX;
348       quad.y = firstSelectionBoxLineInfo.lineOffset;
349       quad.z = visualModel->mControlSize.width;
350       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
351
352       // Boxify at the end of the line.
353       decorator->AddHighlight(actualNumberOfQuads,
354                               quad);
355       ++actualNumberOfQuads;
356
357       // Update the size of the highlighted text.
358       maxHighlightX = visualModel->mControlSize.width;
359     }
360
361     // Boxify the central lines.
362     if(2u < numberOfLines)
363     {
364       for(Vector<SelectionBoxInfo>::ConstIterator it    = selectionBoxLinesInfo.Begin() + 1u,
365                                                   endIt = selectionBoxLinesInfo.End() - 1u;
366           it != endIt;
367           ++it)
368       {
369         const SelectionBoxInfo& info = *it;
370
371         quad.x = 0.f;
372         quad.y = info.lineOffset;
373         quad.z = info.minX;
374         quad.w = info.lineOffset + info.lineHeight;
375
376         decorator->AddHighlight(actualNumberOfQuads,
377                                 quad);
378         ++actualNumberOfQuads;
379
380         quad.x = info.maxX;
381         quad.y = info.lineOffset;
382         quad.z = visualModel->mControlSize.width;
383         quad.w = info.lineOffset + info.lineHeight;
384
385         decorator->AddHighlight(actualNumberOfQuads,
386                                 quad);
387         ++actualNumberOfQuads;
388       }
389
390       // Update the size of the highlighted text.
391       minHighlightX = 0.f;
392       maxHighlightX = visualModel->mControlSize.width;
393     }
394
395     // Boxify the last line.
396     lineRun                                          = visualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
397     const SelectionBoxInfo& lastSelectionBoxLineInfo = *(selectionBoxLinesInfo.End() - 1u);
398
399     boxifyBegin = (LTR == lineRun->direction) && (LTR == endDirection);
400     boxifyEnd   = (LTR != lineRun->direction) && (LTR != endDirection);
401
402     if(boxifyBegin)
403     {
404       quad.x = 0.f;
405       quad.y = lastSelectionBoxLineInfo.lineOffset;
406       quad.z = lastSelectionBoxLineInfo.minX;
407       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
408
409       // Boxify at the beginning of the line.
410       decorator->AddHighlight(actualNumberOfQuads,
411                               quad);
412       ++actualNumberOfQuads;
413
414       // Update the size of the highlighted text.
415       minHighlightX = 0.f;
416     }
417
418     if(boxifyEnd)
419     {
420       quad.x = lastSelectionBoxLineInfo.maxX;
421       quad.y = lastSelectionBoxLineInfo.lineOffset;
422       quad.z = visualModel->mControlSize.width;
423       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
424
425       // Boxify at the end of the line.
426       decorator->AddHighlight(actualNumberOfQuads, quad);
427       ++actualNumberOfQuads;
428
429       // Update the size of the highlighted text.
430       maxHighlightX = visualModel->mControlSize.width;
431     }
432   }
433
434   // Set the actual number of quads.
435   decorator->ResizeHighlightQuads(actualNumberOfQuads);
436
437   // Sets the highlight's size and position. In decorator's coords.
438   // The highlight's height has been calculated above (before 'boxifying' the highlight).
439   highLightSize.width = maxHighlightX - minHighlightX;
440
441   highLightPosition.x                               = minHighlightX;
442   const SelectionBoxInfo& firstSelectionBoxLineInfo = *(selectionBoxLinesInfo.Begin());
443   highLightPosition.y                               = firstSelectionBoxLineInfo.lineOffset;
444
445   decorator->SetHighLightBox(highLightPosition, highLightSize, static_cast<float>(model->GetOutlineWidth()));
446
447   if(!decorator->IsSmoothHandlePanEnabled())
448   {
449     CursorInfo primaryCursorInfo;
450     impl.GetCursorPosition(eventData->mLeftSelectionPosition, primaryCursorInfo);
451
452     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + model->mScrollPosition;
453
454     decorator->SetPosition(LEFT_SELECTION_HANDLE,
455                            primaryPosition.x,
456                            primaryCursorInfo.lineOffset + model->mScrollPosition.y,
457                            primaryCursorInfo.lineHeight);
458
459     CursorInfo secondaryCursorInfo;
460     impl.GetCursorPosition(eventData->mRightSelectionPosition, secondaryCursorInfo);
461
462     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + model->mScrollPosition;
463
464     decorator->SetPosition(RIGHT_SELECTION_HANDLE,
465                            secondaryPosition.x,
466                            secondaryCursorInfo.lineOffset + model->mScrollPosition.y,
467                            secondaryCursorInfo.lineHeight);
468   }
469
470   // Set the flag to update the decorator.
471   eventData->mDecoratorUpdated = true;
472 }
473
474 void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action)
475 {
476   EventData*& eventData = impl.mEventData;
477   if(nullptr == eventData)
478   {
479     // Nothing to do if there is no text input.
480     return;
481   }
482
483   if(impl.IsShowingPlaceholderText())
484   {
485     // Nothing to do if there is the place-holder text.
486     return;
487   }
488
489   ModelPtr&       model          = impl.mModel;
490   VisualModelPtr& visualModel    = model->mVisualModel;
491   const Length    numberOfGlyphs = visualModel->mGlyphs.Count();
492   const Length    numberOfLines  = visualModel->mLines.Count();
493   if((0 == numberOfGlyphs) ||
494      (0 == numberOfLines))
495   {
496     // Nothing to do if there is no text.
497     return;
498   }
499
500   // Find which word was selected
501   CharacterIndex selectionStart(0);
502   CharacterIndex selectionEnd(0);
503   CharacterIndex noTextHitIndex(0);
504   const bool     characterHit = FindSelectionIndices(visualModel,
505                                                  model->mLogicalModel,
506                                                  impl.mMetrics,
507                                                  visualX,
508                                                  visualY,
509                                                  selectionStart,
510                                                  selectionEnd,
511                                                  noTextHitIndex);
512   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", &impl, selectionStart, selectionEnd);
513
514   if(characterHit || (Controller::NoTextTap::HIGHLIGHT == action))
515   {
516     uint32_t oldStart = eventData->mLeftSelectionPosition;
517     uint32_t oldEnd   = eventData->mRightSelectionPosition;
518
519     impl.ChangeState(EventData::SELECTING);
520
521     eventData->mLeftSelectionPosition  = selectionStart;
522     eventData->mRightSelectionPosition = selectionEnd;
523
524     eventData->mUpdateLeftSelectionPosition  = true;
525     eventData->mUpdateRightSelectionPosition = true;
526     eventData->mUpdateHighlightBox           = true;
527
528     // It may happen an InputMethodContext commit event arrives before the selection event
529     // if the InputMethodContext is in pre-edit state. The commit event will set the
530     // eventData->mUpdateCursorPosition flag to true. If it's not set back
531     // to false, the highlight box won't be updated.
532     eventData->mUpdateCursorPosition = false;
533
534     eventData->mScrollAfterUpdatePosition = (eventData->mLeftSelectionPosition != eventData->mRightSelectionPosition);
535
536     // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
537     eventData->mPrimaryCursorPosition = std::max(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
538
539     if(impl.mSelectableControlInterface != nullptr)
540     {
541       impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
542     }
543   }
544   else if(Controller::NoTextTap::SHOW_SELECTION_POPUP == action)
545   {
546     // Nothing to select. i.e. a white space, out of bounds
547     impl.ChangeState(EventData::EDITING_WITH_POPUP);
548
549     eventData->mPrimaryCursorPosition = noTextHitIndex;
550
551     eventData->mUpdateCursorPosition      = true;
552     eventData->mUpdateGrabHandlePosition  = true;
553     eventData->mScrollAfterUpdatePosition = true;
554     eventData->mUpdateInputStyle          = true;
555   }
556   else if(Controller::NoTextTap::NO_ACTION == action)
557   {
558     // Nothing to select. i.e. a white space, out of bounds
559     eventData->mPrimaryCursorPosition = noTextHitIndex;
560
561     eventData->mUpdateCursorPosition      = true;
562     eventData->mUpdateGrabHandlePosition  = true;
563     eventData->mScrollAfterUpdatePosition = true;
564     eventData->mUpdateInputStyle          = true;
565   }
566 }
567
568 void SelectionHandleController::Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo)
569 {
570   if((LEFT_SELECTION_HANDLE != handleType) &&
571      (RIGHT_SELECTION_HANDLE != handleType))
572   {
573     return;
574   }
575
576   ModelPtr&     model          = impl.mModel;
577   const Vector2 cursorPosition = cursorInfo.primaryPosition + model->mScrollPosition;
578
579   // Sets the handle's position.
580   EventData*& eventData = impl.mEventData;
581   eventData->mDecorator->SetPosition(handleType,
582                                      cursorPosition.x,
583                                      cursorInfo.lineOffset + model->mScrollPosition.y,
584                                      cursorInfo.lineHeight);
585
586   // If selection handle at start of the text and other at end of the text then all text is selected.
587   const CharacterIndex startOfSelection = std::min(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
588   const CharacterIndex endOfSelection   = std::max(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition);
589   eventData->mAllTextSelected           = (startOfSelection == 0) && (endOfSelection == model->mLogicalModel->mText.Count());
590 }
591
592 } // namespace Text
593
594 } // namespace Toolkit
595
596 } // namespace Dali