2 * Copyright (c) 2022 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);
337 const LineIndex lastLineIndex = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
338 const bool isLastLine = (previousLineIndex == lastLineIndex);
340 // Retrieve the cursor position info.
341 CursorInfo cursorInfo;
342 impl.GetCursorPosition(primaryCursorPosition,
345 // Get the line above.
346 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
348 // Get the next hit 'y' point.
349 const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line, isLastLine);
351 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
352 bool matchedCharacter = false;
353 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
356 eventData.mCursorHookPositionX,
358 CharacterHitTest::TAP,
361 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
363 // Ignore Shift-Down for text selection for now.
365 // Get first the line index of the current cursor position index.
366 CharacterIndex characterIndex = 0u;
368 if(primaryCursorPosition > 0u)
370 characterIndex = primaryCursorPosition - 1u;
373 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
375 if(lineIndex + 1u < visualModel->mLines.Count())
377 // Retrieve the cursor position info.
378 CursorInfo cursorInfo;
379 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
381 // Get the line below.
382 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
384 // Get last line index
385 const LineIndex lastLineIndex = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
386 const bool isLastLine = (lineIndex + 1u == lastLineIndex);
388 // Get the next hit 'y' point.
389 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line, isLastLine);
391 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
392 bool matchedCharacter = false;
393 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
396 eventData.mCursorHookPositionX,
398 CharacterHitTest::TAP,
403 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
405 // Update selection position after moving the cursor
406 eventData.mLeftSelectionPosition = primaryCursorPosition;
407 eventData.mRightSelectionPosition = primaryCursorPosition;
409 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
411 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
415 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
417 // Handle text selection
418 bool selecting = false;
420 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
422 // Shift-Left/Right to select the text
423 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
424 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
426 eventData.mRightSelectionPosition += cursorPositionDelta;
427 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
429 if(impl.mSelectableControlInterface != nullptr)
431 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
435 if(impl.mSelectableControlInterface != nullptr && eventData.mLeftSelectionPosition == eventData.mRightSelectionPosition)
437 // If left selection position and right selection position are the same, the selection is canceled.
445 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
447 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
453 // Notify the cursor position to the InputMethodContext.
454 if(eventData.mInputMethodContext)
456 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
457 eventData.mInputMethodContext.NotifyCursorPosition();
460 impl.ChangeState(EventData::SELECTING);
462 eventData.mUpdateLeftSelectionPosition = true;
463 eventData.mUpdateRightSelectionPosition = true;
464 eventData.mUpdateGrabHandlePosition = true;
465 eventData.mUpdateHighlightBox = true;
467 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
468 if(eventData.mGrabHandlePopupEnabled)
470 eventData.mDecorator->SetPopupActive(false);
475 // If no selection, set a normal cursor.
476 impl.ChangeState(EventData::EDITING);
477 eventData.mUpdateCursorPosition = true;
482 // Handle normal cursor move
483 impl.ChangeState(EventData::EDITING);
484 eventData.mUpdateCursorPosition = true;
487 eventData.mUpdateInputStyle = true;
488 eventData.mScrollAfterUpdatePosition = true;
491 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
495 const unsigned int tapCount = event.p1.mUint;
496 EventData& eventData = *impl.mEventData;
497 ModelPtr& model = impl.mModel;
498 LogicalModelPtr& logicalModel = model->mLogicalModel;
499 VisualModelPtr& visualModel = model->mVisualModel;
503 if(impl.IsShowingRealText())
505 // Convert from control's coords to text's coords.
506 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
507 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
508 uint32_t oldSelStart = eventData.mLeftSelectionPosition;
509 uint32_t oldSelEnd = eventData.mRightSelectionPosition;
511 // Keep the tap 'x' position. Used to move the cursor.
512 eventData.mCursorHookPositionX = xPosition;
514 // Whether to touch point hits on a glyph.
515 bool matchedCharacter = false;
516 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
521 CharacterHitTest::TAP,
524 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
526 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition);
529 // When the cursor position is changing, delay cursor blinking
530 eventData.mDecorator->DelayCursorBlink();
534 eventData.mPrimaryCursorPosition = 0u;
537 // Update selection position after tapping
538 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
539 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
541 eventData.mUpdateCursorPosition = true;
542 eventData.mUpdateGrabHandlePosition = true;
543 eventData.mScrollAfterUpdatePosition = true;
544 eventData.mUpdateInputStyle = true;
546 // Notify the cursor position to the InputMethodContext.
547 if(eventData.mInputMethodContext)
549 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
550 eventData.mInputMethodContext.NotifyCursorPosition();
553 else if(2u == tapCount)
555 if(eventData.mSelectionEnabled)
557 // Convert from control's coords to text's coords.
558 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
559 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
561 // Calculates the logical position from the x,y coords.
562 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
568 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
572 EventData& eventData = *impl.mEventData;
573 DecoratorPtr& decorator = eventData.mDecorator;
575 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
576 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
578 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
580 // Nothing to do if scrolling is not enabled.
584 const GestureState state = static_cast<GestureState>(event.p1.mInt);
587 case GestureState::STARTED:
589 // Will remove the cursor, handles or text's popup, ...
590 impl.ChangeState(EventData::TEXT_PANNING);
593 case GestureState::CONTINUING:
595 ModelPtr& model = impl.mModel;
597 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
598 Vector2& scrollPosition = model->mScrollPosition;
599 const Vector2 currentScroll = scrollPosition;
601 if(isHorizontalScrollEnabled)
603 const float displacementX = event.p2.mFloat;
604 scrollPosition.x += displacementX;
606 impl.ClampHorizontalScroll(layoutSize);
609 if(isVerticalScrollEnabled)
611 const float displacementY = event.p3.mFloat;
612 scrollPosition.y += displacementY;
614 impl.ClampVerticalScroll(layoutSize);
617 decorator->UpdatePositions(scrollPosition - currentScroll);
620 case GestureState::FINISHED:
621 case GestureState::CANCELLED: // FALLTHROUGH
623 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
624 impl.ChangeState(eventData.mPreviousState);
633 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
635 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
639 EventData& eventData = *impl.mEventData;
641 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
643 impl.ChangeState(EventData::EDITING_WITH_POPUP);
644 eventData.mDecoratorUpdated = true;
645 eventData.mUpdateInputStyle = true;
649 if(eventData.mSelectionEnabled)
651 ModelPtr& model = impl.mModel;
653 // Convert from control's coords to text's coords.
654 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
655 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
657 // Calculates the logical position from the x,y coords.
658 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
664 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
668 const unsigned int state = event.p1.mUint;
669 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
670 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
672 if(HANDLE_PRESSED == state)
674 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
675 } // end ( HANDLE_PRESSED == state )
676 else if((HANDLE_RELEASED == state) ||
679 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
680 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
681 else if(HANDLE_SCROLLING == state)
683 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
684 } // end ( HANDLE_SCROLLING == state )
688 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
690 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
692 ModelPtr& model = impl.mModel;
693 const Vector2& scrollPosition = model->mScrollPosition;
695 // Convert from control's coords to text's coords.
696 const float xPosition = event.p2.mFloat - scrollPosition.x;
697 const float yPosition = event.p3.mFloat - scrollPosition.y;
699 // Calculates the logical position from the x,y coords.
700 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
704 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
706 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
710 EventData& eventData = *impl.mEventData;
711 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
713 ModelPtr& model = impl.mModel;
714 const Vector2& scrollPosition = model->mScrollPosition;
716 // Calculates the logical position from the start.
717 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
718 0.f - scrollPosition.y,
719 Controller::NoTextTap::HIGHLIGHT);
721 uint32_t oldStart = eventData.mLeftSelectionPosition;
722 uint32_t oldEnd = eventData.mRightSelectionPosition;
724 eventData.mLeftSelectionPosition = 0u;
725 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
726 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
728 if(impl.mSelectableControlInterface != nullptr)
730 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
736 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
738 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
742 EventData& eventData = *impl.mEventData;
743 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
745 uint32_t oldStart = eventData.mLeftSelectionPosition;
746 uint32_t oldEnd = eventData.mRightSelectionPosition;
748 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
749 impl.ChangeState(EventData::EDITING);
750 eventData.mUpdateCursorPosition = true;
751 eventData.mUpdateInputStyle = true;
752 eventData.mScrollAfterUpdatePosition = true;
754 if(impl.mSelectableControlInterface != nullptr)
756 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
762 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
764 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
766 ModelPtr& model = impl.mModel;
767 const Vector2& scrollPosition = model->mScrollPosition;
769 // Calculate the selection index.
770 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
771 const uint32_t start = std::min(event.p2.mUint, length);
772 const uint32_t end = std::min(event.p3.mUint, length);
776 uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
777 uint32_t oldEnd = impl.mEventData->mRightSelectionPosition;
779 // Calculates the logical position from the x,y coords.
780 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
782 impl.mEventData->mLeftSelectionPosition = start;
783 impl.mEventData->mRightSelectionPosition = end;
784 impl.mEventData->mPrimaryCursorPosition = end;
786 if(impl.mSelectableControlInterface != nullptr)
788 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
794 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
796 ModelPtr& model = impl.mModel;
797 const Vector2& scrollPosition = model->mScrollPosition;
799 // Convert from decorator's coords to text's coords.
800 const float xPosition = event.p2.mFloat - scrollPosition.x;
801 const float yPosition = event.p3.mFloat - scrollPosition.y;
803 // Need to calculate the handle's new position.
804 bool matchedCharacter = false;
805 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
806 model->mLogicalModel,
810 CharacterHitTest::SCROLL,
813 EventData& eventData = *impl.mEventData;
814 uint32_t oldStart = eventData.mLeftSelectionPosition;
815 uint32_t oldEnd = eventData.mRightSelectionPosition;
817 if(Event::GRAB_HANDLE_EVENT == event.type)
819 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
821 if(handleNewPosition != eventData.mPrimaryCursorPosition)
823 // Updates the cursor position if the handle's new position is different than the current one.
824 eventData.mUpdateCursorPosition = true;
825 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
826 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
827 eventData.mPrimaryCursorPosition = handleNewPosition;
830 // 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.
831 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
833 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
835 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
837 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
838 (handleNewPosition != eventData.mRightSelectionPosition))
840 // Updates the highlight box if the handle's new position is different than the current one.
841 eventData.mUpdateHighlightBox = true;
842 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
843 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
844 eventData.mLeftSelectionPosition = handleNewPosition;
847 // 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.
848 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
850 // Will define the order to scroll the text to match the handle position.
851 eventData.mIsLeftHandleSelected = true;
852 eventData.mIsRightHandleSelected = false;
854 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
856 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
858 if((handleNewPosition != eventData.mRightSelectionPosition) &&
859 (handleNewPosition != eventData.mLeftSelectionPosition))
861 // Updates the highlight box if the handle's new position is different than the current one.
862 eventData.mUpdateHighlightBox = true;
863 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
864 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
865 eventData.mRightSelectionPosition = handleNewPosition;
868 // 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.
869 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
871 // Will define the order to scroll the text to match the handle position.
872 eventData.mIsLeftHandleSelected = false;
873 eventData.mIsRightHandleSelected = true;
876 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
878 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
882 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
884 CharacterIndex handlePosition = 0u;
885 if(handleStopScrolling || isSmoothHandlePanEnabled)
887 ModelPtr& model = impl.mModel;
888 const Vector2& scrollPosition = model->mScrollPosition;
890 // Convert from decorator's coords to text's coords.
891 const float xPosition = event.p2.mFloat - scrollPosition.x;
892 const float yPosition = event.p3.mFloat - scrollPosition.y;
894 bool matchedCharacter = false;
895 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
896 model->mLogicalModel,
900 CharacterHitTest::SCROLL,
904 EventData& eventData = *impl.mEventData;
905 uint32_t oldStart = eventData.mLeftSelectionPosition;
906 uint32_t oldEnd = eventData.mRightSelectionPosition;
908 if(Event::GRAB_HANDLE_EVENT == event.type)
910 eventData.mUpdateCursorPosition = true;
911 eventData.mUpdateGrabHandlePosition = true;
912 eventData.mUpdateInputStyle = true;
914 if(!impl.IsClipboardEmpty())
916 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
919 if(handleStopScrolling || isSmoothHandlePanEnabled)
921 eventData.mScrollAfterUpdatePosition = true;
922 eventData.mPrimaryCursorPosition = handlePosition;
925 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
927 impl.ChangeState(EventData::SELECTING);
929 eventData.mUpdateHighlightBox = true;
930 eventData.mUpdateLeftSelectionPosition = true;
931 eventData.mUpdateRightSelectionPosition = true;
933 if(handleStopScrolling || isSmoothHandlePanEnabled)
935 eventData.mScrollAfterUpdatePosition = true;
937 if((handlePosition != eventData.mRightSelectionPosition) &&
938 (handlePosition != eventData.mLeftSelectionPosition))
940 eventData.mLeftSelectionPosition = handlePosition;
944 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
946 impl.ChangeState(EventData::SELECTING);
948 eventData.mUpdateHighlightBox = true;
949 eventData.mUpdateRightSelectionPosition = true;
950 eventData.mUpdateLeftSelectionPosition = true;
952 if(handleStopScrolling || isSmoothHandlePanEnabled)
954 eventData.mScrollAfterUpdatePosition = true;
955 if((handlePosition != eventData.mRightSelectionPosition) &&
956 (handlePosition != eventData.mLeftSelectionPosition))
958 eventData.mRightSelectionPosition = handlePosition;
963 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
965 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
968 eventData.mDecoratorUpdated = true;
971 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
973 ModelPtr& model = impl.mModel;
974 Vector2& scrollPosition = model->mScrollPosition;
975 VisualModelPtr& visualModel = model->mVisualModel;
977 const float xSpeed = event.p2.mFloat;
978 const float ySpeed = event.p3.mFloat;
979 const Vector2& layoutSize = visualModel->GetLayoutSize();
980 const Vector2 currentScrollPosition = scrollPosition;
982 scrollPosition.x += xSpeed;
983 scrollPosition.y += ySpeed;
985 impl.ClampHorizontalScroll(layoutSize);
986 impl.ClampVerticalScroll(layoutSize);
988 EventData& eventData = *impl.mEventData;
989 DecoratorPtr& decorator = eventData.mDecorator;
991 bool endOfScroll = false;
992 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
994 // Notify the decorator there is no more text to scroll.
995 // The decorator won't send more scroll events.
996 decorator->NotifyEndOfScroll();
997 // Still need to set the position of the handle.
1001 // Set the position of the handle.
1002 const bool scrollRightDirection = xSpeed > 0.f;
1003 const bool scrollBottomDirection = ySpeed > 0.f;
1004 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1005 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1007 if(Event::GRAB_HANDLE_EVENT == event.type)
1009 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
1011 // Get the grab handle position in decorator coords.
1012 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
1014 if(decorator->IsHorizontalScrollEnabled())
1016 // Position the grag handle close to either the left or right edge.
1017 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1020 if(decorator->IsVerticalScrollEnabled())
1022 position.x = eventData.mCursorHookPositionX;
1024 // Position the grag handle close to either the top or bottom edge.
1025 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1028 // Get the new handle position.
1029 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1030 bool matchedCharacter = false;
1031 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1032 impl.mModel->mLogicalModel,
1034 position.x - scrollPosition.x,
1035 position.y - scrollPosition.y,
1036 CharacterHitTest::SCROLL,
1039 if(eventData.mPrimaryCursorPosition != handlePosition)
1041 eventData.mUpdateCursorPosition = true;
1042 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1043 eventData.mScrollAfterUpdatePosition = true;
1044 eventData.mPrimaryCursorPosition = handlePosition;
1046 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
1048 // Updates the decorator if the soft handle panning is enabled.
1049 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
1051 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
1053 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
1055 // Get the selection handle position in decorator coords.
1056 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
1058 if(decorator->IsHorizontalScrollEnabled())
1060 // Position the selection handle close to either the left or right edge.
1061 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1064 if(decorator->IsVerticalScrollEnabled())
1066 position.x = eventData.mCursorHookPositionX;
1068 // Position the grag handle close to either the top or bottom edge.
1069 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1072 // Get the new handle position.
1073 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1074 bool matchedCharacter = false;
1075 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1076 impl.mModel->mLogicalModel,
1078 position.x - scrollPosition.x,
1079 position.y - scrollPosition.y,
1080 CharacterHitTest::SCROLL,
1082 uint32_t oldStart = eventData.mLeftSelectionPosition;
1083 uint32_t oldEnd = eventData.mRightSelectionPosition;
1085 if(leftSelectionHandleEvent)
1087 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1089 if(differentHandles || endOfScroll)
1091 eventData.mUpdateHighlightBox = true;
1092 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1093 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1094 eventData.mLeftSelectionPosition = handlePosition;
1099 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1100 if(differentHandles || endOfScroll)
1102 eventData.mUpdateHighlightBox = true;
1103 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1104 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1105 eventData.mRightSelectionPosition = handlePosition;
1109 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1111 impl.RepositionSelectionHandles();
1113 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1115 if(impl.mSelectableControlInterface != nullptr)
1117 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
1121 eventData.mDecoratorUpdated = true;
1126 } // namespace Toolkit