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 unsigned int oldPos = eventData->mPrimaryCursorPosition;
59 if(eventData->mDecorator)
61 for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
62 iter != eventData->mEventQueue.end();
67 case Event::CURSOR_KEY_EVENT:
69 OnCursorKeyEvent(impl, *iter);
72 case Event::TAP_EVENT:
74 OnTapEvent(impl, *iter);
77 case Event::LONG_PRESS_EVENT:
79 OnLongPressEvent(impl, *iter);
82 case Event::PAN_EVENT:
84 OnPanEvent(impl, *iter);
87 case Event::GRAB_HANDLE_EVENT:
88 case Event::LEFT_SELECTION_HANDLE_EVENT:
89 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
91 OnHandleEvent(impl, *iter);
96 OnSelectEvent(impl, *iter);
99 case Event::SELECT_ALL:
101 OnSelectAllEvent(impl);
104 case Event::SELECT_NONE:
106 OnSelectNoneEvent(impl);
109 case Event::SELECT_RANGE:
111 OnSelectRangeEvent(impl, *iter);
118 if(eventData->mUpdateCursorPosition ||
119 eventData->mUpdateHighlightBox)
121 impl.NotifyInputMethodContext();
124 // The cursor must also be repositioned after inserts into the model
125 if(eventData->mUpdateCursorPosition)
127 // Updates the cursor position and scrolls the text to make it visible.
128 CursorInfo cursorInfo;
130 // Calculate the cursor position from the new cursor index.
131 impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
133 //only emit the event if the cursor is moved in current function.
134 if(nullptr != impl.mEditableControlInterface && eventData->mEventQueue.size() > 0)
136 impl.mEditableControlInterface->CursorPositionChanged(oldPos, eventData->mPrimaryCursorPosition);
139 if(eventData->mUpdateCursorHookPosition)
141 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
142 eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
143 eventData->mUpdateCursorHookPosition = false;
146 // Scroll first the text after delete ...
147 if(eventData->mScrollAfterDelete)
149 impl.ScrollTextToMatchCursor(cursorInfo);
152 // ... then, text can be scrolled to make the cursor visible.
153 if(eventData->mScrollAfterUpdatePosition)
155 const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset);
156 impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight);
158 eventData->mScrollAfterUpdatePosition = false;
159 eventData->mScrollAfterDelete = false;
161 impl.UpdateCursorPosition(cursorInfo);
163 eventData->mDecoratorUpdated = true;
164 eventData->mUpdateCursorPosition = false;
165 eventData->mUpdateGrabHandlePosition = false;
168 if(eventData->mUpdateHighlightBox ||
169 eventData->mUpdateLeftSelectionPosition ||
170 eventData->mUpdateRightSelectionPosition)
172 CursorInfo leftHandleInfo;
173 CursorInfo rightHandleInfo;
175 if(eventData->mUpdateHighlightBox)
177 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
179 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
181 if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition))
183 if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected)
185 CursorInfo& infoLeft = leftHandleInfo;
187 const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset);
188 impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight);
190 CursorInfo& infoRight = rightHandleInfo;
192 const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset);
193 impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight);
197 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
199 const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset);
200 impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight);
205 if(eventData->mUpdateLeftSelectionPosition)
207 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
209 impl.SetPopupButtons();
210 eventData->mDecoratorUpdated = true;
211 eventData->mUpdateLeftSelectionPosition = false;
214 if(eventData->mUpdateRightSelectionPosition)
216 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
218 impl.SetPopupButtons();
219 eventData->mDecoratorUpdated = true;
220 eventData->mUpdateRightSelectionPosition = false;
223 if(eventData->mUpdateHighlightBox)
225 impl.RepositionSelectionHandles();
227 eventData->mUpdateLeftSelectionPosition = false;
228 eventData->mUpdateRightSelectionPosition = false;
229 eventData->mUpdateHighlightBox = false;
230 eventData->mIsLeftHandleSelected = false;
231 eventData->mIsRightHandleSelected = false;
234 eventData->mScrollAfterUpdatePosition = false;
237 if(eventData->mUpdateInputStyle)
239 // Keep a copy of the current input style.
240 InputStyle currentInputStyle;
241 currentInputStyle.Copy(eventData->mInputStyle);
243 // Set the default style first.
244 impl.RetrieveDefaultInputStyle(eventData->mInputStyle);
246 // Get the character index from the cursor index.
247 const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u;
249 // Retrieve the style from the style runs stored in the logical model.
250 impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle);
252 // Compare if the input style has changed.
253 const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle);
255 if(hasInputStyleChanged)
257 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle);
258 // Queue the input style changed signal.
259 eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
262 eventData->mUpdateInputStyle = false;
265 eventData->mEventQueue.clear();
267 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n");
269 const bool decoratorUpdated = eventData->mDecoratorUpdated;
270 eventData->mDecoratorUpdated = false;
272 return decoratorUpdated;
275 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
277 if(NULL == impl.mEventData || !impl.IsShowingRealText())
279 // Nothing to do if there is no text input.
283 int keyCode = event.p1.mInt;
284 bool isShiftModifier = event.p2.mBool;
285 EventData& eventData = *impl.mEventData;
286 ModelPtr& model = impl.mModel;
287 LogicalModelPtr& logicalModel = model->mLogicalModel;
288 VisualModelPtr& visualModel = model->mVisualModel;
289 uint32_t oldSelStart = eventData.mLeftSelectionPosition;
290 uint32_t oldSelEnd = eventData.mRightSelectionPosition;
292 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
293 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
295 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
297 if(primaryCursorPosition > 0u)
299 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
301 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
305 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
309 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
311 if(logicalModel->mText.Count() > primaryCursorPosition)
313 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
315 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
319 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
323 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
325 // Ignore Shift-Up for text selection for now.
327 // Get first the line index of the current cursor position index.
328 CharacterIndex characterIndex = 0u;
330 if(primaryCursorPosition > 0u)
332 characterIndex = primaryCursorPosition - 1u;
335 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
336 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
338 // Retrieve the cursor position info.
339 CursorInfo cursorInfo;
340 impl.GetCursorPosition(primaryCursorPosition,
343 // Get the line above.
344 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
346 // Get the next hit 'y' point.
347 const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
349 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
350 bool matchedCharacter = false;
351 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
354 eventData.mCursorHookPositionX,
356 CharacterHitTest::TAP,
359 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
361 // Ignore Shift-Down for text selection for now.
363 // Get first the line index of the current cursor position index.
364 CharacterIndex characterIndex = 0u;
366 if(primaryCursorPosition > 0u)
368 characterIndex = primaryCursorPosition - 1u;
371 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
373 if(lineIndex + 1u < visualModel->mLines.Count())
375 // Retrieve the cursor position info.
376 CursorInfo cursorInfo;
377 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
379 // Get the line below.
380 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
382 // Get the next hit 'y' point.
383 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
385 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
386 bool matchedCharacter = false;
387 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
390 eventData.mCursorHookPositionX,
392 CharacterHitTest::TAP,
397 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
399 // Update selection position after moving the cursor
400 eventData.mLeftSelectionPosition = primaryCursorPosition;
401 eventData.mRightSelectionPosition = primaryCursorPosition;
403 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
405 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
409 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
411 // Handle text selection
412 bool selecting = false;
414 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
416 // Shift-Left/Right to select the text
417 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
418 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
420 eventData.mRightSelectionPosition += cursorPositionDelta;
422 if(impl.mSelectableControlInterface != nullptr)
424 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
429 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
431 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
437 // Notify the cursor position to the InputMethodContext.
438 if(eventData.mInputMethodContext)
440 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
441 eventData.mInputMethodContext.NotifyCursorPosition();
444 impl.ChangeState(EventData::SELECTING);
446 eventData.mUpdateLeftSelectionPosition = true;
447 eventData.mUpdateRightSelectionPosition = true;
448 eventData.mUpdateGrabHandlePosition = true;
449 eventData.mUpdateHighlightBox = true;
451 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
452 if(eventData.mGrabHandlePopupEnabled)
454 eventData.mDecorator->SetPopupActive(false);
460 // Handle normal cursor move
461 impl.ChangeState(EventData::EDITING);
462 eventData.mUpdateCursorPosition = true;
465 eventData.mUpdateInputStyle = true;
466 eventData.mScrollAfterUpdatePosition = true;
469 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
473 const unsigned int tapCount = event.p1.mUint;
474 EventData& eventData = *impl.mEventData;
475 ModelPtr& model = impl.mModel;
476 LogicalModelPtr& logicalModel = model->mLogicalModel;
477 VisualModelPtr& visualModel = model->mVisualModel;
481 if(impl.IsShowingRealText())
483 // Convert from control's coords to text's coords.
484 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
485 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
486 uint32_t oldSelStart = eventData.mLeftSelectionPosition;
487 uint32_t oldSelEnd = eventData.mRightSelectionPosition;
489 // Keep the tap 'x' position. Used to move the cursor.
490 eventData.mCursorHookPositionX = xPosition;
492 // Whether to touch point hits on a glyph.
493 bool matchedCharacter = false;
494 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
499 CharacterHitTest::TAP,
502 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
504 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition);
507 // When the cursor position is changing, delay cursor blinking
508 eventData.mDecorator->DelayCursorBlink();
512 eventData.mPrimaryCursorPosition = 0u;
515 // Update selection position after tapping
516 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
517 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
519 eventData.mUpdateCursorPosition = true;
520 eventData.mUpdateGrabHandlePosition = true;
521 eventData.mScrollAfterUpdatePosition = true;
522 eventData.mUpdateInputStyle = true;
524 // Notify the cursor position to the InputMethodContext.
525 if(eventData.mInputMethodContext)
527 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
528 eventData.mInputMethodContext.NotifyCursorPosition();
531 else if(2u == tapCount)
533 if(eventData.mSelectionEnabled)
535 // Convert from control's coords to text's coords.
536 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
537 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
539 // Calculates the logical position from the x,y coords.
540 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
546 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
550 EventData& eventData = *impl.mEventData;
551 DecoratorPtr& decorator = eventData.mDecorator;
553 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
554 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
556 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
558 // Nothing to do if scrolling is not enabled.
562 const GestureState state = static_cast<GestureState>(event.p1.mInt);
565 case GestureState::STARTED:
567 // Will remove the cursor, handles or text's popup, ...
568 impl.ChangeState(EventData::TEXT_PANNING);
571 case GestureState::CONTINUING:
573 ModelPtr& model = impl.mModel;
575 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
576 Vector2& scrollPosition = model->mScrollPosition;
577 const Vector2 currentScroll = scrollPosition;
579 if(isHorizontalScrollEnabled)
581 const float displacementX = event.p2.mFloat;
582 scrollPosition.x += displacementX;
584 impl.ClampHorizontalScroll(layoutSize);
587 if(isVerticalScrollEnabled)
589 const float displacementY = event.p3.mFloat;
590 scrollPosition.y += displacementY;
592 impl.ClampVerticalScroll(layoutSize);
595 decorator->UpdatePositions(scrollPosition - currentScroll);
598 case GestureState::FINISHED:
599 case GestureState::CANCELLED: // FALLTHROUGH
601 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
602 impl.ChangeState(eventData.mPreviousState);
611 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
613 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
617 EventData& eventData = *impl.mEventData;
619 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
621 impl.ChangeState(EventData::EDITING_WITH_POPUP);
622 eventData.mDecoratorUpdated = true;
623 eventData.mUpdateInputStyle = true;
627 if(eventData.mSelectionEnabled)
629 ModelPtr& model = impl.mModel;
631 // Convert from control's coords to text's coords.
632 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
633 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
635 // Calculates the logical position from the x,y coords.
636 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
642 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
646 const unsigned int state = event.p1.mUint;
647 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
648 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
650 if(HANDLE_PRESSED == state)
652 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
653 } // end ( HANDLE_PRESSED == state )
654 else if((HANDLE_RELEASED == state) ||
657 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
658 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
659 else if(HANDLE_SCROLLING == state)
661 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
662 } // end ( HANDLE_SCROLLING == state )
666 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
668 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
670 ModelPtr& model = impl.mModel;
671 const Vector2& scrollPosition = model->mScrollPosition;
673 // Convert from control's coords to text's coords.
674 const float xPosition = event.p2.mFloat - scrollPosition.x;
675 const float yPosition = event.p3.mFloat - scrollPosition.y;
677 // Calculates the logical position from the x,y coords.
678 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
682 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
684 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
688 EventData& eventData = *impl.mEventData;
689 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
691 ModelPtr& model = impl.mModel;
692 const Vector2& scrollPosition = model->mScrollPosition;
694 // Calculates the logical position from the start.
695 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
696 0.f - scrollPosition.y,
697 Controller::NoTextTap::HIGHLIGHT);
699 uint32_t oldStart = eventData.mLeftSelectionPosition;
700 uint32_t oldEnd = eventData.mRightSelectionPosition;
702 eventData.mLeftSelectionPosition = 0u;
703 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
705 if(impl.mSelectableControlInterface != nullptr)
707 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
713 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
715 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
719 EventData& eventData = *impl.mEventData;
720 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
722 uint32_t oldStart = eventData.mLeftSelectionPosition;
723 uint32_t oldEnd = eventData.mRightSelectionPosition;
725 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
726 impl.ChangeState(EventData::EDITING);
727 eventData.mUpdateCursorPosition = true;
728 eventData.mUpdateInputStyle = true;
729 eventData.mScrollAfterUpdatePosition = true;
731 if(impl.mSelectableControlInterface != nullptr)
733 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
739 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
741 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
743 ModelPtr& model = impl.mModel;
744 const Vector2& scrollPosition = model->mScrollPosition;
746 // Calculate the selection index.
747 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
748 const uint32_t start = std::min(event.p2.mUint, length);
749 const uint32_t end = std::min(event.p3.mUint, length);
753 uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
754 uint32_t oldEnd = impl.mEventData->mRightSelectionPosition;
756 // Calculates the logical position from the x,y coords.
757 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
759 impl.mEventData->mLeftSelectionPosition = start;
760 impl.mEventData->mRightSelectionPosition = end;
762 if(impl.mSelectableControlInterface != nullptr)
764 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
770 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
772 ModelPtr& model = impl.mModel;
773 const Vector2& scrollPosition = model->mScrollPosition;
775 // Convert from decorator's coords to text's coords.
776 const float xPosition = event.p2.mFloat - scrollPosition.x;
777 const float yPosition = event.p3.mFloat - scrollPosition.y;
779 // Need to calculate the handle's new position.
780 bool matchedCharacter = false;
781 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
782 model->mLogicalModel,
786 CharacterHitTest::SCROLL,
789 EventData& eventData = *impl.mEventData;
790 uint32_t oldStart = eventData.mLeftSelectionPosition;
791 uint32_t oldEnd = eventData.mRightSelectionPosition;
793 if(Event::GRAB_HANDLE_EVENT == event.type)
795 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
797 if(handleNewPosition != eventData.mPrimaryCursorPosition)
799 // Updates the cursor position if the handle's new position is different than the current one.
800 eventData.mUpdateCursorPosition = true;
801 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
802 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
803 eventData.mPrimaryCursorPosition = handleNewPosition;
806 // 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.
807 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
809 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
811 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
813 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
814 (handleNewPosition != eventData.mRightSelectionPosition))
816 // Updates the highlight box if the handle's new position is different than the current one.
817 eventData.mUpdateHighlightBox = true;
818 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
819 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
820 eventData.mLeftSelectionPosition = handleNewPosition;
823 // 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.
824 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
826 // Will define the order to scroll the text to match the handle position.
827 eventData.mIsLeftHandleSelected = true;
828 eventData.mIsRightHandleSelected = false;
830 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
832 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
834 if((handleNewPosition != eventData.mRightSelectionPosition) &&
835 (handleNewPosition != eventData.mLeftSelectionPosition))
837 // Updates the highlight box if the handle's new position is different than the current one.
838 eventData.mUpdateHighlightBox = true;
839 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
840 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
841 eventData.mRightSelectionPosition = handleNewPosition;
844 // 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.
845 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
847 // Will define the order to scroll the text to match the handle position.
848 eventData.mIsLeftHandleSelected = false;
849 eventData.mIsRightHandleSelected = true;
852 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
854 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
858 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
860 CharacterIndex handlePosition = 0u;
861 if(handleStopScrolling || isSmoothHandlePanEnabled)
863 ModelPtr& model = impl.mModel;
864 const Vector2& scrollPosition = model->mScrollPosition;
866 // Convert from decorator's coords to text's coords.
867 const float xPosition = event.p2.mFloat - scrollPosition.x;
868 const float yPosition = event.p3.mFloat - scrollPosition.y;
870 bool matchedCharacter = false;
871 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
872 model->mLogicalModel,
876 CharacterHitTest::SCROLL,
880 EventData& eventData = *impl.mEventData;
881 uint32_t oldStart = eventData.mLeftSelectionPosition;
882 uint32_t oldEnd = eventData.mRightSelectionPosition;
884 if(Event::GRAB_HANDLE_EVENT == event.type)
886 eventData.mUpdateCursorPosition = true;
887 eventData.mUpdateGrabHandlePosition = true;
888 eventData.mUpdateInputStyle = true;
890 if(!impl.IsClipboardEmpty())
892 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
895 if(handleStopScrolling || isSmoothHandlePanEnabled)
897 eventData.mScrollAfterUpdatePosition = true;
898 eventData.mPrimaryCursorPosition = handlePosition;
901 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
903 impl.ChangeState(EventData::SELECTING);
905 eventData.mUpdateHighlightBox = true;
906 eventData.mUpdateLeftSelectionPosition = true;
907 eventData.mUpdateRightSelectionPosition = true;
909 if(handleStopScrolling || isSmoothHandlePanEnabled)
911 eventData.mScrollAfterUpdatePosition = true;
913 if((handlePosition != eventData.mRightSelectionPosition) &&
914 (handlePosition != eventData.mLeftSelectionPosition))
916 eventData.mLeftSelectionPosition = handlePosition;
920 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
922 impl.ChangeState(EventData::SELECTING);
924 eventData.mUpdateHighlightBox = true;
925 eventData.mUpdateRightSelectionPosition = true;
926 eventData.mUpdateLeftSelectionPosition = true;
928 if(handleStopScrolling || isSmoothHandlePanEnabled)
930 eventData.mScrollAfterUpdatePosition = true;
931 if((handlePosition != eventData.mRightSelectionPosition) &&
932 (handlePosition != eventData.mLeftSelectionPosition))
934 eventData.mRightSelectionPosition = handlePosition;
939 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
941 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
944 eventData.mDecoratorUpdated = true;
947 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
949 ModelPtr& model = impl.mModel;
950 Vector2& scrollPosition = model->mScrollPosition;
951 VisualModelPtr& visualModel = model->mVisualModel;
953 const float xSpeed = event.p2.mFloat;
954 const float ySpeed = event.p3.mFloat;
955 const Vector2& layoutSize = visualModel->GetLayoutSize();
956 const Vector2 currentScrollPosition = scrollPosition;
958 scrollPosition.x += xSpeed;
959 scrollPosition.y += ySpeed;
961 impl.ClampHorizontalScroll(layoutSize);
962 impl.ClampVerticalScroll(layoutSize);
964 EventData& eventData = *impl.mEventData;
965 DecoratorPtr& decorator = eventData.mDecorator;
967 bool endOfScroll = false;
968 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
970 // Notify the decorator there is no more text to scroll.
971 // The decorator won't send more scroll events.
972 decorator->NotifyEndOfScroll();
973 // Still need to set the position of the handle.
977 // Set the position of the handle.
978 const bool scrollRightDirection = xSpeed > 0.f;
979 const bool scrollBottomDirection = ySpeed > 0.f;
980 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
981 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
983 if(Event::GRAB_HANDLE_EVENT == event.type)
985 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
987 // Get the grab handle position in decorator coords.
988 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
990 if(decorator->IsHorizontalScrollEnabled())
992 // Position the grag handle close to either the left or right edge.
993 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
996 if(decorator->IsVerticalScrollEnabled())
998 position.x = eventData.mCursorHookPositionX;
1000 // Position the grag handle close to either the top or bottom edge.
1001 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1004 // Get the new handle position.
1005 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1006 bool matchedCharacter = false;
1007 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1008 impl.mModel->mLogicalModel,
1010 position.x - scrollPosition.x,
1011 position.y - scrollPosition.y,
1012 CharacterHitTest::SCROLL,
1015 if(eventData.mPrimaryCursorPosition != handlePosition)
1017 eventData.mUpdateCursorPosition = true;
1018 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1019 eventData.mScrollAfterUpdatePosition = true;
1020 eventData.mPrimaryCursorPosition = handlePosition;
1022 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
1024 // Updates the decorator if the soft handle panning is enabled.
1025 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
1027 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
1029 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
1031 // Get the selection handle position in decorator coords.
1032 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
1034 if(decorator->IsHorizontalScrollEnabled())
1036 // Position the selection handle close to either the left or right edge.
1037 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1040 if(decorator->IsVerticalScrollEnabled())
1042 position.x = eventData.mCursorHookPositionX;
1044 // Position the grag handle close to either the top or bottom edge.
1045 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1048 // Get the new handle position.
1049 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1050 bool matchedCharacter = false;
1051 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1052 impl.mModel->mLogicalModel,
1054 position.x - scrollPosition.x,
1055 position.y - scrollPosition.y,
1056 CharacterHitTest::SCROLL,
1058 uint32_t oldStart = eventData.mLeftSelectionPosition;
1059 uint32_t oldEnd = eventData.mRightSelectionPosition;
1061 if(leftSelectionHandleEvent)
1063 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1065 if(differentHandles || endOfScroll)
1067 eventData.mUpdateHighlightBox = true;
1068 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1069 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1070 eventData.mLeftSelectionPosition = handlePosition;
1075 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1076 if(differentHandles || endOfScroll)
1078 eventData.mUpdateHighlightBox = true;
1079 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1080 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1081 eventData.mRightSelectionPosition = handlePosition;
1085 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1087 impl.RepositionSelectionHandles();
1089 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1091 if(impl.mSelectableControlInterface != nullptr)
1093 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
1097 eventData.mDecoratorUpdated = true;
1102 } // namespace Toolkit