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>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
48 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
50 if( NULL == impl.mEventData || !impl.IsShowingRealText() )
52 // Nothing to do if there is no text input.
56 int keyCode = event.p1.mInt;
57 bool isShiftModifier = event.p2.mBool;
58 EventData& eventData = *impl.mEventData;
59 ModelPtr& model = impl.mModel;
60 LogicalModelPtr& logicalModel = model->mLogicalModel;
61 VisualModelPtr& visualModel = model->mVisualModel;
63 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
64 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
66 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
68 if( primaryCursorPosition > 0u )
70 if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
72 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
76 primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u );
80 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
82 if( logicalModel->mText.Count() > primaryCursorPosition )
84 if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() )
86 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
90 primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition );
94 else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
96 // Ignore Shift-Up for text selection for now.
98 // Get first the line index of the current cursor position index.
99 CharacterIndex characterIndex = 0u;
101 if( primaryCursorPosition > 0u )
103 characterIndex = primaryCursorPosition - 1u;
106 const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
107 const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
109 // Retrieve the cursor position info.
110 CursorInfo cursorInfo;
111 impl.GetCursorPosition( primaryCursorPosition,
114 // Get the line above.
115 const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex );
117 // Get the next hit 'y' point.
118 const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
120 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
121 bool matchedCharacter = false;
122 primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
125 eventData.mCursorHookPositionX,
127 CharacterHitTest::TAP,
130 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier )
132 // Ignore Shift-Down for text selection for now.
134 // Get first the line index of the current cursor position index.
135 CharacterIndex characterIndex = 0u;
137 if( primaryCursorPosition > 0u )
139 characterIndex = primaryCursorPosition - 1u;
142 const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex );
144 if( lineIndex + 1u < visualModel->mLines.Count() )
146 // Retrieve the cursor position info.
147 CursorInfo cursorInfo;
148 impl.GetCursorPosition( primaryCursorPosition, cursorInfo );
150 // Get the line below.
151 const LineRun& line = *( visualModel->mLines.Begin() + lineIndex + 1u );
153 // Get the next hit 'y' point.
154 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
156 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
157 bool matchedCharacter = false;
158 primaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
161 eventData.mCursorHookPositionX,
163 CharacterHitTest::TAP,
168 if ( !isShiftModifier && eventData.mState != EventData::SELECTING )
170 // Update selection position after moving the cursor
171 eventData.mLeftSelectionPosition = primaryCursorPosition;
172 eventData.mRightSelectionPosition = primaryCursorPosition;
175 if ( isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag )
177 // Handle text selection
178 bool selecting = false;
180 if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
182 // Shift-Left/Right to select the text
183 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
184 if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary
186 eventData.mRightSelectionPosition += cursorPositionDelta;
190 else if ( eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition )
192 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
198 // Notify the cursor position to the InputMethodContext.
199 if( eventData.mInputMethodContext )
201 eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition );
202 eventData.mInputMethodContext.NotifyCursorPosition();
205 impl.ChangeState( EventData::SELECTING );
207 eventData.mUpdateLeftSelectionPosition = true;
208 eventData.mUpdateRightSelectionPosition = true;
209 eventData.mUpdateGrabHandlePosition = true;
210 eventData.mUpdateHighlightBox = true;
212 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
213 if( eventData.mGrabHandlePopupEnabled )
215 eventData.mDecorator->SetPopupActive( false );
221 // Handle normal cursor move
222 impl.ChangeState( EventData::EDITING );
223 eventData.mUpdateCursorPosition = true;
226 eventData.mUpdateInputStyle = true;
227 eventData.mScrollAfterUpdatePosition = true;
230 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
232 if( impl.mEventData )
234 const unsigned int tapCount = event.p1.mUint;
235 EventData& eventData = *impl.mEventData;
236 ModelPtr& model = impl.mModel;
237 LogicalModelPtr& logicalModel = model->mLogicalModel;
238 VisualModelPtr& visualModel = model->mVisualModel;
242 if( impl.IsShowingRealText() )
244 // Convert from control's coords to text's coords.
245 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
246 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
248 // Keep the tap 'x' position. Used to move the cursor.
249 eventData.mCursorHookPositionX = xPosition;
251 // Whether to touch point hits on a glyph.
252 bool matchedCharacter = false;
253 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex( visualModel,
258 CharacterHitTest::TAP,
261 // When the cursor position is changing, delay cursor blinking
262 eventData.mDecorator->DelayCursorBlink();
266 eventData.mPrimaryCursorPosition = 0u;
269 // Update selection position after tapping
270 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
271 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
273 eventData.mUpdateCursorPosition = true;
274 eventData.mUpdateGrabHandlePosition = true;
275 eventData.mScrollAfterUpdatePosition = true;
276 eventData.mUpdateInputStyle = true;
278 // Notify the cursor position to the InputMethodContext.
279 if( eventData.mInputMethodContext )
281 eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition );
282 eventData.mInputMethodContext.NotifyCursorPosition();
285 else if( 2u == tapCount )
287 if( eventData.mSelectionEnabled )
289 // Convert from control's coords to text's coords.
290 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
291 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
293 // Calculates the logical position from the x,y coords.
294 impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mDoubleTapAction );
300 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
302 if( impl.mEventData )
304 EventData& eventData = *impl.mEventData;
305 DecoratorPtr& decorator = eventData.mDecorator;
307 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
308 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
310 if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
312 // Nothing to do if scrolling is not enabled.
316 const GestureState state = static_cast<GestureState>( event.p1.mInt );
319 case GestureState::STARTED:
321 // Will remove the cursor, handles or text's popup, ...
322 impl.ChangeState( EventData::TEXT_PANNING );
325 case GestureState::CONTINUING:
327 ModelPtr& model = impl.mModel;
329 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
330 Vector2& scrollPosition = model->mScrollPosition;
331 const Vector2 currentScroll = scrollPosition;
333 if( isHorizontalScrollEnabled )
335 const float displacementX = event.p2.mFloat;
336 scrollPosition.x += displacementX;
338 impl.ClampHorizontalScroll( layoutSize );
341 if( isVerticalScrollEnabled )
343 const float displacementY = event.p3.mFloat;
344 scrollPosition.y += displacementY;
346 impl.ClampVerticalScroll( layoutSize );
349 decorator->UpdatePositions( scrollPosition - currentScroll );
352 case GestureState::FINISHED:
353 case GestureState::CANCELLED: // FALLTHROUGH
355 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
356 impl.ChangeState( eventData.mPreviousState );
365 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
367 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
369 if( impl.mEventData )
371 EventData& eventData = *impl.mEventData;
373 if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) )
375 impl.ChangeState( EventData::EDITING_WITH_POPUP );
376 eventData.mDecoratorUpdated = true;
377 eventData.mUpdateInputStyle = true;
381 if( eventData.mSelectionEnabled )
383 ModelPtr& model = impl.mModel;
385 // Convert from control's coords to text's coords.
386 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
387 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
389 // Calculates the logical position from the x,y coords.
390 impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mLongPressAction );
396 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
398 if( impl.mEventData )
400 const unsigned int state = event.p1.mUint;
401 const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
402 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
404 if( HANDLE_PRESSED == state )
406 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
407 } // end ( HANDLE_PRESSED == state )
408 else if( ( HANDLE_RELEASED == state ) ||
409 handleStopScrolling )
411 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
412 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
413 else if( HANDLE_SCROLLING == state )
415 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
416 } // end ( HANDLE_SCROLLING == state )
420 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event )
422 if( impl.mEventData && impl.mEventData->mSelectionEnabled )
424 ModelPtr& model = impl.mModel;
425 const Vector2& scrollPosition = model->mScrollPosition;
427 // Convert from control's coords to text's coords.
428 const float xPosition = event.p2.mFloat - scrollPosition.x;
429 const float yPosition = event.p3.mFloat - scrollPosition.y;
431 // Calculates the logical position from the x,y coords.
432 impl.RepositionSelectionHandles( xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT );
436 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
438 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
440 if( impl.mEventData )
442 EventData& eventData = *impl.mEventData;
443 if( eventData.mSelectionEnabled )
445 ModelPtr& model = impl.mModel;
446 const Vector2& scrollPosition = model->mScrollPosition;
448 // Calculates the logical position from the start.
449 impl.RepositionSelectionHandles( 0.f - scrollPosition.x,
450 0.f - scrollPosition.y,
451 Controller::NoTextTap::HIGHLIGHT );
453 eventData.mLeftSelectionPosition = 0u;
454 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
459 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
461 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false");
463 if( impl.mEventData )
465 EventData& eventData = *impl.mEventData;
466 if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
468 eventData.mPrimaryCursorPosition = 0u;
469 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
470 impl.ChangeState( EventData::INACTIVE );
471 eventData.mUpdateCursorPosition = true;
472 eventData.mUpdateInputStyle = true;
473 eventData.mScrollAfterUpdatePosition = true;
478 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
480 ModelPtr& model = impl.mModel;
481 const Vector2& scrollPosition = model->mScrollPosition;
483 // Convert from decorator's coords to text's coords.
484 const float xPosition = event.p2.mFloat - scrollPosition.x;
485 const float yPosition = event.p3.mFloat - scrollPosition.y;
487 // Need to calculate the handle's new position.
488 bool matchedCharacter = false;
489 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( model->mVisualModel,
490 model->mLogicalModel,
494 CharacterHitTest::SCROLL,
497 EventData& eventData = *impl.mEventData;
499 if( Event::GRAB_HANDLE_EVENT == event.type )
501 impl.ChangeState ( EventData::GRAB_HANDLE_PANNING );
503 if( handleNewPosition != eventData.mPrimaryCursorPosition )
505 // Updates the cursor position if the handle's new position is different than the current one.
506 eventData.mUpdateCursorPosition = true;
507 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
508 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
509 eventData.mPrimaryCursorPosition = handleNewPosition;
512 // 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.
513 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
515 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
517 impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
519 if( ( handleNewPosition != eventData.mLeftSelectionPosition ) &&
520 ( handleNewPosition != eventData.mRightSelectionPosition ) )
522 // Updates the highlight box if the handle's new position is different than the current one.
523 eventData.mUpdateHighlightBox = true;
524 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
525 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
526 eventData.mLeftSelectionPosition = handleNewPosition;
529 // 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.
530 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
532 // Will define the order to scroll the text to match the handle position.
533 eventData.mIsLeftHandleSelected = true;
534 eventData.mIsRightHandleSelected = false;
536 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
538 impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING );
540 if( ( handleNewPosition != eventData.mRightSelectionPosition ) &&
541 ( handleNewPosition != eventData.mLeftSelectionPosition ) )
543 // Updates the highlight box if the handle's new position is different than the current one.
544 eventData.mUpdateHighlightBox = true;
545 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
546 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
547 eventData.mRightSelectionPosition = handleNewPosition;
550 // 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.
551 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
553 // Will define the order to scroll the text to match the handle position.
554 eventData.mIsLeftHandleSelected = false;
555 eventData.mIsRightHandleSelected = true;
559 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
561 CharacterIndex handlePosition = 0u;
562 if( handleStopScrolling || isSmoothHandlePanEnabled )
564 ModelPtr& model = impl.mModel;
565 const Vector2& scrollPosition = model->mScrollPosition;
567 // Convert from decorator's coords to text's coords.
568 const float xPosition = event.p2.mFloat - scrollPosition.x;
569 const float yPosition = event.p3.mFloat - scrollPosition.y;
571 bool matchedCharacter = false;
572 handlePosition = Text::GetClosestCursorIndex( model->mVisualModel,
573 model->mLogicalModel,
577 CharacterHitTest::SCROLL,
581 EventData& eventData = *impl.mEventData;
583 if( Event::GRAB_HANDLE_EVENT == event.type )
585 eventData.mUpdateCursorPosition = true;
586 eventData.mUpdateGrabHandlePosition = true;
587 eventData.mUpdateInputStyle = true;
589 if( !impl.IsClipboardEmpty() )
591 impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
594 if( handleStopScrolling || isSmoothHandlePanEnabled )
596 eventData.mScrollAfterUpdatePosition = true;
597 eventData.mPrimaryCursorPosition = handlePosition;
600 else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
602 impl.ChangeState( EventData::SELECTING );
604 eventData.mUpdateHighlightBox = true;
605 eventData.mUpdateLeftSelectionPosition = true;
606 eventData.mUpdateRightSelectionPosition = true;
608 if( handleStopScrolling || isSmoothHandlePanEnabled )
610 eventData.mScrollAfterUpdatePosition = true;
612 if( ( handlePosition != eventData.mRightSelectionPosition ) &&
613 ( handlePosition != eventData.mLeftSelectionPosition ) )
615 eventData.mLeftSelectionPosition = handlePosition;
619 else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
621 impl.ChangeState( EventData::SELECTING );
623 eventData.mUpdateHighlightBox = true;
624 eventData.mUpdateRightSelectionPosition = true;
625 eventData.mUpdateLeftSelectionPosition = true;
627 if( handleStopScrolling || isSmoothHandlePanEnabled )
629 eventData.mScrollAfterUpdatePosition = true;
630 if( ( handlePosition != eventData.mRightSelectionPosition ) &&
631 ( handlePosition != eventData.mLeftSelectionPosition ) )
633 eventData.mRightSelectionPosition = handlePosition;
638 eventData.mDecoratorUpdated = true;
641 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
643 ModelPtr& model = impl.mModel;
644 Vector2& scrollPosition = model->mScrollPosition;
645 VisualModelPtr& visualModel = model->mVisualModel;
647 const float xSpeed = event.p2.mFloat;
648 const float ySpeed = event.p3.mFloat;
649 const Vector2& layoutSize = visualModel->GetLayoutSize();
650 const Vector2 currentScrollPosition = scrollPosition;
652 scrollPosition.x += xSpeed;
653 scrollPosition.y += ySpeed;
655 impl.ClampHorizontalScroll( layoutSize );
656 impl.ClampVerticalScroll( layoutSize );
658 EventData& eventData = *impl.mEventData;
659 DecoratorPtr& decorator = eventData.mDecorator;
661 bool endOfScroll = false;
662 if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) )
664 // Notify the decorator there is no more text to scroll.
665 // The decorator won't send more scroll events.
666 decorator->NotifyEndOfScroll();
667 // Still need to set the position of the handle.
671 // Set the position of the handle.
672 const bool scrollRightDirection = xSpeed > 0.f;
673 const bool scrollBottomDirection = ySpeed > 0.f;
674 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
675 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
677 if( Event::GRAB_HANDLE_EVENT == event.type )
679 impl.ChangeState( EventData::GRAB_HANDLE_PANNING );
681 // Get the grab handle position in decorator coords.
682 Vector2 position = decorator->GetPosition( GRAB_HANDLE );
684 if( decorator->IsHorizontalScrollEnabled() )
686 // Position the grag handle close to either the left or right edge.
687 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
690 if( decorator->IsVerticalScrollEnabled() )
692 position.x = eventData.mCursorHookPositionX;
694 // Position the grag handle close to either the top or bottom edge.
695 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
698 // Get the new handle position.
699 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
700 bool matchedCharacter = false;
701 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
702 impl.mModel->mLogicalModel,
704 position.x - scrollPosition.x,
705 position.y - scrollPosition.y,
706 CharacterHitTest::SCROLL,
709 if( eventData.mPrimaryCursorPosition != handlePosition )
711 eventData.mUpdateCursorPosition = true;
712 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
713 eventData.mScrollAfterUpdatePosition = true;
714 eventData.mPrimaryCursorPosition = handlePosition;
716 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
718 // Updates the decorator if the soft handle panning is enabled.
719 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
721 else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
723 impl.ChangeState( EventData::SELECTION_HANDLE_PANNING );
725 // Get the selection handle position in decorator coords.
726 Vector2 position = decorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
728 if( decorator->IsHorizontalScrollEnabled() )
730 // Position the selection handle close to either the left or right edge.
731 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
734 if( decorator->IsVerticalScrollEnabled() )
736 position.x = eventData.mCursorHookPositionX;
738 // Position the grag handle close to either the top or bottom edge.
739 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
742 // Get the new handle position.
743 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
744 bool matchedCharacter = false;
745 const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel,
746 impl.mModel->mLogicalModel,
748 position.x - scrollPosition.x,
749 position.y - scrollPosition.y,
750 CharacterHitTest::SCROLL,
753 if( leftSelectionHandleEvent )
755 const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition );
757 if( differentHandles || endOfScroll )
759 eventData.mUpdateHighlightBox = true;
760 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
761 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
762 eventData.mLeftSelectionPosition = handlePosition;
767 const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition );
768 if( differentHandles || endOfScroll )
770 eventData.mUpdateHighlightBox = true;
771 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
772 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
773 eventData.mRightSelectionPosition = handlePosition;
777 if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition )
779 impl.RepositionSelectionHandles();
781 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
784 eventData.mDecoratorUpdated = true;
789 } // namespace Toolkit