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);
107 case Event::SELECT_RANGE:
109 OnSelectRangeEvent(impl, *iter);
116 if(eventData->mUpdateCursorPosition ||
117 eventData->mUpdateHighlightBox)
119 impl.NotifyInputMethodContext();
122 // The cursor must also be repositioned after inserts into the model
123 if(eventData->mUpdateCursorPosition)
125 // Updates the cursor position and scrolls the text to make it visible.
126 CursorInfo cursorInfo;
127 // Calculate the cursor position from the new cursor index.
128 impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
130 if(nullptr != impl.mEditableControlInterface)
132 impl.mEditableControlInterface->CursorMoved(eventData->mPrimaryCursorPosition);
135 if(eventData->mUpdateCursorHookPosition)
137 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
138 eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
139 eventData->mUpdateCursorHookPosition = false;
142 // Scroll first the text after delete ...
143 if(eventData->mScrollAfterDelete)
145 impl.ScrollTextToMatchCursor(cursorInfo);
148 // ... then, text can be scrolled to make the cursor visible.
149 if(eventData->mScrollAfterUpdatePosition)
151 const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset);
152 impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight);
154 eventData->mScrollAfterUpdatePosition = false;
155 eventData->mScrollAfterDelete = false;
157 impl.UpdateCursorPosition(cursorInfo);
159 eventData->mDecoratorUpdated = true;
160 eventData->mUpdateCursorPosition = false;
161 eventData->mUpdateGrabHandlePosition = false;
164 if(eventData->mUpdateHighlightBox ||
165 eventData->mUpdateLeftSelectionPosition ||
166 eventData->mUpdateRightSelectionPosition)
168 CursorInfo leftHandleInfo;
169 CursorInfo rightHandleInfo;
171 if(eventData->mUpdateHighlightBox)
173 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
175 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
177 if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition))
179 if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected)
181 CursorInfo& infoLeft = leftHandleInfo;
183 const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset);
184 impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight);
186 CursorInfo& infoRight = rightHandleInfo;
188 const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset);
189 impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight);
193 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
195 const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset);
196 impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight);
201 if(eventData->mUpdateLeftSelectionPosition)
203 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
205 impl.SetPopupButtons();
206 eventData->mDecoratorUpdated = true;
207 eventData->mUpdateLeftSelectionPosition = false;
210 if(eventData->mUpdateRightSelectionPosition)
212 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
214 impl.SetPopupButtons();
215 eventData->mDecoratorUpdated = true;
216 eventData->mUpdateRightSelectionPosition = false;
219 if(eventData->mUpdateHighlightBox)
221 impl.RepositionSelectionHandles();
223 eventData->mUpdateLeftSelectionPosition = false;
224 eventData->mUpdateRightSelectionPosition = false;
225 eventData->mUpdateHighlightBox = false;
226 eventData->mIsLeftHandleSelected = false;
227 eventData->mIsRightHandleSelected = false;
230 eventData->mScrollAfterUpdatePosition = false;
233 if(eventData->mUpdateInputStyle)
235 // Keep a copy of the current input style.
236 InputStyle currentInputStyle;
237 currentInputStyle.Copy(eventData->mInputStyle);
239 // Set the default style first.
240 impl.RetrieveDefaultInputStyle(eventData->mInputStyle);
242 // Get the character index from the cursor index.
243 const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u;
245 // Retrieve the style from the style runs stored in the logical model.
246 impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle);
248 // Compare if the input style has changed.
249 const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle);
251 if(hasInputStyleChanged)
253 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle);
254 // Queue the input style changed signal.
255 eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
258 eventData->mUpdateInputStyle = false;
261 eventData->mEventQueue.clear();
263 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n");
265 const bool decoratorUpdated = eventData->mDecoratorUpdated;
266 eventData->mDecoratorUpdated = false;
268 return decoratorUpdated;
271 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
273 if(NULL == impl.mEventData || !impl.IsShowingRealText())
275 // Nothing to do if there is no text input.
279 int keyCode = event.p1.mInt;
280 bool isShiftModifier = event.p2.mBool;
281 EventData& eventData = *impl.mEventData;
282 ModelPtr& model = impl.mModel;
283 LogicalModelPtr& logicalModel = model->mLogicalModel;
284 VisualModelPtr& visualModel = model->mVisualModel;
286 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
287 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
289 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
291 if(primaryCursorPosition > 0u)
293 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
295 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
299 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
303 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
305 if(logicalModel->mText.Count() > primaryCursorPosition)
307 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
309 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
313 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
317 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
319 // Ignore Shift-Up for text selection for now.
321 // Get first the line index of the current cursor position index.
322 CharacterIndex characterIndex = 0u;
324 if(primaryCursorPosition > 0u)
326 characterIndex = primaryCursorPosition - 1u;
329 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
330 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
332 // Retrieve the cursor position info.
333 CursorInfo cursorInfo;
334 impl.GetCursorPosition(primaryCursorPosition,
337 // Get the line above.
338 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
340 // Get the next hit 'y' point.
341 const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
343 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
344 bool matchedCharacter = false;
345 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
348 eventData.mCursorHookPositionX,
350 CharacterHitTest::TAP,
353 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
355 // Ignore Shift-Down for text selection for now.
357 // Get first the line index of the current cursor position index.
358 CharacterIndex characterIndex = 0u;
360 if(primaryCursorPosition > 0u)
362 characterIndex = primaryCursorPosition - 1u;
365 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
367 if(lineIndex + 1u < visualModel->mLines.Count())
369 // Retrieve the cursor position info.
370 CursorInfo cursorInfo;
371 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
373 // Get the line below.
374 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
376 // Get the next hit 'y' point.
377 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
379 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
380 bool matchedCharacter = false;
381 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
384 eventData.mCursorHookPositionX,
386 CharacterHitTest::TAP,
391 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
393 // Update selection position after moving the cursor
394 eventData.mLeftSelectionPosition = primaryCursorPosition;
395 eventData.mRightSelectionPosition = primaryCursorPosition;
398 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
400 // Handle text selection
401 bool selecting = false;
403 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
405 // Shift-Left/Right to select the text
406 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
407 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
409 eventData.mRightSelectionPosition += cursorPositionDelta;
413 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
415 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
421 // Notify the cursor position to the InputMethodContext.
422 if(eventData.mInputMethodContext)
424 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
425 eventData.mInputMethodContext.NotifyCursorPosition();
428 impl.ChangeState(EventData::SELECTING);
430 eventData.mUpdateLeftSelectionPosition = true;
431 eventData.mUpdateRightSelectionPosition = true;
432 eventData.mUpdateGrabHandlePosition = true;
433 eventData.mUpdateHighlightBox = true;
435 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
436 if(eventData.mGrabHandlePopupEnabled)
438 eventData.mDecorator->SetPopupActive(false);
444 // Handle normal cursor move
445 impl.ChangeState(EventData::EDITING);
446 eventData.mUpdateCursorPosition = true;
449 eventData.mUpdateInputStyle = true;
450 eventData.mScrollAfterUpdatePosition = true;
453 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
457 const unsigned int tapCount = event.p1.mUint;
458 EventData& eventData = *impl.mEventData;
459 ModelPtr& model = impl.mModel;
460 LogicalModelPtr& logicalModel = model->mLogicalModel;
461 VisualModelPtr& visualModel = model->mVisualModel;
465 if(impl.IsShowingRealText())
467 // Convert from control's coords to text's coords.
468 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
469 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
471 // Keep the tap 'x' position. Used to move the cursor.
472 eventData.mCursorHookPositionX = xPosition;
474 // Whether to touch point hits on a glyph.
475 bool matchedCharacter = false;
476 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
481 CharacterHitTest::TAP,
484 // When the cursor position is changing, delay cursor blinking
485 eventData.mDecorator->DelayCursorBlink();
489 eventData.mPrimaryCursorPosition = 0u;
492 // Update selection position after tapping
493 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
494 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
496 eventData.mUpdateCursorPosition = true;
497 eventData.mUpdateGrabHandlePosition = true;
498 eventData.mScrollAfterUpdatePosition = true;
499 eventData.mUpdateInputStyle = true;
501 // Notify the cursor position to the InputMethodContext.
502 if(eventData.mInputMethodContext)
504 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
505 eventData.mInputMethodContext.NotifyCursorPosition();
508 else if(2u == tapCount)
510 if(eventData.mSelectionEnabled)
512 // Convert from control's coords to text's coords.
513 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
514 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
516 // Calculates the logical position from the x,y coords.
517 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
523 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
527 EventData& eventData = *impl.mEventData;
528 DecoratorPtr& decorator = eventData.mDecorator;
530 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
531 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
533 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
535 // Nothing to do if scrolling is not enabled.
539 const GestureState state = static_cast<GestureState>(event.p1.mInt);
542 case GestureState::STARTED:
544 // Will remove the cursor, handles or text's popup, ...
545 impl.ChangeState(EventData::TEXT_PANNING);
548 case GestureState::CONTINUING:
550 ModelPtr& model = impl.mModel;
552 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
553 Vector2& scrollPosition = model->mScrollPosition;
554 const Vector2 currentScroll = scrollPosition;
556 if(isHorizontalScrollEnabled)
558 const float displacementX = event.p2.mFloat;
559 scrollPosition.x += displacementX;
561 impl.ClampHorizontalScroll(layoutSize);
564 if(isVerticalScrollEnabled)
566 const float displacementY = event.p3.mFloat;
567 scrollPosition.y += displacementY;
569 impl.ClampVerticalScroll(layoutSize);
572 decorator->UpdatePositions(scrollPosition - currentScroll);
575 case GestureState::FINISHED:
576 case GestureState::CANCELLED: // FALLTHROUGH
578 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
579 impl.ChangeState(eventData.mPreviousState);
588 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
590 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
594 EventData& eventData = *impl.mEventData;
596 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
598 impl.ChangeState(EventData::EDITING_WITH_POPUP);
599 eventData.mDecoratorUpdated = true;
600 eventData.mUpdateInputStyle = true;
604 if(eventData.mSelectionEnabled)
606 ModelPtr& model = impl.mModel;
608 // Convert from control's coords to text's coords.
609 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
610 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
612 // Calculates the logical position from the x,y coords.
613 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
619 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
623 const unsigned int state = event.p1.mUint;
624 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
625 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
627 if(HANDLE_PRESSED == state)
629 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
630 } // end ( HANDLE_PRESSED == state )
631 else if((HANDLE_RELEASED == state) ||
634 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
635 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
636 else if(HANDLE_SCROLLING == state)
638 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
639 } // end ( HANDLE_SCROLLING == state )
643 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
645 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
647 ModelPtr& model = impl.mModel;
648 const Vector2& scrollPosition = model->mScrollPosition;
650 // Convert from control's coords to text's coords.
651 const float xPosition = event.p2.mFloat - scrollPosition.x;
652 const float yPosition = event.p3.mFloat - scrollPosition.y;
654 // Calculates the logical position from the x,y coords.
655 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
659 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
661 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
665 EventData& eventData = *impl.mEventData;
666 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
668 ModelPtr& model = impl.mModel;
669 const Vector2& scrollPosition = model->mScrollPosition;
671 // Calculates the logical position from the start.
672 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
673 0.f - scrollPosition.y,
674 Controller::NoTextTap::HIGHLIGHT);
676 eventData.mLeftSelectionPosition = 0u;
677 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
682 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
684 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
688 EventData& eventData = *impl.mEventData;
689 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
691 eventData.mPrimaryCursorPosition = 0u;
692 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
693 impl.ChangeState(EventData::INACTIVE);
694 eventData.mUpdateCursorPosition = true;
695 eventData.mUpdateInputStyle = true;
696 eventData.mScrollAfterUpdatePosition = true;
701 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
703 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
705 ModelPtr& model = impl.mModel;
706 const Vector2& scrollPosition = model->mScrollPosition;
708 // Calculate the selection index.
709 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
710 const uint32_t start = std::min(event.p2.mUint, length);
711 const uint32_t end = std::min(event.p3.mUint, length);
715 // Calculates the logical position from the x,y coords.
716 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
718 impl.mEventData->mLeftSelectionPosition = start;
719 impl.mEventData->mRightSelectionPosition = end;
724 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
726 ModelPtr& model = impl.mModel;
727 const Vector2& scrollPosition = model->mScrollPosition;
729 // Convert from decorator's coords to text's coords.
730 const float xPosition = event.p2.mFloat - scrollPosition.x;
731 const float yPosition = event.p3.mFloat - scrollPosition.y;
733 // Need to calculate the handle's new position.
734 bool matchedCharacter = false;
735 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
736 model->mLogicalModel,
740 CharacterHitTest::SCROLL,
743 EventData& eventData = *impl.mEventData;
745 if(Event::GRAB_HANDLE_EVENT == event.type)
747 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
749 if(handleNewPosition != eventData.mPrimaryCursorPosition)
751 // Updates the cursor position if the handle's new position is different than the current one.
752 eventData.mUpdateCursorPosition = true;
753 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
754 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
755 eventData.mPrimaryCursorPosition = handleNewPosition;
758 // 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.
759 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
761 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
763 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
765 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
766 (handleNewPosition != eventData.mRightSelectionPosition))
768 // Updates the highlight box if the handle's new position is different than the current one.
769 eventData.mUpdateHighlightBox = true;
770 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
771 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
772 eventData.mLeftSelectionPosition = handleNewPosition;
775 // 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.
776 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
778 // Will define the order to scroll the text to match the handle position.
779 eventData.mIsLeftHandleSelected = true;
780 eventData.mIsRightHandleSelected = false;
782 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
784 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
786 if((handleNewPosition != eventData.mRightSelectionPosition) &&
787 (handleNewPosition != eventData.mLeftSelectionPosition))
789 // Updates the highlight box if the handle's new position is different than the current one.
790 eventData.mUpdateHighlightBox = true;
791 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
792 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
793 eventData.mRightSelectionPosition = handleNewPosition;
796 // 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.
797 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
799 // Will define the order to scroll the text to match the handle position.
800 eventData.mIsLeftHandleSelected = false;
801 eventData.mIsRightHandleSelected = true;
805 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
807 CharacterIndex handlePosition = 0u;
808 if(handleStopScrolling || isSmoothHandlePanEnabled)
810 ModelPtr& model = impl.mModel;
811 const Vector2& scrollPosition = model->mScrollPosition;
813 // Convert from decorator's coords to text's coords.
814 const float xPosition = event.p2.mFloat - scrollPosition.x;
815 const float yPosition = event.p3.mFloat - scrollPosition.y;
817 bool matchedCharacter = false;
818 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
819 model->mLogicalModel,
823 CharacterHitTest::SCROLL,
827 EventData& eventData = *impl.mEventData;
829 if(Event::GRAB_HANDLE_EVENT == event.type)
831 eventData.mUpdateCursorPosition = true;
832 eventData.mUpdateGrabHandlePosition = true;
833 eventData.mUpdateInputStyle = true;
835 if(!impl.IsClipboardEmpty())
837 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
840 if(handleStopScrolling || isSmoothHandlePanEnabled)
842 eventData.mScrollAfterUpdatePosition = true;
843 eventData.mPrimaryCursorPosition = handlePosition;
846 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
848 impl.ChangeState(EventData::SELECTING);
850 eventData.mUpdateHighlightBox = true;
851 eventData.mUpdateLeftSelectionPosition = true;
852 eventData.mUpdateRightSelectionPosition = true;
854 if(handleStopScrolling || isSmoothHandlePanEnabled)
856 eventData.mScrollAfterUpdatePosition = true;
858 if((handlePosition != eventData.mRightSelectionPosition) &&
859 (handlePosition != eventData.mLeftSelectionPosition))
861 eventData.mLeftSelectionPosition = handlePosition;
865 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
867 impl.ChangeState(EventData::SELECTING);
869 eventData.mUpdateHighlightBox = true;
870 eventData.mUpdateRightSelectionPosition = true;
871 eventData.mUpdateLeftSelectionPosition = true;
873 if(handleStopScrolling || isSmoothHandlePanEnabled)
875 eventData.mScrollAfterUpdatePosition = true;
876 if((handlePosition != eventData.mRightSelectionPosition) &&
877 (handlePosition != eventData.mLeftSelectionPosition))
879 eventData.mRightSelectionPosition = handlePosition;
884 eventData.mDecoratorUpdated = true;
887 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
889 ModelPtr& model = impl.mModel;
890 Vector2& scrollPosition = model->mScrollPosition;
891 VisualModelPtr& visualModel = model->mVisualModel;
893 const float xSpeed = event.p2.mFloat;
894 const float ySpeed = event.p3.mFloat;
895 const Vector2& layoutSize = visualModel->GetLayoutSize();
896 const Vector2 currentScrollPosition = scrollPosition;
898 scrollPosition.x += xSpeed;
899 scrollPosition.y += ySpeed;
901 impl.ClampHorizontalScroll(layoutSize);
902 impl.ClampVerticalScroll(layoutSize);
904 EventData& eventData = *impl.mEventData;
905 DecoratorPtr& decorator = eventData.mDecorator;
907 bool endOfScroll = false;
908 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
910 // Notify the decorator there is no more text to scroll.
911 // The decorator won't send more scroll events.
912 decorator->NotifyEndOfScroll();
913 // Still need to set the position of the handle.
917 // Set the position of the handle.
918 const bool scrollRightDirection = xSpeed > 0.f;
919 const bool scrollBottomDirection = ySpeed > 0.f;
920 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
921 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
923 if(Event::GRAB_HANDLE_EVENT == event.type)
925 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
927 // Get the grab handle position in decorator coords.
928 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
930 if(decorator->IsHorizontalScrollEnabled())
932 // Position the grag handle close to either the left or right edge.
933 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
936 if(decorator->IsVerticalScrollEnabled())
938 position.x = eventData.mCursorHookPositionX;
940 // Position the grag handle close to either the top or bottom edge.
941 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
944 // Get the new handle position.
945 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
946 bool matchedCharacter = false;
947 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
948 impl.mModel->mLogicalModel,
950 position.x - scrollPosition.x,
951 position.y - scrollPosition.y,
952 CharacterHitTest::SCROLL,
955 if(eventData.mPrimaryCursorPosition != handlePosition)
957 eventData.mUpdateCursorPosition = true;
958 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
959 eventData.mScrollAfterUpdatePosition = true;
960 eventData.mPrimaryCursorPosition = handlePosition;
962 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
964 // Updates the decorator if the soft handle panning is enabled.
965 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
967 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
969 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
971 // Get the selection handle position in decorator coords.
972 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
974 if(decorator->IsHorizontalScrollEnabled())
976 // Position the selection handle close to either the left or right edge.
977 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
980 if(decorator->IsVerticalScrollEnabled())
982 position.x = eventData.mCursorHookPositionX;
984 // Position the grag handle close to either the top or bottom edge.
985 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
988 // Get the new handle position.
989 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
990 bool matchedCharacter = false;
991 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
992 impl.mModel->mLogicalModel,
994 position.x - scrollPosition.x,
995 position.y - scrollPosition.y,
996 CharacterHitTest::SCROLL,
999 if(leftSelectionHandleEvent)
1001 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1003 if(differentHandles || endOfScroll)
1005 eventData.mUpdateHighlightBox = true;
1006 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1007 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1008 eventData.mLeftSelectionPosition = handlePosition;
1013 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1014 if(differentHandles || endOfScroll)
1016 eventData.mUpdateHighlightBox = true;
1017 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1018 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1019 eventData.mRightSelectionPosition = handlePosition;
1023 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1025 impl.RepositionSelectionHandles();
1027 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1030 eventData.mDecoratorUpdated = true;
1035 } // namespace Toolkit