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;
290 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
291 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
293 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
295 if(primaryCursorPosition > 0u)
297 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
299 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
303 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
307 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
309 if(logicalModel->mText.Count() > primaryCursorPosition)
311 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
313 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
317 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
321 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
323 // Ignore Shift-Up for text selection for now.
325 // Get first the line index of the current cursor position index.
326 CharacterIndex characterIndex = 0u;
328 if(primaryCursorPosition > 0u)
330 characterIndex = primaryCursorPosition - 1u;
333 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
334 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
336 // Retrieve the cursor position info.
337 CursorInfo cursorInfo;
338 impl.GetCursorPosition(primaryCursorPosition,
341 // Get the line above.
342 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
344 // Get the next hit 'y' point.
345 const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
347 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
348 bool matchedCharacter = false;
349 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
352 eventData.mCursorHookPositionX,
354 CharacterHitTest::TAP,
357 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
359 // Ignore Shift-Down for text selection for now.
361 // Get first the line index of the current cursor position index.
362 CharacterIndex characterIndex = 0u;
364 if(primaryCursorPosition > 0u)
366 characterIndex = primaryCursorPosition - 1u;
369 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
371 if(lineIndex + 1u < visualModel->mLines.Count())
373 // Retrieve the cursor position info.
374 CursorInfo cursorInfo;
375 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
377 // Get the line below.
378 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
380 // Get the next hit 'y' point.
381 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
383 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
384 bool matchedCharacter = false;
385 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
388 eventData.mCursorHookPositionX,
390 CharacterHitTest::TAP,
395 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
397 // Update selection position after moving the cursor
398 eventData.mLeftSelectionPosition = primaryCursorPosition;
399 eventData.mRightSelectionPosition = primaryCursorPosition;
402 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
404 // Handle text selection
405 bool selecting = false;
407 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
409 // Shift-Left/Right to select the text
410 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
411 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
413 eventData.mRightSelectionPosition += cursorPositionDelta;
417 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
419 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
425 // Notify the cursor position to the InputMethodContext.
426 if(eventData.mInputMethodContext)
428 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
429 eventData.mInputMethodContext.NotifyCursorPosition();
432 impl.ChangeState(EventData::SELECTING);
434 eventData.mUpdateLeftSelectionPosition = true;
435 eventData.mUpdateRightSelectionPosition = true;
436 eventData.mUpdateGrabHandlePosition = true;
437 eventData.mUpdateHighlightBox = true;
439 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
440 if(eventData.mGrabHandlePopupEnabled)
442 eventData.mDecorator->SetPopupActive(false);
448 // Handle normal cursor move
449 impl.ChangeState(EventData::EDITING);
450 eventData.mUpdateCursorPosition = true;
453 eventData.mUpdateInputStyle = true;
454 eventData.mScrollAfterUpdatePosition = true;
457 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
461 const unsigned int tapCount = event.p1.mUint;
462 EventData& eventData = *impl.mEventData;
463 ModelPtr& model = impl.mModel;
464 LogicalModelPtr& logicalModel = model->mLogicalModel;
465 VisualModelPtr& visualModel = model->mVisualModel;
469 if(impl.IsShowingRealText())
471 // Convert from control's coords to text's coords.
472 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
473 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
475 // Keep the tap 'x' position. Used to move the cursor.
476 eventData.mCursorHookPositionX = xPosition;
478 // Whether to touch point hits on a glyph.
479 bool matchedCharacter = false;
480 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
485 CharacterHitTest::TAP,
488 // When the cursor position is changing, delay cursor blinking
489 eventData.mDecorator->DelayCursorBlink();
493 eventData.mPrimaryCursorPosition = 0u;
496 // Update selection position after tapping
497 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
498 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
500 eventData.mUpdateCursorPosition = true;
501 eventData.mUpdateGrabHandlePosition = true;
502 eventData.mScrollAfterUpdatePosition = true;
503 eventData.mUpdateInputStyle = true;
505 // Notify the cursor position to the InputMethodContext.
506 if(eventData.mInputMethodContext)
508 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
509 eventData.mInputMethodContext.NotifyCursorPosition();
512 else if(2u == tapCount)
514 if(eventData.mSelectionEnabled)
516 // Convert from control's coords to text's coords.
517 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
518 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
520 // Calculates the logical position from the x,y coords.
521 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
527 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
531 EventData& eventData = *impl.mEventData;
532 DecoratorPtr& decorator = eventData.mDecorator;
534 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
535 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
537 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
539 // Nothing to do if scrolling is not enabled.
543 const GestureState state = static_cast<GestureState>(event.p1.mInt);
546 case GestureState::STARTED:
548 // Will remove the cursor, handles or text's popup, ...
549 impl.ChangeState(EventData::TEXT_PANNING);
552 case GestureState::CONTINUING:
554 ModelPtr& model = impl.mModel;
556 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
557 Vector2& scrollPosition = model->mScrollPosition;
558 const Vector2 currentScroll = scrollPosition;
560 if(isHorizontalScrollEnabled)
562 const float displacementX = event.p2.mFloat;
563 scrollPosition.x += displacementX;
565 impl.ClampHorizontalScroll(layoutSize);
568 if(isVerticalScrollEnabled)
570 const float displacementY = event.p3.mFloat;
571 scrollPosition.y += displacementY;
573 impl.ClampVerticalScroll(layoutSize);
576 decorator->UpdatePositions(scrollPosition - currentScroll);
579 case GestureState::FINISHED:
580 case GestureState::CANCELLED: // FALLTHROUGH
582 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
583 impl.ChangeState(eventData.mPreviousState);
592 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
594 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
598 EventData& eventData = *impl.mEventData;
600 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
602 impl.ChangeState(EventData::EDITING_WITH_POPUP);
603 eventData.mDecoratorUpdated = true;
604 eventData.mUpdateInputStyle = true;
608 if(eventData.mSelectionEnabled)
610 ModelPtr& model = impl.mModel;
612 // Convert from control's coords to text's coords.
613 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
614 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
616 // Calculates the logical position from the x,y coords.
617 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
623 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
627 const unsigned int state = event.p1.mUint;
628 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
629 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
631 if(HANDLE_PRESSED == state)
633 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
634 } // end ( HANDLE_PRESSED == state )
635 else if((HANDLE_RELEASED == state) ||
638 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
639 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
640 else if(HANDLE_SCROLLING == state)
642 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
643 } // end ( HANDLE_SCROLLING == state )
647 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
649 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
651 ModelPtr& model = impl.mModel;
652 const Vector2& scrollPosition = model->mScrollPosition;
654 // Convert from control's coords to text's coords.
655 const float xPosition = event.p2.mFloat - scrollPosition.x;
656 const float yPosition = event.p3.mFloat - scrollPosition.y;
658 // Calculates the logical position from the x,y coords.
659 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
663 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
665 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
669 EventData& eventData = *impl.mEventData;
670 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
672 ModelPtr& model = impl.mModel;
673 const Vector2& scrollPosition = model->mScrollPosition;
675 // Calculates the logical position from the start.
676 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
677 0.f - scrollPosition.y,
678 Controller::NoTextTap::HIGHLIGHT);
680 eventData.mLeftSelectionPosition = 0u;
681 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
686 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
688 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
692 EventData& eventData = *impl.mEventData;
693 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
695 eventData.mPrimaryCursorPosition = 0u;
696 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
697 impl.ChangeState(EventData::INACTIVE);
698 eventData.mUpdateCursorPosition = true;
699 eventData.mUpdateInputStyle = true;
700 eventData.mScrollAfterUpdatePosition = true;
705 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
707 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
709 ModelPtr& model = impl.mModel;
710 const Vector2& scrollPosition = model->mScrollPosition;
712 // Calculate the selection index.
713 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
714 const uint32_t start = std::min(event.p2.mUint, length);
715 const uint32_t end = std::min(event.p3.mUint, length);
719 // Calculates the logical position from the x,y coords.
720 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
722 impl.mEventData->mLeftSelectionPosition = start;
723 impl.mEventData->mRightSelectionPosition = end;
728 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
730 ModelPtr& model = impl.mModel;
731 const Vector2& scrollPosition = model->mScrollPosition;
733 // Convert from decorator's coords to text's coords.
734 const float xPosition = event.p2.mFloat - scrollPosition.x;
735 const float yPosition = event.p3.mFloat - scrollPosition.y;
737 // Need to calculate the handle's new position.
738 bool matchedCharacter = false;
739 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
740 model->mLogicalModel,
744 CharacterHitTest::SCROLL,
747 EventData& eventData = *impl.mEventData;
749 if(Event::GRAB_HANDLE_EVENT == event.type)
751 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
753 if(handleNewPosition != eventData.mPrimaryCursorPosition)
755 // Updates the cursor position if the handle's new position is different than the current one.
756 eventData.mUpdateCursorPosition = true;
757 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
758 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
759 eventData.mPrimaryCursorPosition = handleNewPosition;
762 // 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.
763 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
765 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
767 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
769 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
770 (handleNewPosition != eventData.mRightSelectionPosition))
772 // Updates the highlight box if the handle's new position is different than the current one.
773 eventData.mUpdateHighlightBox = true;
774 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
775 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
776 eventData.mLeftSelectionPosition = handleNewPosition;
779 // 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.
780 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
782 // Will define the order to scroll the text to match the handle position.
783 eventData.mIsLeftHandleSelected = true;
784 eventData.mIsRightHandleSelected = false;
786 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
788 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
790 if((handleNewPosition != eventData.mRightSelectionPosition) &&
791 (handleNewPosition != eventData.mLeftSelectionPosition))
793 // Updates the highlight box if the handle's new position is different than the current one.
794 eventData.mUpdateHighlightBox = true;
795 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
796 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
797 eventData.mRightSelectionPosition = handleNewPosition;
800 // 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.
801 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
803 // Will define the order to scroll the text to match the handle position.
804 eventData.mIsLeftHandleSelected = false;
805 eventData.mIsRightHandleSelected = true;
809 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
811 CharacterIndex handlePosition = 0u;
812 if(handleStopScrolling || isSmoothHandlePanEnabled)
814 ModelPtr& model = impl.mModel;
815 const Vector2& scrollPosition = model->mScrollPosition;
817 // Convert from decorator's coords to text's coords.
818 const float xPosition = event.p2.mFloat - scrollPosition.x;
819 const float yPosition = event.p3.mFloat - scrollPosition.y;
821 bool matchedCharacter = false;
822 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
823 model->mLogicalModel,
827 CharacterHitTest::SCROLL,
831 EventData& eventData = *impl.mEventData;
833 if(Event::GRAB_HANDLE_EVENT == event.type)
835 eventData.mUpdateCursorPosition = true;
836 eventData.mUpdateGrabHandlePosition = true;
837 eventData.mUpdateInputStyle = true;
839 if(!impl.IsClipboardEmpty())
841 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
844 if(handleStopScrolling || isSmoothHandlePanEnabled)
846 eventData.mScrollAfterUpdatePosition = true;
847 eventData.mPrimaryCursorPosition = handlePosition;
850 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
852 impl.ChangeState(EventData::SELECTING);
854 eventData.mUpdateHighlightBox = true;
855 eventData.mUpdateLeftSelectionPosition = true;
856 eventData.mUpdateRightSelectionPosition = true;
858 if(handleStopScrolling || isSmoothHandlePanEnabled)
860 eventData.mScrollAfterUpdatePosition = true;
862 if((handlePosition != eventData.mRightSelectionPosition) &&
863 (handlePosition != eventData.mLeftSelectionPosition))
865 eventData.mLeftSelectionPosition = handlePosition;
869 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
871 impl.ChangeState(EventData::SELECTING);
873 eventData.mUpdateHighlightBox = true;
874 eventData.mUpdateRightSelectionPosition = true;
875 eventData.mUpdateLeftSelectionPosition = true;
877 if(handleStopScrolling || isSmoothHandlePanEnabled)
879 eventData.mScrollAfterUpdatePosition = true;
880 if((handlePosition != eventData.mRightSelectionPosition) &&
881 (handlePosition != eventData.mLeftSelectionPosition))
883 eventData.mRightSelectionPosition = handlePosition;
888 eventData.mDecoratorUpdated = true;
891 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
893 ModelPtr& model = impl.mModel;
894 Vector2& scrollPosition = model->mScrollPosition;
895 VisualModelPtr& visualModel = model->mVisualModel;
897 const float xSpeed = event.p2.mFloat;
898 const float ySpeed = event.p3.mFloat;
899 const Vector2& layoutSize = visualModel->GetLayoutSize();
900 const Vector2 currentScrollPosition = scrollPosition;
902 scrollPosition.x += xSpeed;
903 scrollPosition.y += ySpeed;
905 impl.ClampHorizontalScroll(layoutSize);
906 impl.ClampVerticalScroll(layoutSize);
908 EventData& eventData = *impl.mEventData;
909 DecoratorPtr& decorator = eventData.mDecorator;
911 bool endOfScroll = false;
912 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
914 // Notify the decorator there is no more text to scroll.
915 // The decorator won't send more scroll events.
916 decorator->NotifyEndOfScroll();
917 // Still need to set the position of the handle.
921 // Set the position of the handle.
922 const bool scrollRightDirection = xSpeed > 0.f;
923 const bool scrollBottomDirection = ySpeed > 0.f;
924 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
925 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
927 if(Event::GRAB_HANDLE_EVENT == event.type)
929 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
931 // Get the grab handle position in decorator coords.
932 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
934 if(decorator->IsHorizontalScrollEnabled())
936 // Position the grag handle close to either the left or right edge.
937 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
940 if(decorator->IsVerticalScrollEnabled())
942 position.x = eventData.mCursorHookPositionX;
944 // Position the grag handle close to either the top or bottom edge.
945 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
948 // Get the new handle position.
949 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
950 bool matchedCharacter = false;
951 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
952 impl.mModel->mLogicalModel,
954 position.x - scrollPosition.x,
955 position.y - scrollPosition.y,
956 CharacterHitTest::SCROLL,
959 if(eventData.mPrimaryCursorPosition != handlePosition)
961 eventData.mUpdateCursorPosition = true;
962 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
963 eventData.mScrollAfterUpdatePosition = true;
964 eventData.mPrimaryCursorPosition = handlePosition;
966 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
968 // Updates the decorator if the soft handle panning is enabled.
969 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
971 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
973 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
975 // Get the selection handle position in decorator coords.
976 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
978 if(decorator->IsHorizontalScrollEnabled())
980 // Position the selection handle close to either the left or right edge.
981 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
984 if(decorator->IsVerticalScrollEnabled())
986 position.x = eventData.mCursorHookPositionX;
988 // Position the grag handle close to either the top or bottom edge.
989 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
992 // Get the new handle position.
993 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
994 bool matchedCharacter = false;
995 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
996 impl.mModel->mLogicalModel,
998 position.x - scrollPosition.x,
999 position.y - scrollPosition.y,
1000 CharacterHitTest::SCROLL,
1003 if(leftSelectionHandleEvent)
1005 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1007 if(differentHandles || endOfScroll)
1009 eventData.mUpdateHighlightBox = true;
1010 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1011 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1012 eventData.mLeftSelectionPosition = handlePosition;
1017 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1018 if(differentHandles || endOfScroll)
1020 eventData.mUpdateHighlightBox = true;
1021 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1022 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1023 eventData.mRightSelectionPosition = handlePosition;
1027 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1029 impl.RepositionSelectionHandles();
1031 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1034 eventData.mDecoratorUpdated = true;
1039 } // namespace Toolkit