2 * Copyright (c) 2021 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/integration-api/debug.h>
23 #include <dali/public-api/adaptor-framework/key.h>
26 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
27 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
45 bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
47 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n");
49 EventData*& eventData = impl.mEventData;
52 // Nothing to do if there is no text input.
53 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n");
57 if(eventData->mDecorator)
59 for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
60 iter != eventData->mEventQueue.end();
65 case Event::CURSOR_KEY_EVENT:
67 OnCursorKeyEvent(impl, *iter);
70 case Event::TAP_EVENT:
72 OnTapEvent(impl, *iter);
75 case Event::LONG_PRESS_EVENT:
77 OnLongPressEvent(impl, *iter);
80 case Event::PAN_EVENT:
82 OnPanEvent(impl, *iter);
85 case Event::GRAB_HANDLE_EVENT:
86 case Event::LEFT_SELECTION_HANDLE_EVENT:
87 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
89 OnHandleEvent(impl, *iter);
94 OnSelectEvent(impl, *iter);
97 case Event::SELECT_ALL:
99 OnSelectAllEvent(impl);
102 case Event::SELECT_NONE:
104 OnSelectNoneEvent(impl);
111 if(eventData->mUpdateCursorPosition ||
112 eventData->mUpdateHighlightBox)
114 impl.NotifyInputMethodContext();
117 // The cursor must also be repositioned after inserts into the model
118 if(eventData->mUpdateCursorPosition)
120 // Updates the cursor position and scrolls the text to make it visible.
121 CursorInfo cursorInfo;
122 // Calculate the cursor position from the new cursor index.
123 impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
125 if(nullptr != impl.mEditableControlInterface)
127 impl.mEditableControlInterface->CaretMoved(eventData->mPrimaryCursorPosition);
130 if(eventData->mUpdateCursorHookPosition)
132 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
133 eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
134 eventData->mUpdateCursorHookPosition = false;
137 // Scroll first the text after delete ...
138 if(eventData->mScrollAfterDelete)
140 impl.ScrollTextToMatchCursor(cursorInfo);
143 // ... then, text can be scrolled to make the cursor visible.
144 if(eventData->mScrollAfterUpdatePosition)
146 const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset);
147 impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight);
149 eventData->mScrollAfterUpdatePosition = false;
150 eventData->mScrollAfterDelete = false;
152 impl.UpdateCursorPosition(cursorInfo);
154 eventData->mDecoratorUpdated = true;
155 eventData->mUpdateCursorPosition = false;
156 eventData->mUpdateGrabHandlePosition = false;
159 if(eventData->mUpdateHighlightBox ||
160 eventData->mUpdateLeftSelectionPosition ||
161 eventData->mUpdateRightSelectionPosition)
163 CursorInfo leftHandleInfo;
164 CursorInfo rightHandleInfo;
166 if(eventData->mUpdateHighlightBox)
168 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
170 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
172 if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition))
174 if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected)
176 CursorInfo& infoLeft = leftHandleInfo;
178 const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset);
179 impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight);
181 CursorInfo& infoRight = rightHandleInfo;
183 const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset);
184 impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight);
188 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
190 const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset);
191 impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight);
196 if(eventData->mUpdateLeftSelectionPosition)
198 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
200 impl.SetPopupButtons();
201 eventData->mDecoratorUpdated = true;
202 eventData->mUpdateLeftSelectionPosition = false;
205 if(eventData->mUpdateRightSelectionPosition)
207 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
209 impl.SetPopupButtons();
210 eventData->mDecoratorUpdated = true;
211 eventData->mUpdateRightSelectionPosition = false;
214 if(eventData->mUpdateHighlightBox)
216 impl.RepositionSelectionHandles();
218 eventData->mUpdateLeftSelectionPosition = false;
219 eventData->mUpdateRightSelectionPosition = false;
220 eventData->mUpdateHighlightBox = false;
221 eventData->mIsLeftHandleSelected = false;
222 eventData->mIsRightHandleSelected = false;
225 eventData->mScrollAfterUpdatePosition = false;
228 if(eventData->mUpdateInputStyle)
230 // Keep a copy of the current input style.
231 InputStyle currentInputStyle;
232 currentInputStyle.Copy(eventData->mInputStyle);
234 // Set the default style first.
235 impl.RetrieveDefaultInputStyle(eventData->mInputStyle);
237 // Get the character index from the cursor index.
238 const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u;
240 // Retrieve the style from the style runs stored in the logical model.
241 impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle);
243 // Compare if the input style has changed.
244 const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle);
246 if(hasInputStyleChanged)
248 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle);
249 // Queue the input style changed signal.
250 eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
253 eventData->mUpdateInputStyle = false;
256 eventData->mEventQueue.clear();
258 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n");
260 const bool decoratorUpdated = eventData->mDecoratorUpdated;
261 eventData->mDecoratorUpdated = false;
263 return decoratorUpdated;
266 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
268 if(NULL == impl.mEventData || !impl.IsShowingRealText())
270 // Nothing to do if there is no text input.
274 int keyCode = event.p1.mInt;
275 bool isShiftModifier = event.p2.mBool;
276 EventData& eventData = *impl.mEventData;
277 ModelPtr& model = impl.mModel;
278 LogicalModelPtr& logicalModel = model->mLogicalModel;
279 VisualModelPtr& visualModel = model->mVisualModel;
281 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
282 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
284 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
286 if(primaryCursorPosition > 0u)
288 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
290 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
294 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
298 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
300 if(logicalModel->mText.Count() > primaryCursorPosition)
302 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
304 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
308 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
312 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
314 // Ignore Shift-Up for text selection for now.
316 // Get first the line index of the current cursor position index.
317 CharacterIndex characterIndex = 0u;
319 if(primaryCursorPosition > 0u)
321 characterIndex = primaryCursorPosition - 1u;
324 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
325 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
327 // Retrieve the cursor position info.
328 CursorInfo cursorInfo;
329 impl.GetCursorPosition(primaryCursorPosition,
332 // Get the line above.
333 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
335 // Get the next hit 'y' point.
336 const float hitPointY = cursorInfo.lineOffset - 0.5f * (line.ascender - line.descender);
338 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
339 bool matchedCharacter = false;
340 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
343 eventData.mCursorHookPositionX,
345 CharacterHitTest::TAP,
348 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
350 // Ignore Shift-Down for text selection for now.
352 // Get first the line index of the current cursor position index.
353 CharacterIndex characterIndex = 0u;
355 if(primaryCursorPosition > 0u)
357 characterIndex = primaryCursorPosition - 1u;
360 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
362 if(lineIndex + 1u < visualModel->mLines.Count())
364 // Retrieve the cursor position info.
365 CursorInfo cursorInfo;
366 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
368 // Get the line below.
369 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
371 // Get the next hit 'y' point.
372 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * (line.ascender - line.descender);
374 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
375 bool matchedCharacter = false;
376 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
379 eventData.mCursorHookPositionX,
381 CharacterHitTest::TAP,
386 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
388 // Update selection position after moving the cursor
389 eventData.mLeftSelectionPosition = primaryCursorPosition;
390 eventData.mRightSelectionPosition = primaryCursorPosition;
393 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
395 // Handle text selection
396 bool selecting = false;
398 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
400 // Shift-Left/Right to select the text
401 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
402 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
404 eventData.mRightSelectionPosition += cursorPositionDelta;
408 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
410 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
416 // Notify the cursor position to the InputMethodContext.
417 if(eventData.mInputMethodContext)
419 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
420 eventData.mInputMethodContext.NotifyCursorPosition();
423 impl.ChangeState(EventData::SELECTING);
425 eventData.mUpdateLeftSelectionPosition = true;
426 eventData.mUpdateRightSelectionPosition = true;
427 eventData.mUpdateGrabHandlePosition = true;
428 eventData.mUpdateHighlightBox = true;
430 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
431 if(eventData.mGrabHandlePopupEnabled)
433 eventData.mDecorator->SetPopupActive(false);
439 // Handle normal cursor move
440 impl.ChangeState(EventData::EDITING);
441 eventData.mUpdateCursorPosition = true;
444 eventData.mUpdateInputStyle = true;
445 eventData.mScrollAfterUpdatePosition = true;
448 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
452 const unsigned int tapCount = event.p1.mUint;
453 EventData& eventData = *impl.mEventData;
454 ModelPtr& model = impl.mModel;
455 LogicalModelPtr& logicalModel = model->mLogicalModel;
456 VisualModelPtr& visualModel = model->mVisualModel;
460 if(impl.IsShowingRealText())
462 // Convert from control's coords to text's coords.
463 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
464 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
466 // Keep the tap 'x' position. Used to move the cursor.
467 eventData.mCursorHookPositionX = xPosition;
469 // Whether to touch point hits on a glyph.
470 bool matchedCharacter = false;
471 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
476 CharacterHitTest::TAP,
479 // When the cursor position is changing, delay cursor blinking
480 eventData.mDecorator->DelayCursorBlink();
484 eventData.mPrimaryCursorPosition = 0u;
487 // Update selection position after tapping
488 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
489 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
491 eventData.mUpdateCursorPosition = true;
492 eventData.mUpdateGrabHandlePosition = true;
493 eventData.mScrollAfterUpdatePosition = true;
494 eventData.mUpdateInputStyle = true;
496 // Notify the cursor position to the InputMethodContext.
497 if(eventData.mInputMethodContext)
499 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
500 eventData.mInputMethodContext.NotifyCursorPosition();
503 else if(2u == tapCount)
505 if(eventData.mSelectionEnabled)
507 // Convert from control's coords to text's coords.
508 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
509 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
511 // Calculates the logical position from the x,y coords.
512 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
518 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
522 EventData& eventData = *impl.mEventData;
523 DecoratorPtr& decorator = eventData.mDecorator;
525 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
526 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
528 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
530 // Nothing to do if scrolling is not enabled.
534 const GestureState state = static_cast<GestureState>(event.p1.mInt);
537 case GestureState::STARTED:
539 // Will remove the cursor, handles or text's popup, ...
540 impl.ChangeState(EventData::TEXT_PANNING);
543 case GestureState::CONTINUING:
545 ModelPtr& model = impl.mModel;
547 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
548 Vector2& scrollPosition = model->mScrollPosition;
549 const Vector2 currentScroll = scrollPosition;
551 if(isHorizontalScrollEnabled)
553 const float displacementX = event.p2.mFloat;
554 scrollPosition.x += displacementX;
556 impl.ClampHorizontalScroll(layoutSize);
559 if(isVerticalScrollEnabled)
561 const float displacementY = event.p3.mFloat;
562 scrollPosition.y += displacementY;
564 impl.ClampVerticalScroll(layoutSize);
567 decorator->UpdatePositions(scrollPosition - currentScroll);
570 case GestureState::FINISHED:
571 case GestureState::CANCELLED: // FALLTHROUGH
573 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
574 impl.ChangeState(eventData.mPreviousState);
583 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
585 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
589 EventData& eventData = *impl.mEventData;
591 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
593 impl.ChangeState(EventData::EDITING_WITH_POPUP);
594 eventData.mDecoratorUpdated = true;
595 eventData.mUpdateInputStyle = true;
599 if(eventData.mSelectionEnabled)
601 ModelPtr& model = impl.mModel;
603 // Convert from control's coords to text's coords.
604 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
605 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
607 // Calculates the logical position from the x,y coords.
608 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
614 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
618 const unsigned int state = event.p1.mUint;
619 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
620 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
622 if(HANDLE_PRESSED == state)
624 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
625 } // end ( HANDLE_PRESSED == state )
626 else if((HANDLE_RELEASED == state) ||
629 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
630 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
631 else if(HANDLE_SCROLLING == state)
633 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
634 } // end ( HANDLE_SCROLLING == state )
638 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
640 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
642 ModelPtr& model = impl.mModel;
643 const Vector2& scrollPosition = model->mScrollPosition;
645 // Convert from control's coords to text's coords.
646 const float xPosition = event.p2.mFloat - scrollPosition.x;
647 const float yPosition = event.p3.mFloat - scrollPosition.y;
649 // Calculates the logical position from the x,y coords.
650 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
654 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
656 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
660 EventData& eventData = *impl.mEventData;
661 if(eventData.mSelectionEnabled)
663 ModelPtr& model = impl.mModel;
664 const Vector2& scrollPosition = model->mScrollPosition;
666 // Calculates the logical position from the start.
667 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
668 0.f - scrollPosition.y,
669 Controller::NoTextTap::HIGHLIGHT);
671 eventData.mLeftSelectionPosition = 0u;
672 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
677 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
679 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
683 EventData& eventData = *impl.mEventData;
684 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
686 eventData.mPrimaryCursorPosition = 0u;
687 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
688 impl.ChangeState(EventData::INACTIVE);
689 eventData.mUpdateCursorPosition = true;
690 eventData.mUpdateInputStyle = true;
691 eventData.mScrollAfterUpdatePosition = true;
696 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
698 ModelPtr& model = impl.mModel;
699 const Vector2& scrollPosition = model->mScrollPosition;
701 // Convert from decorator's coords to text's coords.
702 const float xPosition = event.p2.mFloat - scrollPosition.x;
703 const float yPosition = event.p3.mFloat - scrollPosition.y;
705 // Need to calculate the handle's new position.
706 bool matchedCharacter = false;
707 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
708 model->mLogicalModel,
712 CharacterHitTest::SCROLL,
715 EventData& eventData = *impl.mEventData;
717 if(Event::GRAB_HANDLE_EVENT == event.type)
719 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
721 if(handleNewPosition != eventData.mPrimaryCursorPosition)
723 // Updates the cursor position if the handle's new position is different than the current one.
724 eventData.mUpdateCursorPosition = true;
725 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
726 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
727 eventData.mPrimaryCursorPosition = handleNewPosition;
730 // 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.
731 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
733 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
735 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
737 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
738 (handleNewPosition != eventData.mRightSelectionPosition))
740 // Updates the highlight box if the handle's new position is different than the current one.
741 eventData.mUpdateHighlightBox = true;
742 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
743 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
744 eventData.mLeftSelectionPosition = handleNewPosition;
747 // 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.
748 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
750 // Will define the order to scroll the text to match the handle position.
751 eventData.mIsLeftHandleSelected = true;
752 eventData.mIsRightHandleSelected = false;
754 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
756 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
758 if((handleNewPosition != eventData.mRightSelectionPosition) &&
759 (handleNewPosition != eventData.mLeftSelectionPosition))
761 // Updates the highlight box if the handle's new position is different than the current one.
762 eventData.mUpdateHighlightBox = true;
763 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
764 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
765 eventData.mRightSelectionPosition = handleNewPosition;
768 // 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.
769 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
771 // Will define the order to scroll the text to match the handle position.
772 eventData.mIsLeftHandleSelected = false;
773 eventData.mIsRightHandleSelected = true;
777 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
779 CharacterIndex handlePosition = 0u;
780 if(handleStopScrolling || isSmoothHandlePanEnabled)
782 ModelPtr& model = impl.mModel;
783 const Vector2& scrollPosition = model->mScrollPosition;
785 // Convert from decorator's coords to text's coords.
786 const float xPosition = event.p2.mFloat - scrollPosition.x;
787 const float yPosition = event.p3.mFloat - scrollPosition.y;
789 bool matchedCharacter = false;
790 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
791 model->mLogicalModel,
795 CharacterHitTest::SCROLL,
799 EventData& eventData = *impl.mEventData;
801 if(Event::GRAB_HANDLE_EVENT == event.type)
803 eventData.mUpdateCursorPosition = true;
804 eventData.mUpdateGrabHandlePosition = true;
805 eventData.mUpdateInputStyle = true;
807 if(!impl.IsClipboardEmpty())
809 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
812 if(handleStopScrolling || isSmoothHandlePanEnabled)
814 eventData.mScrollAfterUpdatePosition = true;
815 eventData.mPrimaryCursorPosition = handlePosition;
818 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
820 impl.ChangeState(EventData::SELECTING);
822 eventData.mUpdateHighlightBox = true;
823 eventData.mUpdateLeftSelectionPosition = true;
824 eventData.mUpdateRightSelectionPosition = true;
826 if(handleStopScrolling || isSmoothHandlePanEnabled)
828 eventData.mScrollAfterUpdatePosition = true;
830 if((handlePosition != eventData.mRightSelectionPosition) &&
831 (handlePosition != eventData.mLeftSelectionPosition))
833 eventData.mLeftSelectionPosition = handlePosition;
837 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
839 impl.ChangeState(EventData::SELECTING);
841 eventData.mUpdateHighlightBox = true;
842 eventData.mUpdateRightSelectionPosition = true;
843 eventData.mUpdateLeftSelectionPosition = true;
845 if(handleStopScrolling || isSmoothHandlePanEnabled)
847 eventData.mScrollAfterUpdatePosition = true;
848 if((handlePosition != eventData.mRightSelectionPosition) &&
849 (handlePosition != eventData.mLeftSelectionPosition))
851 eventData.mRightSelectionPosition = handlePosition;
856 eventData.mDecoratorUpdated = true;
859 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
861 ModelPtr& model = impl.mModel;
862 Vector2& scrollPosition = model->mScrollPosition;
863 VisualModelPtr& visualModel = model->mVisualModel;
865 const float xSpeed = event.p2.mFloat;
866 const float ySpeed = event.p3.mFloat;
867 const Vector2& layoutSize = visualModel->GetLayoutSize();
868 const Vector2 currentScrollPosition = scrollPosition;
870 scrollPosition.x += xSpeed;
871 scrollPosition.y += ySpeed;
873 impl.ClampHorizontalScroll(layoutSize);
874 impl.ClampVerticalScroll(layoutSize);
876 EventData& eventData = *impl.mEventData;
877 DecoratorPtr& decorator = eventData.mDecorator;
879 bool endOfScroll = false;
880 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
882 // Notify the decorator there is no more text to scroll.
883 // The decorator won't send more scroll events.
884 decorator->NotifyEndOfScroll();
885 // Still need to set the position of the handle.
889 // Set the position of the handle.
890 const bool scrollRightDirection = xSpeed > 0.f;
891 const bool scrollBottomDirection = ySpeed > 0.f;
892 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
893 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
895 if(Event::GRAB_HANDLE_EVENT == event.type)
897 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
899 // Get the grab handle position in decorator coords.
900 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
902 if(decorator->IsHorizontalScrollEnabled())
904 // Position the grag handle close to either the left or right edge.
905 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
908 if(decorator->IsVerticalScrollEnabled())
910 position.x = eventData.mCursorHookPositionX;
912 // Position the grag handle close to either the top or bottom edge.
913 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
916 // Get the new handle position.
917 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
918 bool matchedCharacter = false;
919 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
920 impl.mModel->mLogicalModel,
922 position.x - scrollPosition.x,
923 position.y - scrollPosition.y,
924 CharacterHitTest::SCROLL,
927 if(eventData.mPrimaryCursorPosition != handlePosition)
929 eventData.mUpdateCursorPosition = true;
930 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
931 eventData.mScrollAfterUpdatePosition = true;
932 eventData.mPrimaryCursorPosition = handlePosition;
934 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
936 // Updates the decorator if the soft handle panning is enabled.
937 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
939 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
941 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
943 // Get the selection handle position in decorator coords.
944 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
946 if(decorator->IsHorizontalScrollEnabled())
948 // Position the selection handle close to either the left or right edge.
949 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
952 if(decorator->IsVerticalScrollEnabled())
954 position.x = eventData.mCursorHookPositionX;
956 // Position the grag handle close to either the top or bottom edge.
957 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
960 // Get the new handle position.
961 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
962 bool matchedCharacter = false;
963 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
964 impl.mModel->mLogicalModel,
966 position.x - scrollPosition.x,
967 position.y - scrollPosition.y,
968 CharacterHitTest::SCROLL,
971 if(leftSelectionHandleEvent)
973 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
975 if(differentHandles || endOfScroll)
977 eventData.mUpdateHighlightBox = true;
978 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
979 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
980 eventData.mLeftSelectionPosition = handlePosition;
985 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
986 if(differentHandles || endOfScroll)
988 eventData.mUpdateHighlightBox = true;
989 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
990 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
991 eventData.mRightSelectionPosition = handlePosition;
995 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
997 impl.RepositionSelectionHandles();
999 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1002 eventData.mDecoratorUpdated = true;
1007 } // namespace Toolkit