2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
26 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
27 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
49 bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
51 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
53 EventData*& eventData = impl.mEventData;
54 if( NULL == eventData )
56 // Nothing to do if there is no text input.
57 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
61 if( eventData->mDecorator )
63 for( std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
64 iter != eventData->mEventQueue.end();
69 case Event::CURSOR_KEY_EVENT:
71 OnCursorKeyEvent(impl, *iter);
74 case Event::TAP_EVENT:
76 OnTapEvent(impl, *iter);
79 case Event::LONG_PRESS_EVENT:
81 OnLongPressEvent(impl, *iter);
84 case Event::PAN_EVENT:
86 OnPanEvent(impl, *iter);
89 case Event::GRAB_HANDLE_EVENT:
90 case Event::LEFT_SELECTION_HANDLE_EVENT:
91 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
93 OnHandleEvent(impl, *iter);
98 OnSelectEvent(impl, *iter);
101 case Event::SELECT_ALL:
103 OnSelectAllEvent(impl);
106 case Event::SELECT_NONE:
108 OnSelectNoneEvent(impl);
115 if( eventData->mUpdateCursorPosition ||
116 eventData->mUpdateHighlightBox )
118 impl.NotifyInputMethodContext();
121 // The cursor must also be repositioned after inserts into the model
122 if( eventData->mUpdateCursorPosition )
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);
129 if(nullptr != impl.mEditableControlInterface)
131 impl.mEditableControlInterface->CaretMoved( eventData->mPrimaryCursorPosition );
134 if( eventData->mUpdateCursorHookPosition )
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;
141 // Scroll first the text after delete ...
142 if( eventData->mScrollAfterDelete )
144 impl.ScrollTextToMatchCursor(cursorInfo);
147 // ... then, text can be scrolled to make the cursor visible.
148 if( eventData->mScrollAfterUpdatePosition )
150 const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
151 impl.ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
153 eventData->mScrollAfterUpdatePosition = false;
154 eventData->mScrollAfterDelete = false;
156 impl.UpdateCursorPosition( cursorInfo );
158 eventData->mDecoratorUpdated = true;
159 eventData->mUpdateCursorPosition = false;
160 eventData->mUpdateGrabHandlePosition = false;
164 CursorInfo leftHandleInfo;
165 CursorInfo rightHandleInfo;
167 if( eventData->mUpdateHighlightBox )
169 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
171 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
173 if( eventData->mScrollAfterUpdatePosition && ( eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition ) )
175 if( eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected )
177 CursorInfo& infoLeft = leftHandleInfo;
179 const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
180 impl.ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
182 CursorInfo& infoRight = rightHandleInfo;
184 const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
185 impl.ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
189 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
191 const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
192 impl. ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
197 if( eventData->mUpdateLeftSelectionPosition )
199 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
201 impl.SetPopupButtons();
202 eventData->mDecoratorUpdated = true;
203 eventData->mUpdateLeftSelectionPosition = false;
206 if( eventData->mUpdateRightSelectionPosition )
208 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
210 impl.SetPopupButtons();
211 eventData->mDecoratorUpdated = true;
212 eventData->mUpdateRightSelectionPosition = false;
215 if( eventData->mUpdateHighlightBox )
217 impl.RepositionSelectionHandles();
219 eventData->mUpdateLeftSelectionPosition = false;
220 eventData->mUpdateRightSelectionPosition = false;
221 eventData->mUpdateHighlightBox = false;
222 eventData->mIsLeftHandleSelected = false;
223 eventData->mIsRightHandleSelected = false;
226 eventData->mScrollAfterUpdatePosition = false;
229 if( eventData->mUpdateInputStyle )
231 // Keep a copy of the current input style.
232 InputStyle currentInputStyle;
233 currentInputStyle.Copy( eventData->mInputStyle );
235 // Set the default style first.
236 impl.RetrieveDefaultInputStyle( eventData->mInputStyle );
238 // Get the character index from the cursor index.
239 const CharacterIndex styleIndex = ( eventData->mPrimaryCursorPosition > 0u ) ? eventData->mPrimaryCursorPosition - 1u : 0u;
241 // Retrieve the style from the style runs stored in the logical model.
242 impl.mModel->mLogicalModel->RetrieveStyle( styleIndex, eventData->mInputStyle );
244 // Compare if the input style has changed.
245 const bool hasInputStyleChanged = !currentInputStyle.Equal( eventData->mInputStyle );
247 if( hasInputStyleChanged )
249 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( eventData->mInputStyle );
250 // Queue the input style changed signal.
251 eventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
254 eventData->mUpdateInputStyle = false;
257 eventData->mEventQueue.clear();
259 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
261 const bool decoratorUpdated = eventData->mDecoratorUpdated;
262 eventData->mDecoratorUpdated = false;
264 return decoratorUpdated;
268 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
270 if( NULL == impl.mEventData || !impl.IsShowingRealText() )
272 // Nothing to do if there is no text input.
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;
283 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
284 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
286 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
288 if( primaryCursorPosition > 0u )
290 if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
292 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
296 primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u );
300 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
302 if( logicalModel->mText.Count() > primaryCursorPosition )
304 if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
306 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
310 primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition );
314 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
316 // Ignore Shift-Up for text selection for now.
318 // Get first the line index of the current cursor position index.
319 CharacterIndex characterIndex = 0u;
321 if( primaryCursorPosition > 0u )
323 characterIndex = primaryCursorPosition - 1u;
326 const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
327 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
329 // Retrieve the cursor position info.
330 CursorInfo cursorInfo;
331 impl.GetCursorPosition( primaryCursorPosition,
334 // Get the line above.
335 const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex );
337 // Get the next hit 'y' point.
338 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
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,
345 eventData.mCursorHookPositionX,
347 CharacterHitTest::TAP,
350 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
352 // Ignore Shift-Down for text selection for now.
354 // Get first the line index of the current cursor position index.
355 CharacterIndex characterIndex = 0u;
357 if( primaryCursorPosition > 0u )
359 characterIndex = primaryCursorPosition - 1u;
362 const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
364 if( lineIndex + 1u < visualModel->mLines.Count() )
366 // Retrieve the cursor position info.
367 CursorInfo cursorInfo;
368 impl.GetCursorPosition( primaryCursorPosition, cursorInfo );
370 // Get the line below.
371 const LineRun& line = *( visualModel->mLines.Begin() + lineIndex + 1u );
373 // Get the next hit 'y' point.
374 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
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,
381 eventData.mCursorHookPositionX,
383 CharacterHitTest::TAP,
388 if ( !isShiftModifier && eventData.mState != EventData::SELECTING )
390 // Update selection position after moving the cursor
391 eventData.mLeftSelectionPosition = primaryCursorPosition;
392 eventData.mRightSelectionPosition = primaryCursorPosition;
395 if ( isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag )
397 // Handle text selection
398 bool selecting = false;
400 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
402 // Shift-Left/Right to select the text
403 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
404 if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary
406 eventData.mRightSelectionPosition += cursorPositionDelta;
410 else if ( eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition )
412 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
418 // Notify the cursor position to the InputMethodContext.
419 if( eventData.mInputMethodContext )
421 eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition );
422 eventData.mInputMethodContext.NotifyCursorPosition();
425 impl.ChangeState( EventData::SELECTING );
427 eventData.mUpdateLeftSelectionPosition = true;
428 eventData.mUpdateRightSelectionPosition = true;
429 eventData.mUpdateGrabHandlePosition = true;
430 eventData.mUpdateHighlightBox = true;
432 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
433 if( eventData.mGrabHandlePopupEnabled )
435 eventData.mDecorator->SetPopupActive( false );
441 // Handle normal cursor move
442 impl.ChangeState( EventData::EDITING );
443 eventData.mUpdateCursorPosition = true;
446 eventData.mUpdateInputStyle = true;
447 eventData.mScrollAfterUpdatePosition = true;
450 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
452 if( impl.mEventData )
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;
462 if( impl.IsShowingRealText() )
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;
468 // Keep the tap 'x' position. Used to move the cursor.
469 eventData.mCursorHookPositionX = xPosition;
471 // Whether to touch point hits on a glyph.
472 bool matchedCharacter = false;
473 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
478 CharacterHitTest::TAP,
481 // When the cursor position is changing, delay cursor blinking
482 eventData.mDecorator->DelayCursorBlink();
486 eventData.mPrimaryCursorPosition = 0u;
489 // Update selection position after tapping
490 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
491 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
493 eventData.mUpdateCursorPosition = true;
494 eventData.mUpdateGrabHandlePosition = true;
495 eventData.mScrollAfterUpdatePosition = true;
496 eventData.mUpdateInputStyle = true;
498 // Notify the cursor position to the InputMethodContext.
499 if( eventData.mInputMethodContext )
501 eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition );
502 eventData.mInputMethodContext.NotifyCursorPosition();
505 else if( 2u == tapCount )
507 if( eventData.mSelectionEnabled )
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;
513 // Calculates the logical position from the x,y coords.
514 impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mDoubleTapAction );
520 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
522 if( impl.mEventData )
524 EventData& eventData = *impl.mEventData;
525 DecoratorPtr& decorator = eventData.mDecorator;
527 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
528 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
530 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
532 // Nothing to do if scrolling is not enabled.
536 const GestureState state = static_cast<GestureState>( event.p1.mInt );
539 case GestureState::STARTED:
541 // Will remove the cursor, handles or text's popup, ...
542 impl.ChangeState( EventData::TEXT_PANNING );
545 case GestureState::CONTINUING:
547 ModelPtr& model = impl.mModel;
549 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
550 Vector2& scrollPosition = model->mScrollPosition;
551 const Vector2 currentScroll = scrollPosition;
553 if( isHorizontalScrollEnabled )
555 const float displacementX = event.p2.mFloat;
556 scrollPosition.x += displacementX;
558 impl.ClampHorizontalScroll( layoutSize );
561 if( isVerticalScrollEnabled )
563 const float displacementY = event.p3.mFloat;
564 scrollPosition.y += displacementY;
566 impl.ClampVerticalScroll( layoutSize );
569 decorator->UpdatePositions( scrollPosition - currentScroll );
572 case GestureState::FINISHED:
573 case GestureState::CANCELLED: // FALLTHROUGH
575 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
576 impl.ChangeState( eventData.mPreviousState );
585 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
587 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
589 if( impl.mEventData )
591 EventData& eventData = *impl.mEventData;
593 if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) )
595 impl.ChangeState( EventData::EDITING_WITH_POPUP );
596 eventData.mDecoratorUpdated = true;
597 eventData.mUpdateInputStyle = true;
601 if( eventData.mSelectionEnabled )
603 ModelPtr& model = impl.mModel;
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;
609 // Calculates the logical position from the x,y coords.
610 impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mLongPressAction );
616 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
618 if( impl.mEventData )
620 const unsigned int state = event.p1.mUint;
621 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
622 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
624 if( HANDLE_PRESSED == state )
626 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
627 } // end ( HANDLE_PRESSED == state )
628 else if( ( HANDLE_RELEASED == state ) ||
629 handleStopScrolling )
631 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
632 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
633 else if( HANDLE_SCROLLING == state )
635 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
636 } // end ( HANDLE_SCROLLING == state )
640 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event )
642 if( impl.mEventData && impl.mEventData->mSelectionEnabled )
644 ModelPtr& model = impl.mModel;
645 const Vector2& scrollPosition = model->mScrollPosition;
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;
651 // Calculates the logical position from the x,y coords.
652 impl.RepositionSelectionHandles( xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT );
656 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
658 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
660 if( impl.mEventData )
662 EventData& eventData = *impl.mEventData;
663 if( eventData.mSelectionEnabled )
665 ModelPtr& model = impl.mModel;
666 const Vector2& scrollPosition = model->mScrollPosition;
668 // Calculates the logical position from the start.
669 impl.RepositionSelectionHandles( 0.f - scrollPosition.x,
670 0.f - scrollPosition.y,
671 Controller::NoTextTap::HIGHLIGHT );
673 eventData.mLeftSelectionPosition = 0u;
674 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
679 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
681 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
683 if( impl.mEventData )
685 EventData& eventData = *impl.mEventData;
686 if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
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;
698 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
700 ModelPtr& model = impl.mModel;
701 const Vector2& scrollPosition = model->mScrollPosition;
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;
707 // Need to calculate the handle's new position.
708 bool matchedCharacter = false;
709 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( model->mVisualModel,
710 model->mLogicalModel,
714 CharacterHitTest::SCROLL,
717 EventData& eventData = *impl.mEventData;
719 if( Event::GRAB_HANDLE_EVENT == event.type )
721 impl.ChangeState ( EventData::GRAB_HANDLE_PANNING );
723 if( handleNewPosition != eventData.mPrimaryCursorPosition )
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;
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;
735 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
737 impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
739 if( ( handleNewPosition != eventData.mLeftSelectionPosition ) &&
740 ( handleNewPosition != eventData.mRightSelectionPosition ) )
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;
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;
752 // Will define the order to scroll the text to match the handle position.
753 eventData.mIsLeftHandleSelected = true;
754 eventData.mIsRightHandleSelected = false;
756 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
758 impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
760 if( ( handleNewPosition != eventData.mRightSelectionPosition ) &&
761 ( handleNewPosition != eventData.mLeftSelectionPosition ) )
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;
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;
773 // Will define the order to scroll the text to match the handle position.
774 eventData.mIsLeftHandleSelected = false;
775 eventData.mIsRightHandleSelected = true;
779 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
781 CharacterIndex handlePosition = 0u;
782 if( handleStopScrolling || isSmoothHandlePanEnabled )
784 ModelPtr& model = impl.mModel;
785 const Vector2& scrollPosition = model->mScrollPosition;
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;
791 bool matchedCharacter = false;
792 handlePosition = Text::GetClosestCursorIndex( model->mVisualModel,
793 model->mLogicalModel,
797 CharacterHitTest::SCROLL,
801 EventData& eventData = *impl.mEventData;
803 if( Event::GRAB_HANDLE_EVENT == event.type )
805 eventData.mUpdateCursorPosition = true;
806 eventData.mUpdateGrabHandlePosition = true;
807 eventData.mUpdateInputStyle = true;
809 if( !impl.IsClipboardEmpty() )
811 impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
814 if( handleStopScrolling || isSmoothHandlePanEnabled )
816 eventData.mScrollAfterUpdatePosition = true;
817 eventData.mPrimaryCursorPosition = handlePosition;
820 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
822 impl.ChangeState( EventData::SELECTING );
824 eventData.mUpdateHighlightBox = true;
825 eventData.mUpdateLeftSelectionPosition = true;
826 eventData.mUpdateRightSelectionPosition = true;
828 if( handleStopScrolling || isSmoothHandlePanEnabled )
830 eventData.mScrollAfterUpdatePosition = true;
832 if( ( handlePosition != eventData.mRightSelectionPosition ) &&
833 ( handlePosition != eventData.mLeftSelectionPosition ) )
835 eventData.mLeftSelectionPosition = handlePosition;
839 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
841 impl.ChangeState( EventData::SELECTING );
843 eventData.mUpdateHighlightBox = true;
844 eventData.mUpdateRightSelectionPosition = true;
845 eventData.mUpdateLeftSelectionPosition = true;
847 if( handleStopScrolling || isSmoothHandlePanEnabled )
849 eventData.mScrollAfterUpdatePosition = true;
850 if( ( handlePosition != eventData.mRightSelectionPosition ) &&
851 ( handlePosition != eventData.mLeftSelectionPosition ) )
853 eventData.mRightSelectionPosition = handlePosition;
858 eventData.mDecoratorUpdated = true;
861 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
863 ModelPtr& model = impl.mModel;
864 Vector2& scrollPosition = model->mScrollPosition;
865 VisualModelPtr& visualModel = model->mVisualModel;
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;
872 scrollPosition.x += xSpeed;
873 scrollPosition.y += ySpeed;
875 impl.ClampHorizontalScroll( layoutSize );
876 impl.ClampVerticalScroll( layoutSize );
878 EventData& eventData = *impl.mEventData;
879 DecoratorPtr& decorator = eventData.mDecorator;
881 bool endOfScroll = false;
882 if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) )
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.
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;
897 if( Event::GRAB_HANDLE_EVENT == event.type )
899 impl.ChangeState( EventData::GRAB_HANDLE_PANNING );
901 // Get the grab handle position in decorator coords.
902 Vector2 position = decorator->GetPosition( GRAB_HANDLE );
904 if( decorator->IsHorizontalScrollEnabled() )
906 // Position the grag handle close to either the left or right edge.
907 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
910 if( decorator->IsVerticalScrollEnabled() )
912 position.x = eventData.mCursorHookPositionX;
914 // Position the grag handle close to either the top or bottom edge.
915 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
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,
924 position.x - scrollPosition.x,
925 position.y - scrollPosition.y,
926 CharacterHitTest::SCROLL,
929 if( eventData.mPrimaryCursorPosition != handlePosition )
931 eventData.mUpdateCursorPosition = true;
932 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
933 eventData.mScrollAfterUpdatePosition = true;
934 eventData.mPrimaryCursorPosition = handlePosition;
936 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
938 // Updates the decorator if the soft handle panning is enabled.
939 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
941 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
943 impl.ChangeState( EventData::SELECTION_HANDLE_PANNING );
945 // Get the selection handle position in decorator coords.
946 Vector2 position = decorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
948 if( decorator->IsHorizontalScrollEnabled() )
950 // Position the selection handle close to either the left or right edge.
951 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
954 if( decorator->IsVerticalScrollEnabled() )
956 position.x = eventData.mCursorHookPositionX;
958 // Position the grag handle close to either the top or bottom edge.
959 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
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,
968 position.x - scrollPosition.x,
969 position.y - scrollPosition.y,
970 CharacterHitTest::SCROLL,
973 if( leftSelectionHandleEvent )
975 const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition );
977 if( differentHandles || endOfScroll )
979 eventData.mUpdateHighlightBox = true;
980 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
981 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
982 eventData.mLeftSelectionPosition = handlePosition;
987 const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition );
988 if( differentHandles || endOfScroll )
990 eventData.mUpdateHighlightBox = true;
991 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
992 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
993 eventData.mRightSelectionPosition = handlePosition;
997 if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition )
999 impl.RepositionSelectionHandles();
1001 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1004 eventData.mDecoratorUpdated = true;
1009 } // namespace Toolkit