2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/adaptor-framework/key.h>
26 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
27 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
45 bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
47 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n");
49 EventData*& eventData = impl.mEventData;
52 // Nothing to do if there is no text input.
53 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n");
57 if(eventData->mDecorator)
59 for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
60 iter != eventData->mEventQueue.end();
65 case Event::CURSOR_KEY_EVENT:
67 OnCursorKeyEvent(impl, *iter);
70 case Event::TAP_EVENT:
72 OnTapEvent(impl, *iter);
75 case Event::LONG_PRESS_EVENT:
77 OnLongPressEvent(impl, *iter);
80 case Event::PAN_EVENT:
82 OnPanEvent(impl, *iter);
85 case Event::GRAB_HANDLE_EVENT:
86 case Event::LEFT_SELECTION_HANDLE_EVENT:
87 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
89 OnHandleEvent(impl, *iter);
94 OnSelectEvent(impl, *iter);
97 case Event::SELECT_ALL:
99 OnSelectAllEvent(impl);
102 case Event::SELECT_NONE:
104 OnSelectNoneEvent(impl);
111 if(eventData->mUpdateCursorPosition ||
112 eventData->mUpdateHighlightBox)
114 impl.NotifyInputMethodContext();
117 // The cursor must also be repositioned after inserts into the model
118 if(eventData->mUpdateCursorPosition)
120 // Updates the cursor position and scrolls the text to make it visible.
121 CursorInfo cursorInfo;
122 // Calculate the cursor position from the new cursor index.
123 impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
125 if(nullptr != impl.mEditableControlInterface)
127 impl.mEditableControlInterface->CaretMoved(eventData->mPrimaryCursorPosition);
130 if(eventData->mUpdateCursorHookPosition)
132 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
133 eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
134 eventData->mUpdateCursorHookPosition = false;
137 // Scroll first the text after delete ...
138 if(eventData->mScrollAfterDelete)
140 impl.ScrollTextToMatchCursor(cursorInfo);
143 // ... then, text can be scrolled to make the cursor visible.
144 if(eventData->mScrollAfterUpdatePosition)
146 const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset);
147 impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight);
149 eventData->mScrollAfterUpdatePosition = false;
150 eventData->mScrollAfterDelete = false;
152 impl.UpdateCursorPosition(cursorInfo);
154 eventData->mDecoratorUpdated = true;
155 eventData->mUpdateCursorPosition = false;
156 eventData->mUpdateGrabHandlePosition = false;
160 CursorInfo leftHandleInfo;
161 CursorInfo rightHandleInfo;
163 if(eventData->mUpdateHighlightBox)
165 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
167 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
169 if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition))
171 if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected)
173 CursorInfo& infoLeft = leftHandleInfo;
175 const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset);
176 impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight);
178 CursorInfo& infoRight = rightHandleInfo;
180 const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset);
181 impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight);
185 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
187 const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset);
188 impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight);
193 if(eventData->mUpdateLeftSelectionPosition)
195 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
197 impl.SetPopupButtons();
198 eventData->mDecoratorUpdated = true;
199 eventData->mUpdateLeftSelectionPosition = false;
202 if(eventData->mUpdateRightSelectionPosition)
204 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
206 impl.SetPopupButtons();
207 eventData->mDecoratorUpdated = true;
208 eventData->mUpdateRightSelectionPosition = false;
211 if(eventData->mUpdateHighlightBox)
213 impl.RepositionSelectionHandles();
215 eventData->mUpdateLeftSelectionPosition = false;
216 eventData->mUpdateRightSelectionPosition = false;
217 eventData->mUpdateHighlightBox = false;
218 eventData->mIsLeftHandleSelected = false;
219 eventData->mIsRightHandleSelected = false;
222 eventData->mScrollAfterUpdatePosition = false;
225 if(eventData->mUpdateInputStyle)
227 // Keep a copy of the current input style.
228 InputStyle currentInputStyle;
229 currentInputStyle.Copy(eventData->mInputStyle);
231 // Set the default style first.
232 impl.RetrieveDefaultInputStyle(eventData->mInputStyle);
234 // Get the character index from the cursor index.
235 const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u;
237 // Retrieve the style from the style runs stored in the logical model.
238 impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle);
240 // Compare if the input style has changed.
241 const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle);
243 if(hasInputStyleChanged)
245 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle);
246 // Queue the input style changed signal.
247 eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
250 eventData->mUpdateInputStyle = false;
253 eventData->mEventQueue.clear();
255 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n");
257 const bool decoratorUpdated = eventData->mDecoratorUpdated;
258 eventData->mDecoratorUpdated = false;
260 return decoratorUpdated;
263 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
265 if(NULL == impl.mEventData || !impl.IsShowingRealText())
267 // Nothing to do if there is no text input.
271 int keyCode = event.p1.mInt;
272 bool isShiftModifier = event.p2.mBool;
273 EventData& eventData = *impl.mEventData;
274 ModelPtr& model = impl.mModel;
275 LogicalModelPtr& logicalModel = model->mLogicalModel;
276 VisualModelPtr& visualModel = model->mVisualModel;
278 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
279 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
281 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
283 if(primaryCursorPosition > 0u)
285 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
287 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
291 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
295 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
297 if(logicalModel->mText.Count() > primaryCursorPosition)
299 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
301 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
305 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
309 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
311 // Ignore Shift-Up for text selection for now.
313 // Get first the line index of the current cursor position index.
314 CharacterIndex characterIndex = 0u;
316 if(primaryCursorPosition > 0u)
318 characterIndex = primaryCursorPosition - 1u;
321 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
322 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
324 // Retrieve the cursor position info.
325 CursorInfo cursorInfo;
326 impl.GetCursorPosition(primaryCursorPosition,
329 // Get the line above.
330 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
332 // Get the next hit 'y' point.
333 const float hitPointY = cursorInfo.lineOffset - 0.5f * (line.ascender - line.descender);
335 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
336 bool matchedCharacter = false;
337 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
340 eventData.mCursorHookPositionX,
342 CharacterHitTest::TAP,
345 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
347 // Ignore Shift-Down for text selection for now.
349 // Get first the line index of the current cursor position index.
350 CharacterIndex characterIndex = 0u;
352 if(primaryCursorPosition > 0u)
354 characterIndex = primaryCursorPosition - 1u;
357 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
359 if(lineIndex + 1u < visualModel->mLines.Count())
361 // Retrieve the cursor position info.
362 CursorInfo cursorInfo;
363 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
365 // Get the line below.
366 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
368 // Get the next hit 'y' point.
369 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * (line.ascender - line.descender);
371 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
372 bool matchedCharacter = false;
373 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
376 eventData.mCursorHookPositionX,
378 CharacterHitTest::TAP,
383 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
385 // Update selection position after moving the cursor
386 eventData.mLeftSelectionPosition = primaryCursorPosition;
387 eventData.mRightSelectionPosition = primaryCursorPosition;
390 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
392 // Handle text selection
393 bool selecting = false;
395 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
397 // Shift-Left/Right to select the text
398 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
399 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
401 eventData.mRightSelectionPosition += cursorPositionDelta;
405 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
407 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
413 // Notify the cursor position to the InputMethodContext.
414 if(eventData.mInputMethodContext)
416 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
417 eventData.mInputMethodContext.NotifyCursorPosition();
420 impl.ChangeState(EventData::SELECTING);
422 eventData.mUpdateLeftSelectionPosition = true;
423 eventData.mUpdateRightSelectionPosition = true;
424 eventData.mUpdateGrabHandlePosition = true;
425 eventData.mUpdateHighlightBox = true;
427 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
428 if(eventData.mGrabHandlePopupEnabled)
430 eventData.mDecorator->SetPopupActive(false);
436 // Handle normal cursor move
437 impl.ChangeState(EventData::EDITING);
438 eventData.mUpdateCursorPosition = true;
441 eventData.mUpdateInputStyle = true;
442 eventData.mScrollAfterUpdatePosition = true;
445 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
449 const unsigned int tapCount = event.p1.mUint;
450 EventData& eventData = *impl.mEventData;
451 ModelPtr& model = impl.mModel;
452 LogicalModelPtr& logicalModel = model->mLogicalModel;
453 VisualModelPtr& visualModel = model->mVisualModel;
457 if(impl.IsShowingRealText())
459 // Convert from control's coords to text's coords.
460 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
461 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
463 // Keep the tap 'x' position. Used to move the cursor.
464 eventData.mCursorHookPositionX = xPosition;
466 // Whether to touch point hits on a glyph.
467 bool matchedCharacter = false;
468 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
473 CharacterHitTest::TAP,
476 // When the cursor position is changing, delay cursor blinking
477 eventData.mDecorator->DelayCursorBlink();
481 eventData.mPrimaryCursorPosition = 0u;
484 // Update selection position after tapping
485 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
486 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
488 eventData.mUpdateCursorPosition = true;
489 eventData.mUpdateGrabHandlePosition = true;
490 eventData.mScrollAfterUpdatePosition = true;
491 eventData.mUpdateInputStyle = true;
493 // Notify the cursor position to the InputMethodContext.
494 if(eventData.mInputMethodContext)
496 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
497 eventData.mInputMethodContext.NotifyCursorPosition();
500 else if(2u == tapCount)
502 if(eventData.mSelectionEnabled)
504 // Convert from control's coords to text's coords.
505 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
506 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
508 // Calculates the logical position from the x,y coords.
509 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
515 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
519 EventData& eventData = *impl.mEventData;
520 DecoratorPtr& decorator = eventData.mDecorator;
522 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
523 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
525 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
527 // Nothing to do if scrolling is not enabled.
531 const GestureState state = static_cast<GestureState>(event.p1.mInt);
534 case GestureState::STARTED:
536 // Will remove the cursor, handles or text's popup, ...
537 impl.ChangeState(EventData::TEXT_PANNING);
540 case GestureState::CONTINUING:
542 ModelPtr& model = impl.mModel;
544 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
545 Vector2& scrollPosition = model->mScrollPosition;
546 const Vector2 currentScroll = scrollPosition;
548 if(isHorizontalScrollEnabled)
550 const float displacementX = event.p2.mFloat;
551 scrollPosition.x += displacementX;
553 impl.ClampHorizontalScroll(layoutSize);
556 if(isVerticalScrollEnabled)
558 const float displacementY = event.p3.mFloat;
559 scrollPosition.y += displacementY;
561 impl.ClampVerticalScroll(layoutSize);
564 decorator->UpdatePositions(scrollPosition - currentScroll);
567 case GestureState::FINISHED:
568 case GestureState::CANCELLED: // FALLTHROUGH
570 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
571 impl.ChangeState(eventData.mPreviousState);
580 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
582 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
586 EventData& eventData = *impl.mEventData;
588 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
590 impl.ChangeState(EventData::EDITING_WITH_POPUP);
591 eventData.mDecoratorUpdated = true;
592 eventData.mUpdateInputStyle = true;
596 if(eventData.mSelectionEnabled)
598 ModelPtr& model = impl.mModel;
600 // Convert from control's coords to text's coords.
601 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
602 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
604 // Calculates the logical position from the x,y coords.
605 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
611 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
615 const unsigned int state = event.p1.mUint;
616 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
617 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
619 if(HANDLE_PRESSED == state)
621 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
622 } // end ( HANDLE_PRESSED == state )
623 else if((HANDLE_RELEASED == state) ||
626 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
627 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
628 else if(HANDLE_SCROLLING == state)
630 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
631 } // end ( HANDLE_SCROLLING == state )
635 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
637 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
639 ModelPtr& model = impl.mModel;
640 const Vector2& scrollPosition = model->mScrollPosition;
642 // Convert from control's coords to text's coords.
643 const float xPosition = event.p2.mFloat - scrollPosition.x;
644 const float yPosition = event.p3.mFloat - scrollPosition.y;
646 // Calculates the logical position from the x,y coords.
647 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
651 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
653 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
657 EventData& eventData = *impl.mEventData;
658 if(eventData.mSelectionEnabled)
660 ModelPtr& model = impl.mModel;
661 const Vector2& scrollPosition = model->mScrollPosition;
663 // Calculates the logical position from the start.
664 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
665 0.f - scrollPosition.y,
666 Controller::NoTextTap::HIGHLIGHT);
668 eventData.mLeftSelectionPosition = 0u;
669 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
674 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
676 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
680 EventData& eventData = *impl.mEventData;
681 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
683 eventData.mPrimaryCursorPosition = 0u;
684 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
685 impl.ChangeState(EventData::INACTIVE);
686 eventData.mUpdateCursorPosition = true;
687 eventData.mUpdateInputStyle = true;
688 eventData.mScrollAfterUpdatePosition = true;
693 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
695 ModelPtr& model = impl.mModel;
696 const Vector2& scrollPosition = model->mScrollPosition;
698 // Convert from decorator's coords to text's coords.
699 const float xPosition = event.p2.mFloat - scrollPosition.x;
700 const float yPosition = event.p3.mFloat - scrollPosition.y;
702 // Need to calculate the handle's new position.
703 bool matchedCharacter = false;
704 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
705 model->mLogicalModel,
709 CharacterHitTest::SCROLL,
712 EventData& eventData = *impl.mEventData;
714 if(Event::GRAB_HANDLE_EVENT == event.type)
716 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
718 if(handleNewPosition != eventData.mPrimaryCursorPosition)
720 // Updates the cursor position if the handle's new position is different than the current one.
721 eventData.mUpdateCursorPosition = true;
722 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
723 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
724 eventData.mPrimaryCursorPosition = handleNewPosition;
727 // 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.
728 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
730 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
732 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
734 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
735 (handleNewPosition != eventData.mRightSelectionPosition))
737 // Updates the highlight box if the handle's new position is different than the current one.
738 eventData.mUpdateHighlightBox = true;
739 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
740 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
741 eventData.mLeftSelectionPosition = handleNewPosition;
744 // 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.
745 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
747 // Will define the order to scroll the text to match the handle position.
748 eventData.mIsLeftHandleSelected = true;
749 eventData.mIsRightHandleSelected = false;
751 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
753 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
755 if((handleNewPosition != eventData.mRightSelectionPosition) &&
756 (handleNewPosition != eventData.mLeftSelectionPosition))
758 // Updates the highlight box if the handle's new position is different than the current one.
759 eventData.mUpdateHighlightBox = true;
760 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
761 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
762 eventData.mRightSelectionPosition = handleNewPosition;
765 // 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.
766 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
768 // Will define the order to scroll the text to match the handle position.
769 eventData.mIsLeftHandleSelected = false;
770 eventData.mIsRightHandleSelected = true;
774 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
776 CharacterIndex handlePosition = 0u;
777 if(handleStopScrolling || isSmoothHandlePanEnabled)
779 ModelPtr& model = impl.mModel;
780 const Vector2& scrollPosition = model->mScrollPosition;
782 // Convert from decorator's coords to text's coords.
783 const float xPosition = event.p2.mFloat - scrollPosition.x;
784 const float yPosition = event.p3.mFloat - scrollPosition.y;
786 bool matchedCharacter = false;
787 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
788 model->mLogicalModel,
792 CharacterHitTest::SCROLL,
796 EventData& eventData = *impl.mEventData;
798 if(Event::GRAB_HANDLE_EVENT == event.type)
800 eventData.mUpdateCursorPosition = true;
801 eventData.mUpdateGrabHandlePosition = true;
802 eventData.mUpdateInputStyle = true;
804 if(!impl.IsClipboardEmpty())
806 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
809 if(handleStopScrolling || isSmoothHandlePanEnabled)
811 eventData.mScrollAfterUpdatePosition = true;
812 eventData.mPrimaryCursorPosition = handlePosition;
815 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
817 impl.ChangeState(EventData::SELECTING);
819 eventData.mUpdateHighlightBox = true;
820 eventData.mUpdateLeftSelectionPosition = true;
821 eventData.mUpdateRightSelectionPosition = true;
823 if(handleStopScrolling || isSmoothHandlePanEnabled)
825 eventData.mScrollAfterUpdatePosition = true;
827 if((handlePosition != eventData.mRightSelectionPosition) &&
828 (handlePosition != eventData.mLeftSelectionPosition))
830 eventData.mLeftSelectionPosition = handlePosition;
834 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
836 impl.ChangeState(EventData::SELECTING);
838 eventData.mUpdateHighlightBox = true;
839 eventData.mUpdateRightSelectionPosition = true;
840 eventData.mUpdateLeftSelectionPosition = true;
842 if(handleStopScrolling || isSmoothHandlePanEnabled)
844 eventData.mScrollAfterUpdatePosition = true;
845 if((handlePosition != eventData.mRightSelectionPosition) &&
846 (handlePosition != eventData.mLeftSelectionPosition))
848 eventData.mRightSelectionPosition = handlePosition;
853 eventData.mDecoratorUpdated = true;
856 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
858 ModelPtr& model = impl.mModel;
859 Vector2& scrollPosition = model->mScrollPosition;
860 VisualModelPtr& visualModel = model->mVisualModel;
862 const float xSpeed = event.p2.mFloat;
863 const float ySpeed = event.p3.mFloat;
864 const Vector2& layoutSize = visualModel->GetLayoutSize();
865 const Vector2 currentScrollPosition = scrollPosition;
867 scrollPosition.x += xSpeed;
868 scrollPosition.y += ySpeed;
870 impl.ClampHorizontalScroll(layoutSize);
871 impl.ClampVerticalScroll(layoutSize);
873 EventData& eventData = *impl.mEventData;
874 DecoratorPtr& decorator = eventData.mDecorator;
876 bool endOfScroll = false;
877 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
879 // Notify the decorator there is no more text to scroll.
880 // The decorator won't send more scroll events.
881 decorator->NotifyEndOfScroll();
882 // Still need to set the position of the handle.
886 // Set the position of the handle.
887 const bool scrollRightDirection = xSpeed > 0.f;
888 const bool scrollBottomDirection = ySpeed > 0.f;
889 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
890 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
892 if(Event::GRAB_HANDLE_EVENT == event.type)
894 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
896 // Get the grab handle position in decorator coords.
897 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
899 if(decorator->IsHorizontalScrollEnabled())
901 // Position the grag handle close to either the left or right edge.
902 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
905 if(decorator->IsVerticalScrollEnabled())
907 position.x = eventData.mCursorHookPositionX;
909 // Position the grag handle close to either the top or bottom edge.
910 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
913 // Get the new handle position.
914 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
915 bool matchedCharacter = false;
916 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
917 impl.mModel->mLogicalModel,
919 position.x - scrollPosition.x,
920 position.y - scrollPosition.y,
921 CharacterHitTest::SCROLL,
924 if(eventData.mPrimaryCursorPosition != handlePosition)
926 eventData.mUpdateCursorPosition = true;
927 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
928 eventData.mScrollAfterUpdatePosition = true;
929 eventData.mPrimaryCursorPosition = handlePosition;
931 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
933 // Updates the decorator if the soft handle panning is enabled.
934 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
936 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
938 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
940 // Get the selection handle position in decorator coords.
941 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
943 if(decorator->IsHorizontalScrollEnabled())
945 // Position the selection handle close to either the left or right edge.
946 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
949 if(decorator->IsVerticalScrollEnabled())
951 position.x = eventData.mCursorHookPositionX;
953 // Position the grag handle close to either the top or bottom edge.
954 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
957 // Get the new handle position.
958 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
959 bool matchedCharacter = false;
960 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
961 impl.mModel->mLogicalModel,
963 position.x - scrollPosition.x,
964 position.y - scrollPosition.y,
965 CharacterHitTest::SCROLL,
968 if(leftSelectionHandleEvent)
970 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
972 if(differentHandles || endOfScroll)
974 eventData.mUpdateHighlightBox = true;
975 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
976 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
977 eventData.mLeftSelectionPosition = handlePosition;
982 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
983 if(differentHandles || endOfScroll)
985 eventData.mUpdateHighlightBox = true;
986 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
987 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
988 eventData.mRightSelectionPosition = handlePosition;
992 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
994 impl.RepositionSelectionHandles();
996 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
999 eventData.mDecoratorUpdated = true;
1004 } // namespace Toolkit