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;
421 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
423 if(impl.mSelectableControlInterface != nullptr)
425 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
429 if(impl.mSelectableControlInterface != nullptr && eventData.mLeftSelectionPosition == eventData.mRightSelectionPosition)
431 // If left selection position and right selection position are the same, the selection is canceled.
439 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
441 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
447 // Notify the cursor position to the InputMethodContext.
448 if(eventData.mInputMethodContext)
450 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
451 eventData.mInputMethodContext.NotifyCursorPosition();
454 impl.ChangeState(EventData::SELECTING);
456 eventData.mUpdateLeftSelectionPosition = true;
457 eventData.mUpdateRightSelectionPosition = true;
458 eventData.mUpdateGrabHandlePosition = true;
459 eventData.mUpdateHighlightBox = true;
461 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
462 if(eventData.mGrabHandlePopupEnabled)
464 eventData.mDecorator->SetPopupActive(false);
469 // If no selection, set a normal cursor.
470 impl.ChangeState(EventData::EDITING);
471 eventData.mUpdateCursorPosition = true;
476 // Handle normal cursor move
477 impl.ChangeState(EventData::EDITING);
478 eventData.mUpdateCursorPosition = true;
481 eventData.mUpdateInputStyle = true;
482 eventData.mScrollAfterUpdatePosition = true;
485 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
489 const unsigned int tapCount = event.p1.mUint;
490 EventData& eventData = *impl.mEventData;
491 ModelPtr& model = impl.mModel;
492 LogicalModelPtr& logicalModel = model->mLogicalModel;
493 VisualModelPtr& visualModel = model->mVisualModel;
497 if(impl.IsShowingRealText())
499 // Convert from control's coords to text's coords.
500 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
501 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
502 uint32_t oldSelStart = eventData.mLeftSelectionPosition;
503 uint32_t oldSelEnd = eventData.mRightSelectionPosition;
505 // Keep the tap 'x' position. Used to move the cursor.
506 eventData.mCursorHookPositionX = xPosition;
508 // Whether to touch point hits on a glyph.
509 bool matchedCharacter = false;
510 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
515 CharacterHitTest::TAP,
518 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
520 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition);
523 // When the cursor position is changing, delay cursor blinking
524 eventData.mDecorator->DelayCursorBlink();
528 eventData.mPrimaryCursorPosition = 0u;
531 // Update selection position after tapping
532 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
533 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
535 eventData.mUpdateCursorPosition = true;
536 eventData.mUpdateGrabHandlePosition = true;
537 eventData.mScrollAfterUpdatePosition = true;
538 eventData.mUpdateInputStyle = true;
540 // Notify the cursor position to the InputMethodContext.
541 if(eventData.mInputMethodContext)
543 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
544 eventData.mInputMethodContext.NotifyCursorPosition();
547 else if(2u == tapCount)
549 if(eventData.mSelectionEnabled)
551 // Convert from control's coords to text's coords.
552 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
553 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
555 // Calculates the logical position from the x,y coords.
556 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
562 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
566 EventData& eventData = *impl.mEventData;
567 DecoratorPtr& decorator = eventData.mDecorator;
569 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
570 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
572 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
574 // Nothing to do if scrolling is not enabled.
578 const GestureState state = static_cast<GestureState>(event.p1.mInt);
581 case GestureState::STARTED:
583 // Will remove the cursor, handles or text's popup, ...
584 impl.ChangeState(EventData::TEXT_PANNING);
587 case GestureState::CONTINUING:
589 ModelPtr& model = impl.mModel;
591 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
592 Vector2& scrollPosition = model->mScrollPosition;
593 const Vector2 currentScroll = scrollPosition;
595 if(isHorizontalScrollEnabled)
597 const float displacementX = event.p2.mFloat;
598 scrollPosition.x += displacementX;
600 impl.ClampHorizontalScroll(layoutSize);
603 if(isVerticalScrollEnabled)
605 const float displacementY = event.p3.mFloat;
606 scrollPosition.y += displacementY;
608 impl.ClampVerticalScroll(layoutSize);
611 decorator->UpdatePositions(scrollPosition - currentScroll);
614 case GestureState::FINISHED:
615 case GestureState::CANCELLED: // FALLTHROUGH
617 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
618 impl.ChangeState(eventData.mPreviousState);
627 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
629 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
633 EventData& eventData = *impl.mEventData;
635 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
637 impl.ChangeState(EventData::EDITING_WITH_POPUP);
638 eventData.mDecoratorUpdated = true;
639 eventData.mUpdateInputStyle = true;
643 if(eventData.mSelectionEnabled)
645 ModelPtr& model = impl.mModel;
647 // Convert from control's coords to text's coords.
648 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
649 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
651 // Calculates the logical position from the x,y coords.
652 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
658 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
662 const unsigned int state = event.p1.mUint;
663 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
664 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
666 if(HANDLE_PRESSED == state)
668 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
669 } // end ( HANDLE_PRESSED == state )
670 else if((HANDLE_RELEASED == state) ||
673 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
674 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
675 else if(HANDLE_SCROLLING == state)
677 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
678 } // end ( HANDLE_SCROLLING == state )
682 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
684 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
686 ModelPtr& model = impl.mModel;
687 const Vector2& scrollPosition = model->mScrollPosition;
689 // Convert from control's coords to text's coords.
690 const float xPosition = event.p2.mFloat - scrollPosition.x;
691 const float yPosition = event.p3.mFloat - scrollPosition.y;
693 // Calculates the logical position from the x,y coords.
694 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
698 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
700 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
704 EventData& eventData = *impl.mEventData;
705 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
707 ModelPtr& model = impl.mModel;
708 const Vector2& scrollPosition = model->mScrollPosition;
710 // Calculates the logical position from the start.
711 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
712 0.f - scrollPosition.y,
713 Controller::NoTextTap::HIGHLIGHT);
715 uint32_t oldStart = eventData.mLeftSelectionPosition;
716 uint32_t oldEnd = eventData.mRightSelectionPosition;
718 eventData.mLeftSelectionPosition = 0u;
719 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
720 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
722 if(impl.mSelectableControlInterface != nullptr)
724 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
730 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
732 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
736 EventData& eventData = *impl.mEventData;
737 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
739 uint32_t oldStart = eventData.mLeftSelectionPosition;
740 uint32_t oldEnd = eventData.mRightSelectionPosition;
742 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
743 impl.ChangeState(EventData::EDITING);
744 eventData.mUpdateCursorPosition = true;
745 eventData.mUpdateInputStyle = true;
746 eventData.mScrollAfterUpdatePosition = true;
748 if(impl.mSelectableControlInterface != nullptr)
750 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
756 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
758 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
760 ModelPtr& model = impl.mModel;
761 const Vector2& scrollPosition = model->mScrollPosition;
763 // Calculate the selection index.
764 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
765 const uint32_t start = std::min(event.p2.mUint, length);
766 const uint32_t end = std::min(event.p3.mUint, length);
770 uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
771 uint32_t oldEnd = impl.mEventData->mRightSelectionPosition;
773 // Calculates the logical position from the x,y coords.
774 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
776 impl.mEventData->mLeftSelectionPosition = start;
777 impl.mEventData->mRightSelectionPosition = end;
778 impl.mEventData->mPrimaryCursorPosition = end;
780 if(impl.mSelectableControlInterface != nullptr)
782 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
788 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
790 ModelPtr& model = impl.mModel;
791 const Vector2& scrollPosition = model->mScrollPosition;
793 // Convert from decorator's coords to text's coords.
794 const float xPosition = event.p2.mFloat - scrollPosition.x;
795 const float yPosition = event.p3.mFloat - scrollPosition.y;
797 // Need to calculate the handle's new position.
798 bool matchedCharacter = false;
799 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
800 model->mLogicalModel,
804 CharacterHitTest::SCROLL,
807 EventData& eventData = *impl.mEventData;
808 uint32_t oldStart = eventData.mLeftSelectionPosition;
809 uint32_t oldEnd = eventData.mRightSelectionPosition;
811 if(Event::GRAB_HANDLE_EVENT == event.type)
813 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
815 if(handleNewPosition != eventData.mPrimaryCursorPosition)
817 // Updates the cursor position if the handle's new position is different than the current one.
818 eventData.mUpdateCursorPosition = true;
819 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
820 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
821 eventData.mPrimaryCursorPosition = handleNewPosition;
824 // 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.
825 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
827 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
829 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
831 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
832 (handleNewPosition != eventData.mRightSelectionPosition))
834 // Updates the highlight box if the handle's new position is different than the current one.
835 eventData.mUpdateHighlightBox = true;
836 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
837 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
838 eventData.mLeftSelectionPosition = handleNewPosition;
841 // 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.
842 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
844 // Will define the order to scroll the text to match the handle position.
845 eventData.mIsLeftHandleSelected = true;
846 eventData.mIsRightHandleSelected = false;
848 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
850 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
852 if((handleNewPosition != eventData.mRightSelectionPosition) &&
853 (handleNewPosition != eventData.mLeftSelectionPosition))
855 // Updates the highlight box if the handle's new position is different than the current one.
856 eventData.mUpdateHighlightBox = true;
857 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
858 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
859 eventData.mRightSelectionPosition = handleNewPosition;
862 // 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.
863 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
865 // Will define the order to scroll the text to match the handle position.
866 eventData.mIsLeftHandleSelected = false;
867 eventData.mIsRightHandleSelected = true;
870 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
872 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
876 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
878 CharacterIndex handlePosition = 0u;
879 if(handleStopScrolling || isSmoothHandlePanEnabled)
881 ModelPtr& model = impl.mModel;
882 const Vector2& scrollPosition = model->mScrollPosition;
884 // Convert from decorator's coords to text's coords.
885 const float xPosition = event.p2.mFloat - scrollPosition.x;
886 const float yPosition = event.p3.mFloat - scrollPosition.y;
888 bool matchedCharacter = false;
889 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
890 model->mLogicalModel,
894 CharacterHitTest::SCROLL,
898 EventData& eventData = *impl.mEventData;
899 uint32_t oldStart = eventData.mLeftSelectionPosition;
900 uint32_t oldEnd = eventData.mRightSelectionPosition;
902 if(Event::GRAB_HANDLE_EVENT == event.type)
904 eventData.mUpdateCursorPosition = true;
905 eventData.mUpdateGrabHandlePosition = true;
906 eventData.mUpdateInputStyle = true;
908 if(!impl.IsClipboardEmpty())
910 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
913 if(handleStopScrolling || isSmoothHandlePanEnabled)
915 eventData.mScrollAfterUpdatePosition = true;
916 eventData.mPrimaryCursorPosition = handlePosition;
919 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
921 impl.ChangeState(EventData::SELECTING);
923 eventData.mUpdateHighlightBox = true;
924 eventData.mUpdateLeftSelectionPosition = true;
925 eventData.mUpdateRightSelectionPosition = true;
927 if(handleStopScrolling || isSmoothHandlePanEnabled)
929 eventData.mScrollAfterUpdatePosition = true;
931 if((handlePosition != eventData.mRightSelectionPosition) &&
932 (handlePosition != eventData.mLeftSelectionPosition))
934 eventData.mLeftSelectionPosition = handlePosition;
938 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
940 impl.ChangeState(EventData::SELECTING);
942 eventData.mUpdateHighlightBox = true;
943 eventData.mUpdateRightSelectionPosition = true;
944 eventData.mUpdateLeftSelectionPosition = true;
946 if(handleStopScrolling || isSmoothHandlePanEnabled)
948 eventData.mScrollAfterUpdatePosition = true;
949 if((handlePosition != eventData.mRightSelectionPosition) &&
950 (handlePosition != eventData.mLeftSelectionPosition))
952 eventData.mRightSelectionPosition = handlePosition;
957 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
959 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
962 eventData.mDecoratorUpdated = true;
965 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
967 ModelPtr& model = impl.mModel;
968 Vector2& scrollPosition = model->mScrollPosition;
969 VisualModelPtr& visualModel = model->mVisualModel;
971 const float xSpeed = event.p2.mFloat;
972 const float ySpeed = event.p3.mFloat;
973 const Vector2& layoutSize = visualModel->GetLayoutSize();
974 const Vector2 currentScrollPosition = scrollPosition;
976 scrollPosition.x += xSpeed;
977 scrollPosition.y += ySpeed;
979 impl.ClampHorizontalScroll(layoutSize);
980 impl.ClampVerticalScroll(layoutSize);
982 EventData& eventData = *impl.mEventData;
983 DecoratorPtr& decorator = eventData.mDecorator;
985 bool endOfScroll = false;
986 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
988 // Notify the decorator there is no more text to scroll.
989 // The decorator won't send more scroll events.
990 decorator->NotifyEndOfScroll();
991 // Still need to set the position of the handle.
995 // Set the position of the handle.
996 const bool scrollRightDirection = xSpeed > 0.f;
997 const bool scrollBottomDirection = ySpeed > 0.f;
998 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
999 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1001 if(Event::GRAB_HANDLE_EVENT == event.type)
1003 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
1005 // Get the grab handle position in decorator coords.
1006 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
1008 if(decorator->IsHorizontalScrollEnabled())
1010 // Position the grag handle close to either the left or right edge.
1011 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1014 if(decorator->IsVerticalScrollEnabled())
1016 position.x = eventData.mCursorHookPositionX;
1018 // Position the grag handle close to either the top or bottom edge.
1019 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1022 // Get the new handle position.
1023 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1024 bool matchedCharacter = false;
1025 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1026 impl.mModel->mLogicalModel,
1028 position.x - scrollPosition.x,
1029 position.y - scrollPosition.y,
1030 CharacterHitTest::SCROLL,
1033 if(eventData.mPrimaryCursorPosition != handlePosition)
1035 eventData.mUpdateCursorPosition = true;
1036 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1037 eventData.mScrollAfterUpdatePosition = true;
1038 eventData.mPrimaryCursorPosition = handlePosition;
1040 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
1042 // Updates the decorator if the soft handle panning is enabled.
1043 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
1045 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
1047 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
1049 // Get the selection handle position in decorator coords.
1050 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
1052 if(decorator->IsHorizontalScrollEnabled())
1054 // Position the selection handle close to either the left or right edge.
1055 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1058 if(decorator->IsVerticalScrollEnabled())
1060 position.x = eventData.mCursorHookPositionX;
1062 // Position the grag handle close to either the top or bottom edge.
1063 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1066 // Get the new handle position.
1067 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1068 bool matchedCharacter = false;
1069 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1070 impl.mModel->mLogicalModel,
1072 position.x - scrollPosition.x,
1073 position.y - scrollPosition.y,
1074 CharacterHitTest::SCROLL,
1076 uint32_t oldStart = eventData.mLeftSelectionPosition;
1077 uint32_t oldEnd = eventData.mRightSelectionPosition;
1079 if(leftSelectionHandleEvent)
1081 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1083 if(differentHandles || endOfScroll)
1085 eventData.mUpdateHighlightBox = true;
1086 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1087 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1088 eventData.mLeftSelectionPosition = handlePosition;
1093 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1094 if(differentHandles || endOfScroll)
1096 eventData.mUpdateHighlightBox = true;
1097 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1098 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1099 eventData.mRightSelectionPosition = handlePosition;
1103 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1105 impl.RepositionSelectionHandles();
1107 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1109 if(impl.mSelectableControlInterface != nullptr)
1111 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
1115 eventData.mDecoratorUpdated = true;
1120 } // namespace Toolkit