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& nextline = *(visualModel->mLines.Begin() + lineIndex + 1u);
383 const LineRun& currline = *(visualModel->mLines.Begin() + lineIndex);
385 // Get last line index
386 const LineIndex lastLineIndex = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
387 const bool isLastLine = (lineIndex + 1u == lastLineIndex);
389 // Get the next hit 'y' point.
390 const float hitPointY = cursorInfo.lineOffset + GetLineHeight(currline, false) + 0.5f * GetLineHeight(nextline, isLastLine);
392 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
393 bool matchedCharacter = false;
394 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
397 eventData.mCursorHookPositionX,
399 CharacterHitTest::TAP,
404 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
406 // Update selection position after moving the cursor
407 eventData.mLeftSelectionPosition = primaryCursorPosition;
408 eventData.mRightSelectionPosition = primaryCursorPosition;
410 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
412 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
416 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
418 // Handle text selection
419 bool selecting = false;
421 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
423 // Shift-Left/Right to select the text
424 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
425 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
427 eventData.mRightSelectionPosition += cursorPositionDelta;
428 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
430 if(impl.mSelectableControlInterface != nullptr)
432 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
436 if(impl.mSelectableControlInterface != nullptr && eventData.mLeftSelectionPosition == eventData.mRightSelectionPosition)
438 // If left selection position and right selection position are the same, the selection is canceled.
446 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
448 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
454 // Notify the cursor position to the InputMethodContext.
455 if(eventData.mInputMethodContext)
457 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
458 eventData.mInputMethodContext.NotifyCursorPosition();
461 impl.ChangeState(EventData::SELECTING);
463 eventData.mUpdateLeftSelectionPosition = true;
464 eventData.mUpdateRightSelectionPosition = true;
465 eventData.mUpdateGrabHandlePosition = true;
466 eventData.mUpdateHighlightBox = true;
468 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
469 if(eventData.mGrabHandlePopupEnabled)
471 eventData.mDecorator->SetPopupActive(false);
476 // If no selection, set a normal cursor.
477 impl.ChangeState(EventData::EDITING);
478 eventData.mUpdateCursorPosition = true;
483 // Handle normal cursor move
484 impl.ChangeState(EventData::EDITING);
485 eventData.mUpdateCursorPosition = true;
488 eventData.mUpdateInputStyle = true;
489 eventData.mScrollAfterUpdatePosition = true;
492 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
496 const unsigned int tapCount = event.p1.mUint;
497 EventData& eventData = *impl.mEventData;
498 ModelPtr& model = impl.mModel;
499 LogicalModelPtr& logicalModel = model->mLogicalModel;
500 VisualModelPtr& visualModel = model->mVisualModel;
504 if(impl.IsShowingRealText())
506 // Convert from control's coords to text's coords.
507 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
508 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
509 uint32_t oldSelStart = eventData.mLeftSelectionPosition;
510 uint32_t oldSelEnd = eventData.mRightSelectionPosition;
512 // Keep the tap 'x' position. Used to move the cursor.
513 eventData.mCursorHookPositionX = xPosition;
515 // Whether to touch point hits on a glyph.
516 bool matchedCharacter = false;
517 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
522 CharacterHitTest::TAP,
525 if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible())
527 impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition);
530 // When the cursor position is changing, delay cursor blinking
531 eventData.mDecorator->DelayCursorBlink();
535 eventData.mPrimaryCursorPosition = 0u;
538 // Update selection position after tapping
539 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
540 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
542 eventData.mUpdateCursorPosition = true;
543 eventData.mUpdateGrabHandlePosition = true;
544 eventData.mScrollAfterUpdatePosition = true;
545 eventData.mUpdateInputStyle = true;
547 // Notify the cursor position to the InputMethodContext.
548 if(eventData.mInputMethodContext)
550 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
551 eventData.mInputMethodContext.NotifyCursorPosition();
554 else if(2u == tapCount)
556 if(eventData.mSelectionEnabled)
558 // Convert from control's coords to text's coords.
559 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
560 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
562 // Calculates the logical position from the x,y coords.
563 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
569 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
573 EventData& eventData = *impl.mEventData;
574 DecoratorPtr& decorator = eventData.mDecorator;
576 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
577 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
579 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
581 // Nothing to do if scrolling is not enabled.
585 const GestureState state = static_cast<GestureState>(event.p1.mInt);
588 case GestureState::STARTED:
590 // Will remove the cursor, handles or text's popup, ...
591 impl.ChangeState(EventData::TEXT_PANNING);
594 case GestureState::CONTINUING:
596 ModelPtr& model = impl.mModel;
598 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
599 Vector2& scrollPosition = model->mScrollPosition;
600 const Vector2 currentScroll = scrollPosition;
602 if(isHorizontalScrollEnabled)
604 const float displacementX = event.p2.mFloat;
605 scrollPosition.x += displacementX;
607 impl.ClampHorizontalScroll(layoutSize);
610 if(isVerticalScrollEnabled)
612 const float displacementY = event.p3.mFloat;
613 scrollPosition.y += displacementY;
615 impl.ClampVerticalScroll(layoutSize);
618 decorator->UpdatePositions(scrollPosition - currentScroll);
621 case GestureState::FINISHED:
622 case GestureState::CANCELLED: // FALLTHROUGH
624 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
625 impl.ChangeState(eventData.mPreviousState);
634 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
636 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
640 EventData& eventData = *impl.mEventData;
642 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
644 impl.ChangeState(EventData::EDITING_WITH_POPUP);
645 eventData.mDecoratorUpdated = true;
646 eventData.mUpdateInputStyle = true;
650 if(eventData.mSelectionEnabled)
652 ModelPtr& model = impl.mModel;
654 // Convert from control's coords to text's coords.
655 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
656 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
658 // Calculates the logical position from the x,y coords.
659 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
665 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
669 const unsigned int state = event.p1.mUint;
670 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
671 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
673 if(HANDLE_PRESSED == state)
675 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
676 } // end ( HANDLE_PRESSED == state )
677 else if((HANDLE_RELEASED == state) ||
680 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
681 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
682 else if(HANDLE_SCROLLING == state)
684 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
685 } // end ( HANDLE_SCROLLING == state )
689 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
691 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
693 ModelPtr& model = impl.mModel;
694 const Vector2& scrollPosition = model->mScrollPosition;
696 // Convert from control's coords to text's coords.
697 const float xPosition = event.p2.mFloat - scrollPosition.x;
698 const float yPosition = event.p3.mFloat - scrollPosition.y;
700 // Calculates the logical position from the x,y coords.
701 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
705 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
707 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
711 EventData& eventData = *impl.mEventData;
712 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
714 ModelPtr& model = impl.mModel;
715 const Vector2& scrollPosition = model->mScrollPosition;
717 // Calculates the logical position from the start.
718 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
719 0.f - scrollPosition.y,
720 Controller::NoTextTap::HIGHLIGHT);
722 uint32_t oldStart = eventData.mLeftSelectionPosition;
723 uint32_t oldEnd = eventData.mRightSelectionPosition;
725 eventData.mLeftSelectionPosition = 0u;
726 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
727 eventData.mPrimaryCursorPosition = eventData.mRightSelectionPosition;
729 if(impl.mSelectableControlInterface != nullptr)
731 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
737 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
739 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
743 EventData& eventData = *impl.mEventData;
744 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
746 uint32_t oldStart = eventData.mLeftSelectionPosition;
747 uint32_t oldEnd = eventData.mRightSelectionPosition;
749 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
750 impl.ChangeState(EventData::EDITING);
751 eventData.mUpdateCursorPosition = true;
752 eventData.mUpdateInputStyle = true;
753 eventData.mScrollAfterUpdatePosition = true;
755 if(impl.mSelectableControlInterface != nullptr)
757 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
763 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
765 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
767 ModelPtr& model = impl.mModel;
768 const Vector2& scrollPosition = model->mScrollPosition;
770 // Calculate the selection index.
771 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
772 const uint32_t start = std::min(event.p2.mUint, length);
773 const uint32_t end = std::min(event.p3.mUint, length);
777 uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
778 uint32_t oldEnd = impl.mEventData->mRightSelectionPosition;
780 // Calculates the logical position from the x,y coords.
781 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
783 impl.mEventData->mLeftSelectionPosition = start;
784 impl.mEventData->mRightSelectionPosition = end;
785 impl.mEventData->mPrimaryCursorPosition = end;
787 if(impl.mSelectableControlInterface != nullptr)
789 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
795 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
797 ModelPtr& model = impl.mModel;
798 const Vector2& scrollPosition = model->mScrollPosition;
800 // Convert from decorator's coords to text's coords.
801 const float xPosition = event.p2.mFloat - scrollPosition.x;
802 const float yPosition = event.p3.mFloat - scrollPosition.y;
804 // Need to calculate the handle's new position.
805 bool matchedCharacter = false;
806 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
807 model->mLogicalModel,
811 CharacterHitTest::SCROLL,
814 EventData& eventData = *impl.mEventData;
815 uint32_t oldStart = eventData.mLeftSelectionPosition;
816 uint32_t oldEnd = eventData.mRightSelectionPosition;
818 if(Event::GRAB_HANDLE_EVENT == event.type)
820 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
822 if(handleNewPosition != eventData.mPrimaryCursorPosition)
824 // Updates the cursor position if the handle's new position is different than the current one.
825 eventData.mUpdateCursorPosition = true;
826 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
827 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
828 eventData.mPrimaryCursorPosition = handleNewPosition;
831 // 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.
832 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
834 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
836 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
838 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
839 (handleNewPosition != eventData.mRightSelectionPosition))
841 // Updates the highlight box if the handle's new position is different than the current one.
842 eventData.mUpdateHighlightBox = true;
843 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
844 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
845 eventData.mLeftSelectionPosition = handleNewPosition;
848 // 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.
849 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
851 // Will define the order to scroll the text to match the handle position.
852 eventData.mIsLeftHandleSelected = true;
853 eventData.mIsRightHandleSelected = false;
855 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
857 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
859 if((handleNewPosition != eventData.mRightSelectionPosition) &&
860 (handleNewPosition != eventData.mLeftSelectionPosition))
862 // Updates the highlight box if the handle's new position is different than the current one.
863 eventData.mUpdateHighlightBox = true;
864 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
865 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
866 eventData.mRightSelectionPosition = handleNewPosition;
869 // 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.
870 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
872 // Will define the order to scroll the text to match the handle position.
873 eventData.mIsLeftHandleSelected = false;
874 eventData.mIsRightHandleSelected = true;
877 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
879 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
883 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
885 CharacterIndex handlePosition = 0u;
886 if(handleStopScrolling || isSmoothHandlePanEnabled)
888 ModelPtr& model = impl.mModel;
889 const Vector2& scrollPosition = model->mScrollPosition;
891 // Convert from decorator's coords to text's coords.
892 const float xPosition = event.p2.mFloat - scrollPosition.x;
893 const float yPosition = event.p3.mFloat - scrollPosition.y;
895 bool matchedCharacter = false;
896 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
897 model->mLogicalModel,
901 CharacterHitTest::SCROLL,
905 EventData& eventData = *impl.mEventData;
906 uint32_t oldStart = eventData.mLeftSelectionPosition;
907 uint32_t oldEnd = eventData.mRightSelectionPosition;
909 if(Event::GRAB_HANDLE_EVENT == event.type)
911 eventData.mUpdateCursorPosition = true;
912 eventData.mUpdateGrabHandlePosition = true;
913 eventData.mUpdateInputStyle = true;
915 if(!impl.IsClipboardEmpty())
917 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
920 if(handleStopScrolling || isSmoothHandlePanEnabled)
922 eventData.mScrollAfterUpdatePosition = true;
923 eventData.mPrimaryCursorPosition = handlePosition;
926 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
928 impl.ChangeState(EventData::SELECTING);
930 eventData.mUpdateHighlightBox = true;
931 eventData.mUpdateLeftSelectionPosition = true;
932 eventData.mUpdateRightSelectionPosition = true;
934 if(handleStopScrolling || isSmoothHandlePanEnabled)
936 eventData.mScrollAfterUpdatePosition = true;
938 if((handlePosition != eventData.mRightSelectionPosition) &&
939 (handlePosition != eventData.mLeftSelectionPosition))
941 eventData.mLeftSelectionPosition = handlePosition;
945 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
947 impl.ChangeState(EventData::SELECTING);
949 eventData.mUpdateHighlightBox = true;
950 eventData.mUpdateRightSelectionPosition = true;
951 eventData.mUpdateLeftSelectionPosition = true;
953 if(handleStopScrolling || isSmoothHandlePanEnabled)
955 eventData.mScrollAfterUpdatePosition = true;
956 if((handlePosition != eventData.mRightSelectionPosition) &&
957 (handlePosition != eventData.mLeftSelectionPosition))
959 eventData.mRightSelectionPosition = handlePosition;
964 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
966 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
969 eventData.mDecoratorUpdated = true;
972 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
974 ModelPtr& model = impl.mModel;
975 Vector2& scrollPosition = model->mScrollPosition;
976 VisualModelPtr& visualModel = model->mVisualModel;
978 const float xSpeed = event.p2.mFloat;
979 const float ySpeed = event.p3.mFloat;
980 const Vector2& layoutSize = visualModel->GetLayoutSize();
981 const Vector2 currentScrollPosition = scrollPosition;
983 scrollPosition.x += xSpeed;
984 scrollPosition.y += ySpeed;
986 impl.ClampHorizontalScroll(layoutSize);
987 impl.ClampVerticalScroll(layoutSize);
989 EventData& eventData = *impl.mEventData;
990 DecoratorPtr& decorator = eventData.mDecorator;
992 bool endOfScroll = false;
993 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
995 // Notify the decorator there is no more text to scroll.
996 // The decorator won't send more scroll events.
997 decorator->NotifyEndOfScroll();
998 // Still need to set the position of the handle.
1002 // Set the position of the handle.
1003 const bool scrollRightDirection = xSpeed > 0.f;
1004 const bool scrollBottomDirection = ySpeed > 0.f;
1005 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1006 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1008 if(Event::GRAB_HANDLE_EVENT == event.type)
1010 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
1012 // Get the grab handle position in decorator coords.
1013 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
1015 if(decorator->IsHorizontalScrollEnabled())
1017 // Position the grag handle close to either the left or right edge.
1018 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1021 if(decorator->IsVerticalScrollEnabled())
1023 position.x = eventData.mCursorHookPositionX;
1025 // Position the grag handle close to either the top or bottom edge.
1026 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1029 // Get the new handle position.
1030 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1031 bool matchedCharacter = false;
1032 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1033 impl.mModel->mLogicalModel,
1035 position.x - scrollPosition.x,
1036 position.y - scrollPosition.y,
1037 CharacterHitTest::SCROLL,
1040 if(eventData.mPrimaryCursorPosition != handlePosition)
1042 eventData.mUpdateCursorPosition = true;
1043 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1044 eventData.mScrollAfterUpdatePosition = true;
1045 eventData.mPrimaryCursorPosition = handlePosition;
1047 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
1049 // Updates the decorator if the soft handle panning is enabled.
1050 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
1052 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
1054 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
1056 // Get the selection handle position in decorator coords.
1057 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
1059 if(decorator->IsHorizontalScrollEnabled())
1061 // Position the selection handle close to either the left or right edge.
1062 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1065 if(decorator->IsVerticalScrollEnabled())
1067 position.x = eventData.mCursorHookPositionX;
1069 // Position the grag handle close to either the top or bottom edge.
1070 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1073 // Get the new handle position.
1074 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1075 bool matchedCharacter = false;
1076 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1077 impl.mModel->mLogicalModel,
1079 position.x - scrollPosition.x,
1080 position.y - scrollPosition.y,
1081 CharacterHitTest::SCROLL,
1083 uint32_t oldStart = eventData.mLeftSelectionPosition;
1084 uint32_t oldEnd = eventData.mRightSelectionPosition;
1086 if(leftSelectionHandleEvent)
1088 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1090 if(differentHandles || endOfScroll)
1092 eventData.mUpdateHighlightBox = true;
1093 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1094 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1095 eventData.mLeftSelectionPosition = handlePosition;
1100 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1101 if(differentHandles || endOfScroll)
1103 eventData.mUpdateHighlightBox = true;
1104 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1105 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1106 eventData.mRightSelectionPosition = handlePosition;
1110 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1112 impl.RepositionSelectionHandles();
1114 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1116 if(impl.mSelectableControlInterface != nullptr)
1118 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
1122 eventData.mDecoratorUpdated = true;
1127 } // namespace Toolkit