Merge "Further refactoring of text-controller-impl" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl-event-handler.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-controller-impl-event-handler.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
27 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
28
29 using namespace Dali;
30
31 namespace
32 {
33
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
36 #endif
37
38 } // namespace
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Text
47 {
48
49 bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
50 {
51   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
52
53   EventData*& eventData = impl.mEventData;
54   if( NULL == eventData )
55   {
56     // Nothing to do if there is no text input.
57     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
58     return false;
59   }
60
61   if( eventData->mDecorator )
62   {
63     for( std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
64          iter != eventData->mEventQueue.end();
65          ++iter )
66     {
67       switch( iter->type )
68       {
69         case Event::CURSOR_KEY_EVENT:
70         {
71           OnCursorKeyEvent(impl, *iter);
72           break;
73         }
74         case Event::TAP_EVENT:
75         {
76           OnTapEvent(impl, *iter);
77           break;
78         }
79         case Event::LONG_PRESS_EVENT:
80         {
81           OnLongPressEvent(impl, *iter);
82           break;
83         }
84         case Event::PAN_EVENT:
85         {
86           OnPanEvent(impl, *iter);
87           break;
88         }
89         case Event::GRAB_HANDLE_EVENT:
90         case Event::LEFT_SELECTION_HANDLE_EVENT:
91         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
92         {
93           OnHandleEvent(impl, *iter);
94           break;
95         }
96         case Event::SELECT:
97         {
98           OnSelectEvent(impl, *iter);
99           break;
100         }
101         case Event::SELECT_ALL:
102         {
103           OnSelectAllEvent(impl);
104           break;
105         }
106         case Event::SELECT_NONE:
107         {
108           OnSelectNoneEvent(impl);
109           break;
110         }
111       }
112     }
113   }
114
115   if( eventData->mUpdateCursorPosition ||
116       eventData->mUpdateHighlightBox )
117   {
118     impl.NotifyInputMethodContext();
119   }
120
121   // The cursor must also be repositioned after inserts into the model
122   if( eventData->mUpdateCursorPosition )
123   {
124     // Updates the cursor position and scrolls the text to make it visible.
125     CursorInfo cursorInfo;
126     // Calculate the cursor position from the new cursor index.
127     impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
128
129     if(nullptr != impl.mEditableControlInterface)
130     {
131       impl.mEditableControlInterface->CaretMoved( eventData->mPrimaryCursorPosition );
132     }
133
134     if( eventData->mUpdateCursorHookPosition )
135     {
136       // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
137       eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
138       eventData->mUpdateCursorHookPosition = false;
139     }
140
141     // Scroll first the text after delete ...
142     if( eventData->mScrollAfterDelete )
143     {
144       impl.ScrollTextToMatchCursor(cursorInfo);
145     }
146
147     // ... then, text can be scrolled to make the cursor visible.
148     if( eventData->mScrollAfterUpdatePosition )
149     {
150       const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
151       impl.ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
152     }
153     eventData->mScrollAfterUpdatePosition = false;
154     eventData->mScrollAfterDelete = false;
155
156     impl.UpdateCursorPosition( cursorInfo );
157
158     eventData->mDecoratorUpdated = true;
159     eventData->mUpdateCursorPosition = false;
160     eventData->mUpdateGrabHandlePosition = false;
161   }
162   else
163   {
164     CursorInfo leftHandleInfo;
165     CursorInfo rightHandleInfo;
166
167     if( eventData->mUpdateHighlightBox )
168     {
169       impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
170
171       impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
172
173       if( eventData->mScrollAfterUpdatePosition && ( eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition ) )
174       {
175         if( eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected )
176         {
177           CursorInfo& infoLeft = leftHandleInfo;
178
179           const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
180           impl.ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
181
182           CursorInfo& infoRight = rightHandleInfo;
183
184           const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
185           impl.ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
186         }
187         else
188         {
189           CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
190
191           const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
192           impl. ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
193         }
194       }
195     }
196
197     if( eventData->mUpdateLeftSelectionPosition )
198     {
199       impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
200
201       impl.SetPopupButtons();
202       eventData->mDecoratorUpdated = true;
203       eventData->mUpdateLeftSelectionPosition = false;
204     }
205
206     if( eventData->mUpdateRightSelectionPosition )
207     {
208       impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
209
210       impl.SetPopupButtons();
211       eventData->mDecoratorUpdated = true;
212       eventData->mUpdateRightSelectionPosition = false;
213     }
214
215     if( eventData->mUpdateHighlightBox )
216     {
217       impl.RepositionSelectionHandles();
218
219       eventData->mUpdateLeftSelectionPosition = false;
220       eventData->mUpdateRightSelectionPosition = false;
221       eventData->mUpdateHighlightBox = false;
222       eventData->mIsLeftHandleSelected = false;
223       eventData->mIsRightHandleSelected = false;
224     }
225
226     eventData->mScrollAfterUpdatePosition = false;
227   }
228
229   if( eventData->mUpdateInputStyle )
230   {
231     // Keep a copy of the current input style.
232     InputStyle currentInputStyle;
233     currentInputStyle.Copy( eventData->mInputStyle );
234
235     // Set the default style first.
236     impl.RetrieveDefaultInputStyle( eventData->mInputStyle );
237
238     // Get the character index from the cursor index.
239     const CharacterIndex styleIndex = ( eventData->mPrimaryCursorPosition > 0u ) ? eventData->mPrimaryCursorPosition - 1u : 0u;
240
241     // Retrieve the style from the style runs stored in the logical model.
242     impl.mModel->mLogicalModel->RetrieveStyle( styleIndex, eventData->mInputStyle );
243
244     // Compare if the input style has changed.
245     const bool hasInputStyleChanged = !currentInputStyle.Equal( eventData->mInputStyle );
246
247     if( hasInputStyleChanged )
248     {
249       const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( eventData->mInputStyle );
250       // Queue the input style changed signal.
251       eventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
252     }
253
254     eventData->mUpdateInputStyle = false;
255   }
256
257   eventData->mEventQueue.clear();
258
259   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
260
261   const bool decoratorUpdated = eventData->mDecoratorUpdated;
262   eventData->mDecoratorUpdated = false;
263
264   return decoratorUpdated;
265 }
266
267
268 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
269 {
270   if( NULL == impl.mEventData || !impl.IsShowingRealText() )
271   {
272     // Nothing to do if there is no text input.
273     return;
274   }
275
276   int keyCode = event.p1.mInt;
277   bool isShiftModifier = event.p2.mBool;
278   EventData& eventData = *impl.mEventData;
279   ModelPtr& model = impl.mModel;
280   LogicalModelPtr& logicalModel = model->mLogicalModel;
281   VisualModelPtr& visualModel = model->mVisualModel;
282
283   CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
284   CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
285
286   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
287   {
288     if( primaryCursorPosition > 0u )
289     {
290       if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
291       {
292         primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
293       }
294       else
295       {
296         primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u );
297       }
298     }
299   }
300   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
301   {
302     if( logicalModel->mText.Count() > primaryCursorPosition )
303     {
304       if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
305       {
306         primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
307       }
308       else
309       {
310         primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition );
311       }
312     }
313   }
314   else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
315   {
316     // Ignore Shift-Up for text selection for now.
317
318     // Get first the line index of the current cursor position index.
319     CharacterIndex characterIndex = 0u;
320
321     if( primaryCursorPosition > 0u )
322     {
323       characterIndex = primaryCursorPosition - 1u;
324     }
325
326     const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
327     const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
328
329     // Retrieve the cursor position info.
330     CursorInfo cursorInfo;
331     impl.GetCursorPosition( primaryCursorPosition,
332                             cursorInfo );
333
334     // Get the line above.
335     const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex );
336
337     // Get the next hit 'y' point.
338     const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
339
340     // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
341     bool matchedCharacter = false;
342     primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
343                                                          logicalModel,
344                                                          impl.mMetrics,
345                                                          eventData.mCursorHookPositionX,
346                                                          hitPointY,
347                                                          CharacterHitTest::TAP,
348                                                          matchedCharacter );
349   }
350   else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
351   {
352     // Ignore Shift-Down for text selection for now.
353
354     // Get first the line index of the current cursor position index.
355     CharacterIndex characterIndex = 0u;
356
357     if( primaryCursorPosition > 0u )
358     {
359       characterIndex = primaryCursorPosition - 1u;
360     }
361
362     const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
363
364     if( lineIndex + 1u < visualModel->mLines.Count() )
365     {
366       // Retrieve the cursor position info.
367       CursorInfo cursorInfo;
368       impl.GetCursorPosition( primaryCursorPosition, cursorInfo );
369
370       // Get the line below.
371       const LineRun& line = *( visualModel->mLines.Begin() + lineIndex + 1u );
372
373       // Get the next hit 'y' point.
374       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
375
376       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
377       bool matchedCharacter = false;
378       primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
379                                                            logicalModel,
380                                                            impl.mMetrics,
381                                                            eventData.mCursorHookPositionX,
382                                                            hitPointY,
383                                                            CharacterHitTest::TAP,
384                                                            matchedCharacter );
385     }
386   }
387
388   if ( !isShiftModifier && eventData.mState != EventData::SELECTING )
389   {
390     // Update selection position after moving the cursor
391     eventData.mLeftSelectionPosition = primaryCursorPosition;
392     eventData.mRightSelectionPosition = primaryCursorPosition;
393   }
394
395   if ( isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag )
396   {
397     // Handle text selection
398     bool selecting = false;
399
400     if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
401     {
402       // Shift-Left/Right to select the text
403       int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
404       if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary
405       {
406         eventData.mRightSelectionPosition += cursorPositionDelta;
407       }
408       selecting = true;
409     }
410     else if ( eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition )
411     {
412       // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
413       selecting = true;
414     }
415
416     if ( selecting )
417     {
418       // Notify the cursor position to the InputMethodContext.
419       if( eventData.mInputMethodContext )
420       {
421         eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition );
422         eventData.mInputMethodContext.NotifyCursorPosition();
423       }
424
425       impl.ChangeState( EventData::SELECTING );
426
427       eventData.mUpdateLeftSelectionPosition = true;
428       eventData.mUpdateRightSelectionPosition = true;
429       eventData.mUpdateGrabHandlePosition = true;
430       eventData.mUpdateHighlightBox = true;
431
432       // Hide the text selection popup if select the text using keyboard instead of moving grab handles
433       if( eventData.mGrabHandlePopupEnabled )
434       {
435         eventData.mDecorator->SetPopupActive( false );
436       }
437     }
438   }
439   else
440   {
441     // Handle normal cursor move
442     impl.ChangeState( EventData::EDITING );
443     eventData.mUpdateCursorPosition = true;
444   }
445
446   eventData.mUpdateInputStyle = true;
447   eventData.mScrollAfterUpdatePosition = true;
448 }
449
450 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
451 {
452   if( impl.mEventData )
453   {
454     const unsigned int tapCount = event.p1.mUint;
455     EventData& eventData = *impl.mEventData;
456     ModelPtr& model = impl.mModel;
457     LogicalModelPtr& logicalModel = model->mLogicalModel;
458     VisualModelPtr& visualModel = model->mVisualModel;
459
460     if( 1u == tapCount )
461     {
462       if( impl.IsShowingRealText() )
463       {
464         // Convert from control's coords to text's coords.
465         const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
466         const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
467
468         // Keep the tap 'x' position. Used to move the cursor.
469         eventData.mCursorHookPositionX = xPosition;
470
471         // Whether to touch point hits on a glyph.
472         bool matchedCharacter = false;
473         eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
474                                                                         logicalModel,
475                                                                         impl.mMetrics,
476                                                                         xPosition,
477                                                                         yPosition,
478                                                                         CharacterHitTest::TAP,
479                                                                         matchedCharacter );
480
481         // When the cursor position is changing, delay cursor blinking
482         eventData.mDecorator->DelayCursorBlink();
483       }
484       else
485       {
486         eventData.mPrimaryCursorPosition = 0u;
487       }
488
489       // Update selection position after tapping
490       eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
491       eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
492
493       eventData.mUpdateCursorPosition = true;
494       eventData.mUpdateGrabHandlePosition = true;
495       eventData.mScrollAfterUpdatePosition = true;
496       eventData.mUpdateInputStyle = true;
497
498       // Notify the cursor position to the InputMethodContext.
499       if( eventData.mInputMethodContext )
500       {
501         eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition );
502         eventData.mInputMethodContext.NotifyCursorPosition();
503       }
504     }
505     else if( 2u == tapCount )
506     {
507       if( eventData.mSelectionEnabled )
508       {
509         // Convert from control's coords to text's coords.
510         const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
511         const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
512
513         // Calculates the logical position from the x,y coords.
514         impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mDoubleTapAction );
515       }
516     }
517   }
518 }
519
520 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
521 {
522   if( impl.mEventData )
523   {
524     EventData& eventData = *impl.mEventData;
525     DecoratorPtr& decorator = eventData.mDecorator;
526
527     const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
528     const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
529
530     if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
531     {
532       // Nothing to do if scrolling is not enabled.
533       return;
534     }
535
536     const GestureState state = static_cast<GestureState>( event.p1.mInt );
537     switch( state )
538     {
539       case GestureState::STARTED:
540       {
541         // Will remove the cursor, handles or text's popup, ...
542         impl.ChangeState( EventData::TEXT_PANNING );
543         break;
544       }
545       case GestureState::CONTINUING:
546       {
547         ModelPtr& model = impl.mModel;
548
549         const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
550         Vector2& scrollPosition = model->mScrollPosition;
551         const Vector2 currentScroll = scrollPosition;
552
553         if( isHorizontalScrollEnabled )
554         {
555           const float displacementX = event.p2.mFloat;
556           scrollPosition.x += displacementX;
557
558           impl.ClampHorizontalScroll( layoutSize );
559         }
560
561         if( isVerticalScrollEnabled )
562         {
563           const float displacementY = event.p3.mFloat;
564           scrollPosition.y += displacementY;
565
566           impl.ClampVerticalScroll( layoutSize );
567         }
568
569         decorator->UpdatePositions( scrollPosition - currentScroll );
570         break;
571       }
572       case GestureState::FINISHED:
573       case GestureState::CANCELLED: // FALLTHROUGH
574       {
575         // Will go back to the previous state to show the cursor, handles, the text's popup, ...
576         impl.ChangeState( eventData.mPreviousState );
577         break;
578       }
579       default:
580         break;
581     }
582   }
583 }
584
585 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
586 {
587   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
588
589   if( impl.mEventData )
590   {
591     EventData& eventData = *impl.mEventData;
592
593     if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) )
594     {
595       impl.ChangeState( EventData::EDITING_WITH_POPUP );
596       eventData.mDecoratorUpdated = true;
597       eventData.mUpdateInputStyle = true;
598     }
599     else
600     {
601       if( eventData.mSelectionEnabled )
602       {
603         ModelPtr& model = impl.mModel;
604
605         // Convert from control's coords to text's coords.
606         const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
607         const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
608
609         // Calculates the logical position from the x,y coords.
610         impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mLongPressAction );
611       }
612     }
613   }
614 }
615
616 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
617 {
618   if( impl.mEventData )
619   {
620     const unsigned int state = event.p1.mUint;
621     const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
622     const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
623
624     if( HANDLE_PRESSED == state )
625     {
626       OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
627     } // end ( HANDLE_PRESSED == state )
628     else if( ( HANDLE_RELEASED == state ) ||
629              handleStopScrolling )
630     {
631       OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
632     } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
633     else if( HANDLE_SCROLLING == state )
634     {
635       OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
636     } // end ( HANDLE_SCROLLING == state )
637   }
638 }
639
640 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event )
641 {
642   if( impl.mEventData && impl.mEventData->mSelectionEnabled )
643   {
644     ModelPtr& model = impl.mModel;
645     const Vector2& scrollPosition = model->mScrollPosition;
646
647     // Convert from control's coords to text's coords.
648     const float xPosition = event.p2.mFloat - scrollPosition.x;
649     const float yPosition = event.p3.mFloat - scrollPosition.y;
650
651     // Calculates the logical position from the x,y coords.
652     impl.RepositionSelectionHandles( xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT );
653   }
654 }
655
656 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
657 {
658   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
659
660   if( impl.mEventData )
661   {
662     EventData& eventData = *impl.mEventData;
663     if( eventData.mSelectionEnabled )
664     {
665       ModelPtr& model = impl.mModel;
666       const Vector2& scrollPosition = model->mScrollPosition;
667
668       // Calculates the logical position from the start.
669       impl.RepositionSelectionHandles( 0.f - scrollPosition.x,
670                                        0.f - scrollPosition.y,
671                                        Controller::NoTextTap::HIGHLIGHT );
672
673       eventData.mLeftSelectionPosition = 0u;
674       eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
675     }
676   }
677 }
678
679 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
680 {
681   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
682
683   if( impl.mEventData )
684   {
685     EventData& eventData = *impl.mEventData;
686     if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
687     {
688       eventData.mPrimaryCursorPosition = 0u;
689       eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
690       impl.ChangeState( EventData::INACTIVE );
691       eventData.mUpdateCursorPosition = true;
692       eventData.mUpdateInputStyle = true;
693       eventData.mScrollAfterUpdatePosition = true;
694     }
695   }
696 }
697
698 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
699 {
700   ModelPtr& model = impl.mModel;
701   const Vector2& scrollPosition = model->mScrollPosition;
702
703   // Convert from decorator's coords to text's coords.
704   const float xPosition = event.p2.mFloat - scrollPosition.x;
705   const float yPosition = event.p3.mFloat - scrollPosition.y;
706
707   // Need to calculate the handle's new position.
708   bool matchedCharacter = false;
709   const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( model->mVisualModel,
710                                                                         model->mLogicalModel,
711                                                                         impl.mMetrics,
712                                                                         xPosition,
713                                                                         yPosition,
714                                                                         CharacterHitTest::SCROLL,
715                                                                         matchedCharacter );
716
717   EventData& eventData = *impl.mEventData;
718
719   if( Event::GRAB_HANDLE_EVENT == event.type )
720   {
721     impl.ChangeState ( EventData::GRAB_HANDLE_PANNING );
722
723     if( handleNewPosition != eventData.mPrimaryCursorPosition )
724     {
725       // Updates the cursor position if the handle's new position is different than the current one.
726       eventData.mUpdateCursorPosition = true;
727       // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
728       eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
729       eventData.mPrimaryCursorPosition = handleNewPosition;
730     }
731
732     // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
733     eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
734   }
735   else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
736   {
737     impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
738
739     if( ( handleNewPosition != eventData.mLeftSelectionPosition ) &&
740         ( handleNewPosition != eventData.mRightSelectionPosition ) )
741     {
742       // Updates the highlight box if the handle's new position is different than the current one.
743       eventData.mUpdateHighlightBox = true;
744       // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
745       eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
746       eventData.mLeftSelectionPosition = handleNewPosition;
747     }
748
749     // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
750     eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
751
752     // Will define the order to scroll the text to match the handle position.
753     eventData.mIsLeftHandleSelected = true;
754     eventData.mIsRightHandleSelected = false;
755   }
756   else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
757   {
758     impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
759
760     if( ( handleNewPosition != eventData.mRightSelectionPosition ) &&
761         ( handleNewPosition != eventData.mLeftSelectionPosition ) )
762     {
763       // Updates the highlight box if the handle's new position is different than the current one.
764       eventData.mUpdateHighlightBox = true;
765       // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
766       eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
767       eventData.mRightSelectionPosition = handleNewPosition;
768     }
769
770     // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
771     eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
772
773     // Will define the order to scroll the text to match the handle position.
774     eventData.mIsLeftHandleSelected = false;
775     eventData.mIsRightHandleSelected = true;
776   }
777 }
778
779 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
780 {
781   CharacterIndex handlePosition = 0u;
782   if( handleStopScrolling || isSmoothHandlePanEnabled )
783   {
784     ModelPtr& model = impl.mModel;
785     const Vector2& scrollPosition = model->mScrollPosition;
786
787     // Convert from decorator's coords to text's coords.
788     const float xPosition = event.p2.mFloat - scrollPosition.x;
789     const float yPosition = event.p3.mFloat - scrollPosition.y;
790
791     bool matchedCharacter = false;
792     handlePosition = Text::GetClosestCursorIndex( model->mVisualModel,
793                                                   model->mLogicalModel,
794                                                   impl.mMetrics,
795                                                   xPosition,
796                                                   yPosition,
797                                                   CharacterHitTest::SCROLL,
798                                                   matchedCharacter );
799   }
800
801   EventData& eventData = *impl.mEventData;
802
803   if( Event::GRAB_HANDLE_EVENT == event.type )
804   {
805     eventData.mUpdateCursorPosition = true;
806     eventData.mUpdateGrabHandlePosition = true;
807     eventData.mUpdateInputStyle = true;
808
809     if( !impl.IsClipboardEmpty() )
810     {
811       impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
812     }
813
814     if( handleStopScrolling || isSmoothHandlePanEnabled )
815     {
816       eventData.mScrollAfterUpdatePosition = true;
817       eventData.mPrimaryCursorPosition = handlePosition;
818     }
819   }
820   else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
821   {
822     impl.ChangeState( EventData::SELECTING );
823
824     eventData.mUpdateHighlightBox = true;
825     eventData.mUpdateLeftSelectionPosition = true;
826     eventData.mUpdateRightSelectionPosition = true;
827
828     if( handleStopScrolling || isSmoothHandlePanEnabled )
829     {
830       eventData.mScrollAfterUpdatePosition = true;
831
832       if( ( handlePosition != eventData.mRightSelectionPosition ) &&
833           ( handlePosition != eventData.mLeftSelectionPosition ) )
834       {
835         eventData.mLeftSelectionPosition = handlePosition;
836       }
837     }
838   }
839   else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
840   {
841     impl.ChangeState( EventData::SELECTING );
842
843     eventData.mUpdateHighlightBox = true;
844     eventData.mUpdateRightSelectionPosition = true;
845     eventData.mUpdateLeftSelectionPosition = true;
846
847     if( handleStopScrolling || isSmoothHandlePanEnabled )
848     {
849       eventData.mScrollAfterUpdatePosition = true;
850       if( ( handlePosition != eventData.mRightSelectionPosition ) &&
851           ( handlePosition != eventData.mLeftSelectionPosition ) )
852       {
853         eventData.mRightSelectionPosition = handlePosition;
854       }
855     }
856   }
857
858   eventData.mDecoratorUpdated = true;
859 }
860
861 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
862 {
863   ModelPtr& model = impl.mModel;
864   Vector2& scrollPosition = model->mScrollPosition;
865   VisualModelPtr& visualModel = model->mVisualModel;
866
867   const float xSpeed = event.p2.mFloat;
868   const float ySpeed = event.p3.mFloat;
869   const Vector2& layoutSize = visualModel->GetLayoutSize();
870   const Vector2 currentScrollPosition = scrollPosition;
871
872   scrollPosition.x += xSpeed;
873   scrollPosition.y += ySpeed;
874
875   impl.ClampHorizontalScroll( layoutSize );
876   impl.ClampVerticalScroll( layoutSize );
877
878   EventData& eventData = *impl.mEventData;
879   DecoratorPtr& decorator = eventData.mDecorator;
880
881   bool endOfScroll = false;
882   if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) )
883   {
884     // Notify the decorator there is no more text to scroll.
885     // The decorator won't send more scroll events.
886     decorator->NotifyEndOfScroll();
887     // Still need to set the position of the handle.
888     endOfScroll = true;
889   }
890
891   // Set the position of the handle.
892   const bool scrollRightDirection = xSpeed > 0.f;
893   const bool scrollBottomDirection = ySpeed > 0.f;
894   const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
895   const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
896
897   if( Event::GRAB_HANDLE_EVENT == event.type )
898   {
899     impl.ChangeState( EventData::GRAB_HANDLE_PANNING );
900
901     // Get the grab handle position in decorator coords.
902     Vector2 position = decorator->GetPosition( GRAB_HANDLE );
903
904     if( decorator->IsHorizontalScrollEnabled() )
905     {
906       // Position the grag handle close to either the left or right edge.
907       position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
908     }
909
910     if( decorator->IsVerticalScrollEnabled() )
911     {
912       position.x = eventData.mCursorHookPositionX;
913
914       // Position the grag handle close to either the top or bottom edge.
915       position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
916     }
917
918     // Get the new handle position.
919     // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
920     bool matchedCharacter = false;
921     const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
922                                                                        impl.mModel->mLogicalModel,
923                                                                        impl.mMetrics,
924                                                                        position.x - scrollPosition.x,
925                                                                        position.y - scrollPosition.y,
926                                                                        CharacterHitTest::SCROLL,
927                                                                        matchedCharacter );
928
929     if( eventData.mPrimaryCursorPosition != handlePosition )
930     {
931       eventData.mUpdateCursorPosition = true;
932       eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
933       eventData.mScrollAfterUpdatePosition = true;
934       eventData.mPrimaryCursorPosition = handlePosition;
935     }
936     eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
937
938     // Updates the decorator if the soft handle panning is enabled.
939     eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
940   }
941   else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
942   {
943     impl.ChangeState( EventData::SELECTION_HANDLE_PANNING );
944
945     // Get the selection handle position in decorator coords.
946     Vector2 position = decorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
947
948     if( decorator->IsHorizontalScrollEnabled() )
949     {
950       // Position the selection handle close to either the left or right edge.
951       position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
952     }
953
954     if( decorator->IsVerticalScrollEnabled() )
955     {
956       position.x = eventData.mCursorHookPositionX;
957
958       // Position the grag handle close to either the top or bottom edge.
959       position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
960     }
961
962     // Get the new handle position.
963     // The selection handle's position is in decorator's coords. Need to transform to text's coords.
964     bool matchedCharacter = false;
965     const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
966                                                                        impl.mModel->mLogicalModel,
967                                                                        impl.mMetrics,
968                                                                        position.x - scrollPosition.x,
969                                                                        position.y - scrollPosition.y,
970                                                                        CharacterHitTest::SCROLL,
971                                                                        matchedCharacter );
972
973     if( leftSelectionHandleEvent )
974     {
975       const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition );
976
977       if( differentHandles || endOfScroll )
978       {
979         eventData.mUpdateHighlightBox = true;
980         eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
981         eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
982         eventData.mLeftSelectionPosition = handlePosition;
983       }
984     }
985     else
986     {
987       const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition );
988       if( differentHandles || endOfScroll )
989       {
990         eventData.mUpdateHighlightBox = true;
991         eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
992         eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
993         eventData.mRightSelectionPosition = handlePosition;
994       }
995     }
996
997     if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition )
998     {
999       impl.RepositionSelectionHandles();
1000
1001       eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1002     }
1003   }
1004   eventData.mDecoratorUpdated = true;
1005 }
1006
1007 } // namespace Text
1008
1009 } // namespace Toolkit
1010
1011 } // namespace Dali